Compare commits
1393 Commits
v3.5.0-rc1
..
v3.4.3
| Author | SHA1 | Date | |
|---|---|---|---|
| 05a434229c | |||
| e5c3ba2df9 | |||
| 2f3bcbc5ce | |||
| 4c2a5ce403 | |||
| 97a800149c | |||
| 9510c3c535 | |||
| 5c1b473931 | |||
| f2f7255f7b | |||
| 3a20fb23a5 | |||
| 6f0e3c70a9 | |||
| 212b6daf32 | |||
| d5f69f0c5c | |||
| b066f554c4 | |||
| e6a65ee796 | |||
| d44adb98f6 | |||
| 321d41a022 | |||
| 2f477b5764 | |||
| 8596627666 | |||
| 7f7e03e923 | |||
| f9d6769ecd | |||
| dc15df1651 | |||
| 17d4aeacb4 | |||
| 4d2dc4246d | |||
| 08c3480834 | |||
| 67d10dd9f3 | |||
| 13a215ee8f | |||
| 8b90c6d421 | |||
| 1087c7ecf6 | |||
| a7a2e52230 | |||
| a061b37c9f | |||
| a7e25a4743 | |||
| 6ed7752d77 | |||
| 3d519e9028 | |||
| cca4584795 | |||
| 285d723271 | |||
| 862b48ec3e | |||
| 2c00480315 | |||
| 93476477d6 | |||
| 755fe15718 | |||
| 38899f4068 | |||
| 07e77422e6 | |||
| 2e2bfe1c8e | |||
| b04d01c29e | |||
| 67b4cde9bd | |||
| e1baf5e1d6 | |||
| 0f82392c8b | |||
| a023256c41 | |||
| 108ed14f1d | |||
| d419fe2ccf | |||
| d70da80b82 | |||
| 8ef2cc73c7 | |||
| 2e764430b3 | |||
| 644c42f936 | |||
| 6df0fa92f3 | |||
| a274f62195 | |||
| c941c541b4 | |||
| 6204ed292e | |||
| 3c4be4e9f5 | |||
| 2489791a70 | |||
| e0700b4c79 | |||
| 91fdd3b806 | |||
| a4a05cffdb | |||
| 039f5b0472 | |||
| 4a6aa192a0 | |||
| c4a24dbc2e | |||
| 1f89dde34f | |||
| 5604f5977f | |||
| 6d882faecf | |||
| b561a9a3c2 | |||
| 836f8eb62e | |||
| 5bcf683030 | |||
| 3403ae0f9d | |||
| 6e7bc3a86a | |||
| cd674a6d1d | |||
| 9b5778fcd1 | |||
| 2986db7b8d | |||
| 75f2840454 | |||
| 0d91ed2942 | |||
| b2b8f037de | |||
| 0011785c0a | |||
| bfd070b711 | |||
| 53616ddb09 | |||
| 9c10ba6e78 | |||
| db2797b58c | |||
| a50cb83349 | |||
| 25e366c3f5 | |||
| df986a59fc | |||
| 78f478c00b | |||
| 031a7948f1 | |||
| 0c1f7db133 | |||
| 3fa0cd9708 | |||
| 023c3d4f0d | |||
| 20f9ff3a10 | |||
| 464a6c7f7e | |||
| 43f86202dc | |||
| 0cd244b5c7 | |||
| 1d49ba282a | |||
| 500a03f9ed | |||
| 74d018c3e0 | |||
| 0628adc6c7 | |||
| 4f53102a9e | |||
| 3c3451b1b6 | |||
| 29d090c15c | |||
| 500146d1e6 | |||
| 6cc7e8cd51 | |||
| 18a3db531e | |||
| 00781f3590 | |||
| 475d4345ca | |||
| 569e2aae3b | |||
| eb0b01e3c9 | |||
| 6147acceba | |||
| 5c581ac980 | |||
| fe99227626 | |||
| a586c08def | |||
| 9db4483076 | |||
| 927fe3ce99 | |||
| b53663e359 | |||
| 3e88f8e7d1 | |||
| 608cb5dcc0 | |||
| d99070f10e | |||
| 9b7163faf1 | |||
| 40e290fe27 | |||
| f53c4b18b4 | |||
| ef56dcb9dd | |||
| 1e0ca61d45 | |||
| adb4e9b55e | |||
| 31f20b9309 | |||
| 472770e15e | |||
| 8a5c22a052 | |||
| 32df854591 | |||
| 42caaedf5e | |||
| 1813fb1019 | |||
| b909153fef | |||
| 0b01e3abc1 | |||
| ed061d4a9a | |||
| 6a06f60fc6 | |||
| b694fb4a33 | |||
| 852a814791 | |||
| 3cd8687bec | |||
| 0d7e2fc5ec | |||
| fdcb33c5c5 | |||
| 07dc8783a9 | |||
| 4d829b1159 | |||
| 791824e682 | |||
| 3c7fe9a187 | |||
| 6359c7e7be | |||
| 36f95f4473 | |||
| 059b45807e | |||
| bc36eb358c | |||
| 9dcb1ca891 | |||
| e8aed4e726 | |||
| 5701891122 | |||
| 79c1c70d3e | |||
| 4972e130b0 | |||
| 29fde8b965 | |||
| 9a8e043005 | |||
| 01c96e9407 | |||
| fe91efe959 | |||
| b70613ff41 | |||
| f4fb0b93ce | |||
| 4d5b3a33b4 | |||
| 042bce6e96 | |||
| 7f0e4dc648 | |||
| ebad5e1ad0 | |||
| c4e40568e6 | |||
| 7e34297bc4 | |||
| 00ee2d2253 | |||
| 4f63dba263 | |||
| 50c5de127c | |||
| 3a4032ddc8 | |||
| b3db534b8c | |||
| a37d821895 | |||
| 763db71ad3 | |||
| 87abd9b0aa | |||
| 8d34874399 | |||
| 1fe42d1cd1 | |||
| 5764a4640f | |||
| 403e3b0333 | |||
| c305233628 | |||
| 47265c83e0 | |||
| a942eb648a | |||
| 26fad064f0 | |||
| 26c255a160 | |||
| e00c025ed6 | |||
| 3bf826e23d | |||
| 3f98a6af5b | |||
| ab4de7a081 | |||
| 269327c0fb | |||
| 4b012e921c | |||
| f304a59b8a | |||
| 6294e3b9ec | |||
| 845a6a8329 | |||
| d038e858df | |||
| 176aacfe93 | |||
| 9063bdf5b1 | |||
| 1ed8bd1b2f | |||
| c78bbe369c | |||
| baec14ae3a | |||
| 876fd26435 | |||
| c7c6f2107b | |||
| f49cae6e04 | |||
| ba84a6e926 | |||
| defa163768 | |||
| f88ea97b79 | |||
| 4e71852fbc | |||
| 7e8fc8cc0f | |||
| 4cd32d3712 | |||
| a8027ed5cb | |||
| e5affab8bd | |||
| 4e262e6b2b | |||
| eb5bcca5ad | |||
| 94b01d5304 | |||
| fcc16c0dd3 | |||
| 79aac77a26 | |||
| 3bc60b4f89 | |||
| 872958acbf | |||
| b4ff89c851 | |||
| e9780ff068 | |||
| 640fda0ce5 | |||
| 8010382a9c | |||
| ce76768382 | |||
| a4efb88ff2 | |||
| 6d0112902a | |||
| 1078a686bd | |||
| 352fd99b38 | |||
| 238a285a1b | |||
| df14c026bf | |||
| 2915719cb9 | |||
| 9abdeed325 | |||
| 52dd3facb5 | |||
| 27aaaf2998 | |||
| e4fcfbb8f9 | |||
| 4e62b0f999 | |||
| b09f1d8be9 | |||
| 3bab35e6b1 | |||
| 419af6769e | |||
| 82f8050bb8 | |||
| 8e8a48cfd9 | |||
| 4a877f36d2 | |||
| 27b956ef56 | |||
| 5603f19631 | |||
| 44378db9cf | |||
| 83ff8f1eac | |||
| 31ced1fa3f | |||
| 361fc9a30c | |||
| 37d4ba0269 | |||
| 8d229bfb4c | |||
| 59354457e0 | |||
| 276d129e03 | |||
| 639add8bf9 | |||
| 7b25853ccb | |||
| 111a056d3f | |||
| b3753e60fa | |||
| e9419db48d | |||
| 6d30a1ded4 | |||
| 9cf2c160dc | |||
| f4603b1e15 | |||
| 97585a325d | |||
| fb34d23759 | |||
| f46e96bd91 | |||
| 4a62ff444e | |||
| d3cbf97a2e | |||
| b62eb9310e | |||
| f6ebb57702 | |||
| c381d9c18f | |||
| 386c5299d8 | |||
| c96525e2bd | |||
| 5886b5ef47 | |||
| 8bde0603d1 | |||
| fcf745c170 | |||
| bdda38b8b6 | |||
| 8048aedf46 | |||
| 057a8fe78b | |||
| 8b8f1bf85c | |||
| d5a6324fe1 | |||
| 2964d00f94 | |||
| 8e5187c0b6 | |||
| 04c8c1431b | |||
| 61091a5457 | |||
| b5400f20cb | |||
| 5d705cbed4 | |||
| 8966bb5b38 | |||
| 46985532b5 | |||
| 75dc5bf14b | |||
| 82c8ef17b0 | |||
| aeaf9f0986 | |||
| 031f4e28d9 | |||
| 9bb560cd7c | |||
| 269cbdce37 | |||
| 998fad0954 | |||
| 970515e932 | |||
| de070d92e1 | |||
| 5555399582 | |||
| bfa32ae5c0 | |||
| fc7c30e3f0 | |||
| fad52c36fd | |||
| 1e3a6e2d32 | |||
| a59a5912ee | |||
| 5e8f5cbce3 | |||
| 2d390e8b69 | |||
| d1c45ed355 | |||
| 08fd60f505 | |||
| dd95c21a95 | |||
| dc9888df17 | |||
| 636a50044b | |||
| ac2e6921cb | |||
| 2dcc83ac38 | |||
| c2f944b022 | |||
| f47cd85ff6 | |||
| 1a329e6977 | |||
| 351bfbef06 | |||
| 951c886951 | |||
| a4c23f2b13 | |||
| 647c7ec982 | |||
| 31ebe1ff67 | |||
| aaf9b0db67 | |||
| 790a3c60de | |||
| ab58842db9 | |||
| 38c5451942 | |||
| 1a3bdfd652 | |||
| 5dcc3bca58 | |||
| 4e5e4d715b | |||
| 533e41f82e | |||
| 24a171c5b9 | |||
| 43be1507a1 | |||
| 7c772f449b | |||
| 72da93a9ea | |||
| 802ab825ed | |||
| 5c8bebc159 | |||
| 1d5d122b27 | |||
| 242797a9d8 | |||
| 734825bc6d | |||
| bd087739c6 | |||
| 28a7c5b677 | |||
| c7b3f78e25 | |||
| ec2474927f | |||
| abb19f6cd1 | |||
| f95d1bd73a | |||
| 3d791f1b6b | |||
| 3b9f278a95 | |||
| 6ca9bbd0e8 | |||
| 8e786a3a30 | |||
| 770045533a | |||
| d45a552c02 | |||
| 276350a9a6 | |||
| ae4963c300 | |||
| 27a3313539 | |||
| 45742c52a4 | |||
| 4ab9b1a502 | |||
| fa83660e35 | |||
| 15c5a72024 | |||
| 63f3fd97b0 | |||
| 49f37c4573 | |||
| 7029b4eff4 | |||
| 730f7da8eb | |||
| 389fed5720 | |||
| 9967972257 | |||
| 698391ffe4 | |||
| bae1a479aa | |||
| 365697c942 | |||
| a61fab9c50 | |||
| 4fbacc4a5e | |||
| 0ffb2b4156 | |||
| 9d3b7bb04c | |||
| b712af05fb | |||
| 7cc490a3dd | |||
| 66df2222f5 | |||
| 373a574f62 | |||
| c15b72b415 | |||
| b7236f9fdb | |||
| 376212f1b9 | |||
| b22517e334 | |||
| 3fd4a77fde | |||
| 26ec8c1b67 | |||
| 7a88d29380 | |||
| 3df1711e6d | |||
| 14d714b69c | |||
| 72c09c0e3f | |||
| d52682b09d | |||
| 61506589a6 | |||
| b124c934ee | |||
| 65b7b6cac5 | |||
| 52797db36b | |||
| 63869e39b5 | |||
| 9053adb675 | |||
| d73cb53055 | |||
| 9b6bf24f7f | |||
| eb8d5f283e | |||
| ba2d1550a9 | |||
| ace94710be | |||
| 75af50bb8d | |||
| 54ccd521a9 | |||
| 9a4404e071 | |||
| afc0076cf2 | |||
| bcefe75402 | |||
| 2e49807d50 | |||
| dbdae3d343 | |||
| 06f237aa68 | |||
| 1062a75b04 | |||
| 84f092a44b | |||
| 35e39d8b5d | |||
| 7fe24b695c | |||
| a7ffba763d | |||
| 973457da4c | |||
| 8a18c88a60 | |||
| ab8eb0a56e | |||
| 95368281bc | |||
| f234880958 | |||
| d1387ac765 | |||
| 5262ae8faf | |||
| aae70e19ae | |||
| 19f06a1877 | |||
| fbf766ba01 | |||
| d4db4a869f | |||
| 627299b796 | |||
| 6e102f2c6d | |||
| fb639d95b5 | |||
| af90bd0b7e | |||
| b598bd2f79 | |||
| c8cf913c76 | |||
| 88639eefc4 | |||
| 444bdb189a | |||
| 96f5327b3d | |||
| f505863218 | |||
| f6dc9263f8 | |||
| 09577f1ec8 | |||
| 92b4e5956e | |||
| 1de7010e47 | |||
| 7bf7bee56c | |||
| 95bc9fa631 | |||
| a1656afa41 | |||
| df77539994 | |||
| 04ef818023 | |||
| 1444ea075e | |||
| 457f4b7551 | |||
| f02e11d668 | |||
| a1eef605f0 | |||
| fe3ece78ea | |||
| 8905f55508 | |||
| 89feef08e8 | |||
| 96a257f7a9 | |||
| 45b3665c00 | |||
| 509c176f0a | |||
| 8788ad4f14 | |||
| 64466bd2b8 | |||
| 1d1baa54f0 | |||
| 4376919fee | |||
| 6de72ccebd | |||
| 5011a166b8 | |||
| 6e6edbe001 | |||
| 6728b43113 | |||
| 34858987ca | |||
| c6f9dce2a0 | |||
| 06d3bab902 | |||
| 0eb5643936 | |||
| 4d53f252e3 | |||
| 775a804c68 | |||
| ac183832bc | |||
| 9d0f4315f0 | |||
| 524312d1ef | |||
| 81bd6817b9 | |||
| 99b227c604 | |||
| ecc63154f9 | |||
| 642389a9a9 | |||
| 8cac59ef84 | |||
| bb9dcb7479 | |||
| 3b1e2eb6f4 | |||
| 1692fae8c0 | |||
| 7d8b9d454e | |||
| f4b2263a56 | |||
| db318336de | |||
| e072d0eec4 | |||
| 3d8caa7ea5 | |||
| 4984f4bdac | |||
| b2eccf48da | |||
| 98daec96f4 | |||
| b8fb5e361a | |||
| 4917cc34e7 | |||
| 66208cc750 | |||
| 9df4cecb27 | |||
| 8338bf6593 | |||
| 1c86c7ddc1 | |||
| ec0dae6909 | |||
| 9556d7bb2b | |||
| 7eb62206da | |||
| d4b288aa9f | |||
| 18b5d648d3 | |||
| 7fcec033b0 | |||
| f1e5ab7715 | |||
| ece49dae92 | |||
| 8e7f7d2e50 | |||
| 56909e7f80 | |||
| 4c58569211 | |||
| 3da0292df5 | |||
| 4663dc19f2 | |||
| 1971a6b958 | |||
| 3515c76f92 | |||
| fb795d0177 | |||
| 868681e9ba | |||
| 9a6b854e5c | |||
| 397bd9aa3c | |||
| 54d35aec03 | |||
| 7b15e26ef7 | |||
| c2ea064a30 | |||
| 7b585ddb84 | |||
| a991d5b9f4 | |||
| 1ab6c6caef | |||
| 24abced8a6 | |||
| d2b9bbf551 | |||
| 14bdc702a5 | |||
| fd8a0e5498 | |||
| ca91ebf33e | |||
| 8df10e8a28 | |||
| 6292041827 | |||
| cc53b1773a | |||
| 708d156131 | |||
| 1266d56c57 | |||
| c4c73a3b44 | |||
| 24a051e949 | |||
| 1fc5fe1ddd | |||
| e819de5611 | |||
| 0cc1957ddd | |||
| 953a4575d7 | |||
| ed94efe520 | |||
| 8c9880c2ba | |||
| 8f52e5f0da | |||
| b4d758c6a7 | |||
| 0de5067c23 | |||
| 1f52bff88a | |||
| e0c45f30a8 | |||
| 6b2ac967a8 | |||
| ce1bde9502 | |||
| 28780bdb62 | |||
| 9959542265 | |||
| a7713fd8eb | |||
| bff5ccf474 | |||
| 9d1d7caf54 | |||
| ee7c1b3a24 | |||
| 8c43f4cc9f | |||
| 496c5f57f6 | |||
| 966acf7fc5 | |||
| 4548bff23b | |||
| 0c24851679 | |||
| f98c0bc576 | |||
| fa49cdeb8a | |||
| 634830af0b | |||
| 77f4c014b4 | |||
| 3b8e4fb5de | |||
| c321bdd891 | |||
| 77430d203d | |||
| 69693630a4 | |||
| 0e16e180df | |||
| 2cd0309d90 | |||
| 52a844087a | |||
| 109eb1124c | |||
| 5c1004b8a3 | |||
| 5309e7f63d | |||
| 686e91d5c0 | |||
| 1355912aa8 | |||
| b9aa614831 | |||
| 1e803c595a | |||
| c52427e1b8 | |||
| 33b62aab65 | |||
| 0961388a0a | |||
| 92c327fccd | |||
| fe7fbf24b9 | |||
| 2998e41620 | |||
| 89e4561e94 | |||
| 92c7a3b3c5 | |||
| f33ef72a29 | |||
| db7a1199cf | |||
| 6a670f942c | |||
| 4d5ae78760 | |||
| 02ca3a12ce | |||
| fc2238cfec | |||
| ab30639634 | |||
| 666b12c1e6 | |||
| 53f38ff0d9 | |||
| 4030001cf8 | |||
| 184f7d3a85 | |||
| 7409199411 | |||
| bc421d6d46 | |||
| 850bbe3b97 | |||
| e95b8a7c7b | |||
| c98c41712d | |||
| df5f0c8f93 | |||
| b05ffcfc57 | |||
| 7015547371 | |||
| 70242a8f17 | |||
| 6b39e44679 | |||
| bb49184116 | |||
| 07dd873f63 | |||
| 00ab1f6e58 | |||
| 8f2e5829ce | |||
| d564f67b2d | |||
| db6aacf45e | |||
| b808bf5374 | |||
| 2fbd688d8f | |||
| d39ae0084a | |||
| f539686553 | |||
| fadee607e9 | |||
| 199b34b5d0 | |||
| 713a61f712 | |||
| 5a4cd49648 | |||
| dec44f9d39 | |||
| 61bb1ebe33 | |||
| 933ce256a6 | |||
| 70348aa81e | |||
| 5f1145b4ce | |||
| 308435acb4 | |||
| a8c579b4c5 | |||
| 321a30e14f | |||
| edadfa06cb | |||
| 21c438f24a | |||
| 4d01802d31 | |||
| 91513c6bd3 | |||
| 623acad16b | |||
| f62a247467 | |||
| 8071b5fc9e | |||
| 6d044ed3d9 | |||
| 966f659277 | |||
| 00e98f2a8b | |||
| ca9f979270 | |||
| 6f1680569b | |||
| f647b94a75 | |||
| b1f50cdf98 | |||
| 26109431b5 | |||
| 36506f7354 | |||
| 60d78e5a11 | |||
| 7569618528 | |||
| f42a899268 | |||
| 46612109ff | |||
| 5712717383 | |||
| a00bc971d5 | |||
| ec965d9e00 | |||
| 507867b76d | |||
| 935d2c9f29 | |||
| 0023c0254f | |||
| 42770ce9a9 | |||
| 810e854153 | |||
| bc7287eaf5 | |||
| 4dc61ed547 | |||
| 2496cd7ffd | |||
| 2d8ddc8239 | |||
| d7174ce4e2 | |||
| 4fe8b57969 | |||
| ed7d3f13b6 | |||
| 55653a2dac | |||
| 4fe9879b14 | |||
| 2f6c6c0026 | |||
| 551fa309a0 | |||
| 476380749b | |||
| f143f75d91 | |||
| 4ed6dd031a | |||
| f13c851d05 | |||
| 328c32c3db | |||
| 5a0251179a | |||
| 450aa66dcd | |||
| 2377be335a | |||
| 9665640c24 | |||
| bd0ce23402 | |||
| 82df7d0a4d | |||
| b3c6973362 | |||
| dc43213556 | |||
| 5959911fff | |||
| 90632e0763 | |||
| 2d621dc0ef | |||
| 9066f3bde6 | |||
| e3529026a0 | |||
| 2f56d7d101 | |||
| 15a84d14d5 | |||
| c184fb81f3 | |||
| 737f272b1e | |||
| 89d622013b | |||
| 44d7ee0937 | |||
| 12440c7f9d | |||
| df740a511b | |||
| 334bea41ec | |||
| fd3d62ca37 | |||
| 3ad6fc7852 | |||
| d61bbb5f37 | |||
| e4a71a9a96 | |||
| 51cd6d5f48 | |||
| 34a39ba884 | |||
| fe9c70b9b5 | |||
| 5d4d21a31a | |||
| a564495dcf | |||
| f070d262a1 | |||
| 12d8c5a611 | |||
| 5251a11c59 | |||
| 522f199222 | |||
| 1e70bd2b95 | |||
| 2830aca413 | |||
| 8995a77306 | |||
| 97e8019261 | |||
| 658b689b12 | |||
| 92282f5320 | |||
| 2cfb7f823f | |||
| 8e02d8b3df | |||
| aa096c380d | |||
| cf0e873506 | |||
| bdceed1023 | |||
| 53f8de7af4 | |||
| c30595af59 | |||
| ec5e497a35 | |||
| b800501c76 | |||
| e2e7f32d6c | |||
| 2cd48e09c7 | |||
| 0d2a976843 | |||
| bc021bf6c6 | |||
| a2545bad49 | |||
| 62bfa92b7a | |||
| 68842b56d4 | |||
| 513ac30d0a | |||
| df5eac853a | |||
| edec1e65d2 | |||
| 1b99f0c8a1 | |||
| 128f077b68 | |||
| 497d52b407 | |||
| e58107b258 | |||
| f0a2fb7e2f | |||
| d7e920d8dd | |||
| 65f957051c | |||
| 6972d683ef | |||
| 60c03cf181 | |||
| 0786b8a1c3 | |||
| da769bbc8c | |||
| 173b5ea5ac | |||
| c5c18d46dc | |||
| 8253bba831 | |||
| b65ae316f8 | |||
| 31f46d8db2 | |||
| 72b01c5382 | |||
| 021be5877a | |||
| 90feb75113 | |||
| b13a4c68ed | |||
| 70937a4a20 | |||
| 3db6e89ccc | |||
| 285f957a5b | |||
| 98c563e0b7 | |||
| 6894a65f72 | |||
| bfff12f527 | |||
| d16e97fdcc | |||
| 389a872baa | |||
| 1127f6768d | |||
| f34b345910 | |||
| 17615d90d4 | |||
| 05866360b7 | |||
| c3ab217820 | |||
| a3ef4709ae | |||
| 6966cdc45b | |||
| 65fa7c7d15 | |||
| 96cf945c7e | |||
| 47f75d41ab | |||
| bede042b12 | |||
| b195c7dc90 | |||
| 2988b04705 | |||
| 78928a3537 | |||
| a1b9c05003 | |||
| ef86897b46 | |||
| 1886d9df92 | |||
| dee930150d | |||
| 3267f5be8c | |||
| 799b4a2618 | |||
| 794ec9478e | |||
| 86d77c31ca | |||
| 8b66c45e9d | |||
| c485e42447 | |||
| 72901ac846 | |||
| 71aad59120 | |||
| 3f476f0c5a | |||
| 5499f63d46 | |||
| 973108a5c8 | |||
| 3094f53f1e | |||
| 263e505748 | |||
| ec33505139 | |||
| cb46936de8 | |||
| bc514fac18 | |||
| ea59dfbb81 | |||
| 8b1b0011f0 | |||
| e6571f9af9 | |||
| dea705b6b1 | |||
| aef659d1e2 | |||
| 94e7b022c7 | |||
| 98e4385879 | |||
| 81eec779d9 | |||
| 735da2261b | |||
| ad3fe959fd | |||
| 66af90fde4 | |||
| d9aa39baad | |||
| c71654fd2c | |||
| 8f72459f1a | |||
| 89985f5c5b | |||
| 4d07ed6b17 | |||
| 42c3218b2c | |||
| 1706ca6140 | |||
| 06ae551f04 | |||
| c22ea810ee | |||
| 0167ce3ded | |||
| c6f4528a7f | |||
| b3016e0e1b | |||
| 7286f62c01 | |||
| 50dcf3bdae | |||
| e8dae4203d | |||
| 30279d16dd | |||
| 8088ae2b3c | |||
| 362a7ed6fe | |||
| 2298b97814 | |||
| f2289ba9a2 | |||
| 731f9bd3f2 | |||
| 804e7a6142 | |||
| c1bb75a533 | |||
| e319a383dc | |||
| 72999a7eb9 | |||
| cd505acf19 | |||
| cf9ef3275d | |||
| 11e2c441cd | |||
| c499b15fe0 | |||
| 21f2c3435b | |||
| 61305a7684 | |||
| 85eec82bc7 | |||
| 841d54bc4a | |||
| e842b68d67 | |||
| 0fbe9dfb95 | |||
| 6f8ea0e2ea | |||
| 63a54665f2 | |||
| 69b0484c6b | |||
| bcc0d0e9eb | |||
| 80d7a98d84 | |||
| 19ff10cf06 | |||
| 71a1d2bd85 | |||
| 3dacb31271 | |||
| 15a4560637 | |||
| 6010b427b4 | |||
| c0ca1a22bf | |||
| e143bae8ea | |||
| 51da83291b | |||
| def77d7882 | |||
| 3f1cc42f4b | |||
| 164034c4ec | |||
| 289fe1ef76 | |||
| 1d58d59c86 | |||
| 24125dd102 | |||
| 1263887287 | |||
| a8d1cc6598 | |||
| d0c9577d6c | |||
| 17e1f9a22d | |||
| b526202a35 | |||
| 3ff16f4ed9 | |||
| 1fca1d68b5 | |||
| 86a1fa9984 | |||
| 1840aa4cb1 | |||
| f4e99c54bb | |||
| 1c6506769a | |||
| d13fa3fb6d | |||
| 6418439f08 | |||
| 1ae5e63101 | |||
| 70902e0f9c | |||
| b005459b1e | |||
| 74653366f3 | |||
| e0008d0ae3 | |||
| 7d0f502483 | |||
| 0301d8bd62 | |||
| ed154400cd | |||
| 21f3353471 | |||
| 3b1041475e | |||
| 3e74f4c3ce | |||
| 0af73bec89 | |||
| 059ac53abb | |||
| d968714802 | |||
| ff74536adb | |||
| 8221c18d51 | |||
| a40d205eb5 | |||
| ec72828d18 | |||
| e8e497f4d4 | |||
| 6b1a68d2b5 | |||
| 8756f250eb | |||
| 52902fb436 | |||
| 24112d09b9 | |||
| e1a1f5652f | |||
| 413d6b046b | |||
| fa19d59444 | |||
| 6bfebfc7b8 | |||
| 6f7a1abba4 | |||
| f3e2bf2b7c | |||
| 1c3887d692 | |||
| 40df9d3653 | |||
| 85270722f5 | |||
| 5cff9f0e9a | |||
| bdf49f89f5 | |||
| 59f3aac4ac | |||
| 1126aaf1a2 | |||
| ceb90e7131 | |||
| 93244439b5 | |||
| 3191a94345 | |||
| e45feac1ad | |||
| 5e35fa16d5 | |||
| 08b3b1b449 | |||
| 1af36b5ffd | |||
| 058ba89dab | |||
| 16e0ea970e | |||
| fa5e817df9 | |||
| ac424c9bb6 | |||
| b7eab5a554 | |||
| a6a174d817 | |||
| d0064b9d05 | |||
| cf32b66e19 | |||
| c56217eee1 | |||
| 29570a7a84 | |||
| b0199478df | |||
| 5b1cfc900d | |||
| 02804d1cec | |||
| 004e5f9c66 | |||
| 4ce48de8d6 | |||
| 32324be121 | |||
| 80528b181a | |||
| e037ee80fb | |||
| fc649d6700 | |||
| 96baa3d320 | |||
| eadb6a737e | |||
| 9a88b86217 | |||
| 77c2211b75 | |||
| e2bb36513f | |||
| 664308286c | |||
| 8553738434 | |||
| 7a414b0522 | |||
| ba27a4f741 | |||
| 18ea34c2a5 | |||
| db75447658 | |||
| f594f67868 | |||
| 77786dec80 | |||
| 01dc5c1a45 | |||
| 45202e1154 | |||
| 8c12581e12 | |||
| 6bbba0d921 | |||
| 0d3166adb5 | |||
| 5a43147f56 | |||
| 3e3c2994be | |||
| 4448ddc159 | |||
| d293116fda | |||
| 2f2d7e6358 | |||
| db6cff3f7a | |||
| 283f265fbe | |||
| 091b2fad14 | |||
| aba9cc490d | |||
| 9cb9c85728 | |||
| 7edc187fb7 | |||
| 3b35ab9fd5 | |||
| 5678028de7 | |||
| 100e9f3d12 | |||
| 5bcc32e756 | |||
| 202affd1a2 | |||
| ab72dbee64 | |||
| 496a40a49b | |||
| 1a24b6b2d7 | |||
| 38f9a7b1c8 | |||
| ab121de9de | |||
| 429ea1545c | |||
| 9c397f9f63 | |||
| 85fea314bb | |||
| 58f7ad98d7 | |||
| 422889d0eb | |||
| bb279dc22e | |||
| 507ab4cf6b | |||
| 36877227fb | |||
| b08db1daa2 | |||
| 65970afe4e | |||
| 9cb1ac35c2 | |||
| 9089f50463 | |||
| 4d3651a151 | |||
| d977e539d5 | |||
| 2b89024ea2 | |||
| a9bb5dbe8d | |||
| 8aa07c75a1 | |||
| ed21686174 | |||
| 60af40cbfd | |||
| 17fed89644 | |||
| 350e280741 | |||
| a9c01e8c52 | |||
| e44cedd104 | |||
| 82f5d60aa6 | |||
| c5910cb7dc | |||
| 9dd8490aa6 | |||
| 3486243420 | |||
| 79e685233f | |||
| 38c0a5d05f | |||
| 444b165c09 | |||
| 060b55064f | |||
| 05818d70eb | |||
| 4c86ca6666 | |||
| 718235f569 | |||
| 8dcd8b8132 | |||
| eed3f1a83d | |||
| 95fab37f66 | |||
| 3c7eb5f50c | |||
| 512f9e294f | |||
| 831b569c5f | |||
| 34f90c2bdd | |||
| fa72c683bd | |||
| 3f8287efe3 | |||
| 4a086154fa | |||
| 75ba3e39d5 | |||
| 2b9d5423de | |||
| 5e8961ebe0 | |||
| 8737636d72 | |||
| c9950e69a2 | |||
| 0f9cf691a8 | |||
| e5e71433c4 | |||
| 3b2e74d33e | |||
| d0d8b19dab | |||
| c4b5d16556 | |||
| 04881b93f1 | |||
| 89d0471764 | |||
| 679f5d18ff | |||
| 6aa7833c71 | |||
| df4a6bbcc2 | |||
| 6c375449df | |||
| 0555022bc2 | |||
| 6d05b0db52 | |||
| ca40c1f4ee | |||
| 1bd8a0a2aa | |||
| e4fac0f267 | |||
| 8be3607c28 | |||
| ac7aae0dd2 | |||
| ab4d19f260 | |||
| f18f4c297d | |||
| ee121e7fc3 | |||
| e82f7b14b8 | |||
| 3ed13a332c | |||
| 780fe61954 | |||
| a588c0f85b | |||
| b5e4c94307 | |||
| 4f5d1c5cbf | |||
| a19ee0e707 | |||
| f5bb389882 | |||
| 95bce65a9f | |||
| 7cdd1a5c8e | |||
| c8d028d3f1 | |||
| 961fd545cf | |||
| 6bbcb9c2e5 | |||
| 93482c3a53 | |||
| f393faa86c | |||
| 69f18d2a63 | |||
| d41b8d3f32 | |||
| ec40f18efb | |||
| 645bc22468 | |||
| 92eb0a6d9c | |||
| c5098c6704 | |||
| 07dc19ad69 | |||
| fbf616e651 | |||
| 1ddeb827a3 | |||
| bcd507a3f7 | |||
| c45b1e62dd | |||
| b36c124f3d | |||
| 43b5384168 | |||
| ffb43a83b0 | |||
| 3e9d0de501 | |||
| 440989aeab | |||
| c4107dec84 | |||
| 3f81c4a322 | |||
| c9236c6860 | |||
| 960252e17e | |||
| 9b55ca16d5 | |||
| 17147e5204 | |||
| a9369b1941 | |||
| 89fc5ef888 | |||
| 5dcd9c53e3 | |||
| 758066eda4 | |||
| 6a12f2de83 | |||
| fe8663110a | |||
| 0f6761962a | |||
| bc066df1f6 | |||
| 246b39cba9 | |||
| facd24b4c7 | |||
| b7b3b75b2c | |||
| 98ac97e2a0 | |||
| 9aa6e8bfa4 | |||
| 03a65d8916 | |||
| 5e430e98cb | |||
| 068f22aee0 | |||
| 99d570fef4 | |||
| 4a2bc331fb | |||
| 3fef39fc68 | |||
| 5d434d4d19 | |||
| 440113b457 | |||
| 41ff45a7a7 | |||
| fa4274319d | |||
| 06c5a9e6ff | |||
| ca3605500e | |||
| 6beb56ba1f | |||
| f83fc14e4d | |||
| 844eae52fb | |||
| cf304a8870 | |||
| 8ec36b0fe2 | |||
| 31a9ee8172 | |||
| 2787a52470 | |||
| d0308d8d76 | |||
| 144c240841 | |||
| 434c5f8a56 | |||
| 0ce4a24888 | |||
| bfd1dd07d8 | |||
| 51e05c09c3 | |||
| 02990bd6b9 | |||
| 6d38596df0 | |||
| a66195b8b9 | |||
| ae7953ff01 | |||
| e5dc80a10c | |||
| f080964380 | |||
| 2ea79c66cf | |||
| 36b738c185 | |||
| 1a89875c36 | |||
| c29b6e8c15 | |||
| ff710b83c3 | |||
| c619279f5e | |||
| 5772b0fba2 | |||
| c89d473133 | |||
| 0e540897e6 | |||
| 27e2fe6981 | |||
| e35bc644de | |||
| 5d7d6cb52b | |||
| 913abd448b | |||
| dfe9aa213b | |||
| dc24b81fef | |||
| cff1f1943e | |||
| fe5d17b166 | |||
| cdda22dad7 | |||
| 641a281bef | |||
| e70b33b720 | |||
| 8578d28631 | |||
| c7a1ecd9f6 | |||
| 829adb5f21 | |||
| b6e79ab8d8 | |||
| 2ba2d02202 | |||
| 2d117605b8 | |||
| 237e1a85ce | |||
| 5cb64c1faa | |||
| e714f3cee9 | |||
| 16deaa8611 | |||
| ffa36a4bbe | |||
| 5ed65e12e1 | |||
| d1c15a50c9 | |||
| bc121625c3 | |||
| 251a824d8e | |||
| efde049bdf | |||
| 88498220a1 | |||
| dfdadbdbe2 | |||
| fb8b947963 | |||
| b844baf8be | |||
| 0c794fc199 | |||
| 2f4feacac7 | |||
| 7d7edc8820 | |||
| 18c838f586 | |||
| 21c63124ab | |||
| 350cefe41e | |||
| 47c56f3475 | |||
| 4b5ac4ceae | |||
| 8ed193c3ee | |||
| 40e430e634 | |||
| aaf94b8f75 | |||
| 18e6878f5a | |||
| 1041ccaf2e | |||
| fb7b0157c1 | |||
| 8827e7fc3f | |||
| b85ec901dd | |||
| 2faa795209 | |||
| ca5a9de8c5 | |||
| b9287872eb | |||
| 8b448de4b0 | |||
| 06588b2cc0 | |||
| e8e4566aaa | |||
| 94ccf35860 | |||
| cf0386e763 | |||
| a603f86d65 | |||
| 265e374204 | |||
| e62e93aa0d | |||
| d00a6f52b4 | |||
| 0c75121648 | |||
| 5681222a0b | |||
| a45b7aa7ee | |||
| 3093cc28f1 | |||
| 60f544f331 | |||
| 0ff581cece | |||
| b93a787b3e | |||
| 4ab37af070 | |||
| f85f002b37 | |||
| f7bdcf959f | |||
| 64abea941e | |||
| b9bebd0e36 | |||
| de703e70e7 | |||
| b35acd2249 | |||
| a45c466000 | |||
| 3b7f331372 | |||
| 3145f21de1 | |||
| f8550e7982 | |||
| ab0847d328 | |||
| 54339683ac | |||
| 65e260281b | |||
| f29bd28d6a | |||
| 9654f2cd71 | |||
| 2337078a30 | |||
| 29d9535277 | |||
| 4b162c766d | |||
| 606c17e6ef | |||
| daf44482e9 | |||
| 7da0e04ca3 | |||
| 45c42e01a3 | |||
| 74af3b176d | |||
| 7e9f55f138 | |||
| 7354b4ea0e | |||
| 9f6edf2171 | |||
| 511c9addfa | |||
| 8616e10e89 | |||
| 91406c9103 | |||
| aa383f1b47 | |||
| 8bb22c4544 | |||
| ff383cdd57 | |||
| 23e190ead5 | |||
| d997069c62 | |||
| 96cc77d435 | |||
| 1daf3fafc0 | |||
| 7522b6a2e3 | |||
| ece94bb4e2 | |||
| cd44d4e463 | |||
| 395c985759 | |||
| 684d866e20 | |||
| 3b2e8b4a0a | |||
| 816e06e809 | |||
| 6924954471 | |||
| 2875ff2934 | |||
| b1ddacfc8e | |||
| 6d353a7918 | |||
| f9e09bd963 | |||
| 4f657eeed9 | |||
| 60e57b8448 | |||
| 628f99bf45 | |||
| 68e670d8ea | |||
| af9b21a388 | |||
| de08d71fc8 | |||
| 4846674f5c | |||
| ac66136751 | |||
| 5617f26568 | |||
| ad81f22148 | |||
| aabf264460 | |||
| c22d3a30a1 | |||
| 64d3271039 | |||
| c83c2c0f47 | |||
| 5fdb912a82 | |||
| 7a06e15952 | |||
| b64f314662 | |||
| e5994e8c04 | |||
| 1fb4f49cca | |||
| 32b4b57b26 | |||
| fd7e75a8a5 | |||
| abd7d4394b | |||
| e4256cd769 | |||
| 5a6a4cef8b | |||
| 8721afb2c6 | |||
| 0e199a5aec | |||
| 320947908b | |||
| 2e55209603 | |||
| f8979fac97 | |||
| 6a33a850fc | |||
| 9164ab6e39 | |||
| 2af1421e34 | |||
| 5a9fa0fe57 | |||
| c1db07d55b | |||
| c5b7a25ee6 | |||
| 7cc665e9c7 | |||
| 05fd85142e | |||
| e62ea2b803 | |||
| 972142a9af | |||
| b25f3693bb | |||
| 369caa6183 | |||
| 00268741c2 | |||
| 4c492f8646 | |||
| 605b0547de | |||
| eea1aff443 | |||
| 1bc5a5abe9 | |||
| be4ea98f29 | |||
| 845215d826 | |||
| 2a9c11965b | |||
| 8c3d8a68f7 | |||
| 969010296d | |||
| 87186b9710 | |||
| 539ad434cb | |||
| af4fa2a55b | |||
| 15aec0b054 | |||
| 7974bbcaf7 | |||
| b4cbad15de | |||
| fe1eb00c0f | |||
| 03df75c0de | |||
| 21c6513a7c | |||
| 8938d826d9 | |||
| e71a526011 | |||
| f3f6960d3e | |||
| 8952d3f100 | |||
| 0bb9e2d8d7 | |||
| 07b6c89868 | |||
| ffba0168b1 | |||
| 26a276cea1 | |||
| 3e2eb62931 | |||
| 62104652c8 | |||
| c950c91696 | |||
| 73e4f14d1b | |||
| 87d3c32f2e | |||
| b8e0488c90 | |||
| 6c23087aa7 | |||
| a6d9966786 | |||
| 1a8167e781 | |||
| 47dd1d85aa | |||
| c29edc7551 | |||
| fdddb80b70 | |||
| 70a804d98e | |||
| c12caea3d4 | |||
| 191a7fd205 | |||
| b0cb0168f4 | |||
| fb762d4e5c | |||
| ed0e988020 | |||
| 20cb5f4086 | |||
| de5d2667ab | |||
| 4a54efd42a | |||
| 5769d5fc86 | |||
| 0225f34969 | |||
| 23c69f1ef0 | |||
| 6e6b38e0b5 | |||
| 2e3892e951 | |||
| abe62e33e6 | |||
| 78731d0b96 | |||
| 56a58d538c | |||
| ef1c918973 | |||
| 3b9fce4e1c | |||
| 7b9e9eb98d | |||
| 0456d880f0 | |||
| 705776211a | |||
| 94b8228ba1 | |||
| 0b7d0d0f79 | |||
| c29501c06d | |||
| c152025706 | |||
| 0444dd2b70 | |||
| 15aded1e2a | |||
| cbff142b8e | |||
| ee16cb39bf | |||
| 7f0eba32de | |||
| 144c9ba7c3 | |||
| b676ff39a4 | |||
| fe709d665d | |||
| d692104051 | |||
| 17ebd603e1 | |||
| b24eb6c565 | |||
| c449504287 | |||
| 8df6b374bb | |||
| e84e1bbff3 | |||
| 3cfc322bdf | |||
| 6da402bf34 | |||
| 126c58ede4 | |||
| 6217d91173 | |||
| d28be61fb5 | |||
| a96ef27857 | |||
| 255541d592 | |||
| 19b4c997e9 | |||
| 93819366a4 | |||
| 7726e2e2d6 | |||
| d95ccdd4ec | |||
| 5698d90e6a | |||
| 44b87b5d95 | |||
| 334412f1cc | |||
| afcb4cd69a | |||
| d8a7c52ee6 | |||
| 05a70bac38 | |||
| 951f10fd3e | |||
| 10a5e2a8de | |||
| 82c32f9a80 | |||
| 17ae93679d | |||
| 27ffa62131 | |||
| 97a4db9c97 | |||
| 6f1a49b8b7 | |||
| 740807d2e3 | |||
| 066b01a788 | |||
| e7b4c991ee | |||
| 42ee1fc276 | |||
| d56935d66f | |||
| 9e2565d84d | |||
| 73c72b4148 | |||
| 244af9a0da | |||
| 2eb043fb36 | |||
| 2454da368a | |||
| 8e899e949d | |||
| 6b0b697fb8 | |||
| 9a854a2fc9 | |||
| b6f1cf03e3 | |||
| d26cdca687 | |||
| dbeee32b59 | |||
| 0a8663b425 |
+2
-4
@@ -3,8 +3,8 @@
|
||||
*/**/build/
|
||||
node_modules/
|
||||
vendor/
|
||||
admin/tool/policy/amd/src/jquery-eu-cookie-law-popup.js
|
||||
admin/tool/usertours/amd/src/tour.js
|
||||
admin/tool/usertours/amd/src/popper.js
|
||||
auth/cas/CAS/
|
||||
enrol/lti/ims-blti/
|
||||
filter/algebra/AlgParser.pm
|
||||
@@ -41,6 +41,7 @@ lib/html2text/
|
||||
lib/markdown/
|
||||
lib/recaptchalib.php
|
||||
lib/xhprof/
|
||||
lib/xmlize.php
|
||||
lib/horde/
|
||||
lib/requirejs/
|
||||
lib/amd/src/loglevel.js
|
||||
@@ -57,10 +58,7 @@ lib/maxmind/MaxMind/
|
||||
lib/ltiprovider/
|
||||
lib/amd/src/truncate.js
|
||||
lib/fonts/
|
||||
lib/amd/src/adapter.js
|
||||
lib/validateurlsyntax.php
|
||||
lib/amd/src/popper.js
|
||||
lib/geopattern-php/
|
||||
media/player/videojs/amd/src/video-lazy.js
|
||||
media/player/videojs/amd/src/Youtube-lazy.js
|
||||
media/player/videojs/videojs/
|
||||
|
||||
+2
-4
@@ -4,8 +4,8 @@ theme/clean/style/custom.css
|
||||
theme/more/style/custom.css
|
||||
node_modules/
|
||||
vendor/
|
||||
admin/tool/policy/amd/src/jquery-eu-cookie-law-popup.js
|
||||
admin/tool/usertours/amd/src/tour.js
|
||||
admin/tool/usertours/amd/src/popper.js
|
||||
auth/cas/CAS/
|
||||
enrol/lti/ims-blti/
|
||||
filter/algebra/AlgParser.pm
|
||||
@@ -42,6 +42,7 @@ lib/html2text/
|
||||
lib/markdown/
|
||||
lib/recaptchalib.php
|
||||
lib/xhprof/
|
||||
lib/xmlize.php
|
||||
lib/horde/
|
||||
lib/requirejs/
|
||||
lib/amd/src/loglevel.js
|
||||
@@ -58,10 +59,7 @@ lib/maxmind/MaxMind/
|
||||
lib/ltiprovider/
|
||||
lib/amd/src/truncate.js
|
||||
lib/fonts/
|
||||
lib/amd/src/adapter.js
|
||||
lib/validateurlsyntax.php
|
||||
lib/amd/src/popper.js
|
||||
lib/geopattern-php/
|
||||
media/player/videojs/amd/src/video-lazy.js
|
||||
media/player/videojs/amd/src/Youtube-lazy.js
|
||||
media/player/videojs/videojs/
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@ before_script:
|
||||
# We need the official upstream for comparison
|
||||
git remote add upstream https://github.com/moodle/moodle.git;
|
||||
|
||||
git fetch upstream master;
|
||||
git fetch upstream MOODLE_34_STABLE;
|
||||
export GIT_PREVIOUS_COMMIT="`git merge-base FETCH_HEAD $TRAVIS_COMMIT`";
|
||||
export GIT_COMMIT="$TRAVIS_COMMIT";
|
||||
export UPSTREAM_FETCH_HEAD=`git rev-parse FETCH_HEAD`
|
||||
|
||||
@@ -73,7 +73,6 @@ foreach ($rs as $user) {
|
||||
echo "Redeleting user $user->id: $user->username ($user->email)\n";
|
||||
delete_user($user);
|
||||
}
|
||||
$rs->close();
|
||||
|
||||
cli_heading('Deleting all leftovers');
|
||||
|
||||
|
||||
@@ -219,6 +219,7 @@ $databases = array('mysqli' => moodle_database::get_driver_instance('mysqli', 'n
|
||||
'pgsql' => moodle_database::get_driver_instance('pgsql', 'native'),
|
||||
'oci' => moodle_database::get_driver_instance('oci', 'native'),
|
||||
'sqlsrv' => moodle_database::get_driver_instance('sqlsrv', 'native'), // MS SQL*Server PHP driver
|
||||
'mssql' => moodle_database::get_driver_instance('mssql', 'native'), // FreeTDS driver
|
||||
);
|
||||
foreach ($databases as $type=>$database) {
|
||||
if ($database->driver_installed() !== true) {
|
||||
@@ -442,7 +443,6 @@ if ($interactive) {
|
||||
}
|
||||
}
|
||||
$CFG->tempdir = $CFG->dataroot.'/temp';
|
||||
$CFG->backuptempdir = $CFG->tempdir.'/backup';
|
||||
$CFG->cachedir = $CFG->dataroot.'/cache';
|
||||
$CFG->localcachedir = $CFG->dataroot.'/localcache';
|
||||
|
||||
|
||||
@@ -131,11 +131,10 @@ if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
|
||||
cli_error(get_string('pluginschecktodo', 'admin'));
|
||||
}
|
||||
|
||||
$a = new stdClass();
|
||||
$a->oldversion = $oldversion;
|
||||
$a->newversion = $newversion;
|
||||
|
||||
if ($interactive) {
|
||||
$a = new stdClass();
|
||||
$a->oldversion = $oldversion;
|
||||
$a->newversion = $newversion;
|
||||
echo cli_heading(get_string('databasechecking', '', $a)) . PHP_EOL;
|
||||
}
|
||||
|
||||
@@ -194,5 +193,5 @@ admin_apply_default_settings(NULL, false);
|
||||
// to immediately start browsing the site.
|
||||
upgrade_themes();
|
||||
|
||||
echo get_string('cliupgradefinished', 'admin', $a)."\n";
|
||||
echo get_string('cliupgradefinished', 'admin')."\n";
|
||||
exit(0); // 0 means success
|
||||
|
||||
@@ -29,8 +29,7 @@ $mode = required_param('mode', PARAM_ALPHANUMEXT);
|
||||
$classformode = array(
|
||||
'assign' => 'core_role_allow_assign_page',
|
||||
'override' => 'core_role_allow_override_page',
|
||||
'switch' => 'core_role_allow_switch_page',
|
||||
'view' => 'core_role_allow_view_page'
|
||||
'switch' => 'core_role_allow_switch_page'
|
||||
);
|
||||
if (!isset($classformode[$mode])) {
|
||||
print_error('invalidmode', '', '', $mode);
|
||||
@@ -59,9 +58,6 @@ if (optional_param('submit', false, PARAM_BOOL) && data_submitted() && confirm_s
|
||||
case 'switch':
|
||||
$event = \core\event\role_allow_switch_updated::create(array('context' => $syscontext));
|
||||
break;
|
||||
case 'view':
|
||||
$event = \core\event\role_allow_view_updated::create(array('context' => $syscontext));
|
||||
break;
|
||||
}
|
||||
if ($event) {
|
||||
$event->trigger();
|
||||
|
||||
@@ -33,7 +33,7 @@ class core_role_allow_assign_page extends core_role_allow_role_page {
|
||||
}
|
||||
|
||||
protected function set_allow($fromroleid, $targetroleid) {
|
||||
core_role_set_assign_allowed($fromroleid, $targetroleid);
|
||||
allow_assign($fromroleid, $targetroleid);
|
||||
}
|
||||
|
||||
protected function get_cell_tooltip($fromrole, $targetrole) {
|
||||
|
||||
@@ -33,7 +33,7 @@ class core_role_allow_override_page extends core_role_allow_role_page {
|
||||
}
|
||||
|
||||
protected function set_allow($fromroleid, $targetroleid) {
|
||||
core_role_set_override_allowed($fromroleid, $targetroleid);
|
||||
allow_override($fromroleid, $targetroleid);
|
||||
}
|
||||
|
||||
protected function get_cell_tooltip($fromrole, $targetrole) {
|
||||
|
||||
@@ -41,7 +41,7 @@ class core_role_allow_switch_page extends core_role_allow_role_page {
|
||||
}
|
||||
|
||||
protected function set_allow($fromroleid, $targetroleid) {
|
||||
core_role_set_switch_allowed($fromroleid, $targetroleid);
|
||||
allow_switch($fromroleid, $targetroleid);
|
||||
}
|
||||
|
||||
protected function is_allowed_target($targetroleid) {
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Role view matrix.
|
||||
*
|
||||
* @package core_role
|
||||
* @copyright 2016 onwards Andrew Hancox <andrewdchancox@googlemail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Subclass of role_allow_role_page for the Allow views tab.
|
||||
*
|
||||
* @package core_role
|
||||
* @copyright 2016 onwards Andrew Hancox <andrewdchancox@googlemail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_role_allow_view_page extends core_role_allow_role_page {
|
||||
/** @var array */
|
||||
protected $allowedtargetroles;
|
||||
|
||||
/**
|
||||
* core_role_allow_view_page constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct('role_allow_view', 'allowview');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow from role to view target role.
|
||||
* @param int $fromroleid
|
||||
* @param int $targetroleid
|
||||
*/
|
||||
protected function set_allow($fromroleid, $targetroleid) {
|
||||
core_role_set_view_allowed($fromroleid, $targetroleid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tool tip for cell.
|
||||
* @param string $fromrole
|
||||
* @param string $targetrole
|
||||
* @return string
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
protected function get_cell_tooltip($fromrole, $targetrole) {
|
||||
$a = new stdClass;
|
||||
$a->fromrole = $fromrole->localname;
|
||||
$a->targetrole = $targetrole->localname;
|
||||
return get_string('allowroletoview', 'core_role', $a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get intro text for role allow view page.
|
||||
* @return string
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
public function get_intro_text() {
|
||||
return get_string('configallowview', 'core_admin');
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,6 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
protected $allowassign;
|
||||
protected $allowoverride;
|
||||
protected $allowswitch;
|
||||
protected $allowview;
|
||||
|
||||
public function __construct($context, $roleid) {
|
||||
$this->roleid = $roleid;
|
||||
@@ -73,7 +72,6 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
$this->allowassign = array_keys($this->get_allow_roles_list('assign'));
|
||||
$this->allowoverride = array_keys($this->get_allow_roles_list('override'));
|
||||
$this->allowswitch = array_keys($this->get_allow_roles_list('switch'));
|
||||
$this->allowview = array_keys($this->get_allow_roles_list('view'));
|
||||
|
||||
} else {
|
||||
$this->role = new stdClass;
|
||||
@@ -85,7 +83,6 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
$this->allowassign = array();
|
||||
$this->allowoverride = array();
|
||||
$this->allowswitch = array();
|
||||
$this->allowview = array();
|
||||
}
|
||||
parent::load_current_permissions();
|
||||
}
|
||||
@@ -167,10 +164,6 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
if (!is_null($allow)) {
|
||||
$this->allowswitch = $allow;
|
||||
}
|
||||
$allow = optional_param_array('allowview', null, PARAM_INT);
|
||||
if (!is_null($allow)) {
|
||||
$this->allowview = $allow;
|
||||
}
|
||||
|
||||
// Now read the permissions for each capability.
|
||||
parent::read_submitted_permissions();
|
||||
@@ -187,8 +180,7 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
* @param int $roleid role id or 0 for no role
|
||||
* @param array $options array with following keys:
|
||||
* 'name', 'shortname', 'description', 'permissions', 'archetype',
|
||||
* 'contextlevels', 'allowassign', 'allowoverride', 'allowswitch',
|
||||
* 'allowview'
|
||||
* 'contextlevels', 'allowassign', 'allowoverride', 'allowswitch'
|
||||
*/
|
||||
public function force_duplicate($roleid, array $options) {
|
||||
global $DB;
|
||||
@@ -225,9 +217,6 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
if ($options['allowswitch']) {
|
||||
$this->allowswitch = array();
|
||||
}
|
||||
if ($options['allowview']) {
|
||||
$this->allowview = array();
|
||||
}
|
||||
|
||||
if ($options['permissions']) {
|
||||
foreach ($this->capabilities as $capid => $cap) {
|
||||
@@ -273,9 +262,6 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
if ($options['allowswitch']) {
|
||||
$this->allowswitch = array_keys($this->get_allow_roles_list('switch', $roleid));
|
||||
}
|
||||
if ($options['allowview']) {
|
||||
$this->allowview = array_keys($this->get_allow_roles_list('view', $roleid));
|
||||
}
|
||||
|
||||
if ($options['permissions']) {
|
||||
$this->permissions = $DB->get_records_menu('role_capabilities',
|
||||
@@ -296,8 +282,7 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
* @param string $archetype
|
||||
* @param array $options array with following keys:
|
||||
* 'name', 'shortname', 'description', 'permissions', 'archetype',
|
||||
* 'contextlevels', 'allowassign', 'allowoverride', 'allowswitch',
|
||||
* 'allowview'
|
||||
* 'contextlevels', 'allowassign', 'allowoverride', 'allowswitch'
|
||||
*/
|
||||
public function force_archetype($archetype, array $options) {
|
||||
$archetypes = get_role_archetypes();
|
||||
@@ -338,9 +323,6 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
if ($options['allowswitch']) {
|
||||
$this->allowswitch = get_default_role_archetype_allows('switch', $archetype);
|
||||
}
|
||||
if ($options['allowview']) {
|
||||
$this->allowview = get_default_role_archetype_allows('view', $archetype);
|
||||
}
|
||||
|
||||
if ($options['permissions']) {
|
||||
$defaultpermissions = get_default_capabilities($archetype);
|
||||
@@ -360,8 +342,7 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
* @param string $xml
|
||||
* @param array $options array with following keys:
|
||||
* 'name', 'shortname', 'description', 'permissions', 'archetype',
|
||||
* 'contextlevels', 'allowassign', 'allowoverride', 'allowswitch',
|
||||
* 'allowview'
|
||||
* 'contextlevels', 'allowassign', 'allowoverride', 'allowswitch'
|
||||
*/
|
||||
public function force_preset($xml, array $options) {
|
||||
if (!$info = core_role_preset::parse_preset($xml)) {
|
||||
@@ -398,7 +379,7 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array('assign', 'override', 'switch', 'view') as $type) {
|
||||
foreach (array('assign', 'override', 'switch') as $type) {
|
||||
if ($options['allow'.$type]) {
|
||||
if (isset($info['allow'.$type])) {
|
||||
$this->{'allow'.$type} = $info['allow'.$type];
|
||||
@@ -459,7 +440,6 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
$this->save_allow('assign');
|
||||
$this->save_allow('override');
|
||||
$this->save_allow('switch');
|
||||
$this->save_allow('view');
|
||||
|
||||
// Permissions.
|
||||
parent::save_changes();
|
||||
@@ -471,7 +451,7 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
$current = array_keys($this->get_allow_roles_list($type));
|
||||
$wanted = $this->{'allow'.$type};
|
||||
|
||||
$addfunction = "core_role_set_{$type}_allowed";
|
||||
$addfunction = 'allow_'.$type;
|
||||
$deltable = 'role_allow_'.$type;
|
||||
$field = 'allow'.$type;
|
||||
|
||||
@@ -528,11 +508,9 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
if (!$this->disabled) {
|
||||
$output .= '<input type="hidden" name="contextlevel' . $cl . '" value="0" />';
|
||||
}
|
||||
$output .= '<div class="form-check justify-content-start w-100">';
|
||||
$output .= '<input class="form-check-input" type="checkbox" id="cl' . $cl . '" name="contextlevel' . $cl .
|
||||
$output .= '<input type="checkbox" id="cl' . $cl . '" name="contextlevel' . $cl .
|
||||
'" value="1" ' . $extraarguments . '/> ';
|
||||
$output .= '<label class="form-check-label" for="cl' . $cl . '">' . $clname . "</label>\n";
|
||||
$output .= '</div>';
|
||||
$output .= '<label for="cl' . $cl . '">' . $clname . "</label><br />\n";
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
@@ -547,7 +525,7 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
protected function get_allow_roles_list($type, $roleid = null) {
|
||||
global $DB;
|
||||
|
||||
if ($type !== 'assign' and $type !== 'switch' and $type !== 'override' and $type !== 'view') {
|
||||
if ($type !== 'assign' and $type !== 'switch' and $type !== 'override') {
|
||||
debugging('Invalid role allowed type specified', DEBUG_DEVELOPER);
|
||||
return array();
|
||||
}
|
||||
@@ -571,11 +549,11 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
/**
|
||||
* Returns an array of roles with the allowed type.
|
||||
*
|
||||
* @param string $type Must be one of: assign, switch, override or view.
|
||||
* @param string $type Must be one of: assign, switch, or override.
|
||||
* @return array Am array of role names with the allowed type
|
||||
*/
|
||||
protected function get_allow_role_control($type) {
|
||||
if ($type !== 'assign' and $type !== 'switch' and $type !== 'override' and $type !== 'view') {
|
||||
if ($type !== 'assign' and $type !== 'switch' and $type !== 'override') {
|
||||
debugging('Invalid role allowed type specified', DEBUG_DEVELOPER);
|
||||
return '';
|
||||
}
|
||||
@@ -665,7 +643,6 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
$this->print_field('menuallowassign', get_string('allowassign', 'core_role'), $this->get_allow_role_control('assign'));
|
||||
$this->print_field('menuallowoverride', get_string('allowoverride', 'core_role'), $this->get_allow_role_control('override'));
|
||||
$this->print_field('menuallowswitch', get_string('allowswitch', 'core_role'), $this->get_allow_role_control('switch'));
|
||||
$this->print_field('menuallowview', get_string('allowview', 'core_role'), $this->get_allow_role_control('view'));
|
||||
if ($risks = $this->get_role_risks_info()) {
|
||||
$this->print_field('', get_string('rolerisks', 'core_role'), $risks);
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ class core_role_preset {
|
||||
$contextlevels->appendChild($dom->createElement('level', $name));
|
||||
}
|
||||
|
||||
foreach (array('assign', 'override', 'switch', 'view') as $type) {
|
||||
foreach (array('assign', 'override', 'switch') as $type) {
|
||||
$allows = $dom->createElement('allow'.$type);
|
||||
$top->appendChild($allows);
|
||||
$records = $DB->get_records('role_allow_'.$type, array('roleid'=>$roleid), "allow$type ASC");
|
||||
@@ -205,7 +205,7 @@ class core_role_preset {
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array('assign', 'override', 'switch', 'view') as $type) {
|
||||
foreach (array('assign', 'override', 'switch') as $type) {
|
||||
$values = self::get_node_children_values($dom, '/role/allow'.$type, 'shortname');
|
||||
if (!isset($values)) {
|
||||
$info['allow'.$type] = null;
|
||||
|
||||
@@ -79,7 +79,6 @@ class core_role_preset_form extends moodleform {
|
||||
$mform->addElement('advcheckbox', 'allowassign', get_string('allowassign', 'core_role'));
|
||||
$mform->addElement('advcheckbox', 'allowoverride', get_string('allowoverride', 'core_role'));
|
||||
$mform->addElement('advcheckbox', 'allowswitch', get_string('allowswitch', 'core_role'));
|
||||
$mform->addElement('advcheckbox', 'allowview', get_string('allowview', 'core_role'));
|
||||
$mform->addElement('advcheckbox', 'permissions', get_string('permissions', 'core_role'));
|
||||
}
|
||||
|
||||
|
||||
@@ -313,9 +313,6 @@ class provider implements
|
||||
// Don't belong to the modifier user.
|
||||
|
||||
// Remove data from role_assignments.
|
||||
if (empty($context)) {
|
||||
return;
|
||||
}
|
||||
$DB->delete_records('role_assignments', ['contextid' => $context->id]);
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -103,8 +103,7 @@ if ($action === 'add' and $resettype !== 'none') {
|
||||
'contextlevels' => 1,
|
||||
'allowassign' => 1,
|
||||
'allowoverride' => 1,
|
||||
'allowswitch' => 1,
|
||||
'allowview' => 1);
|
||||
'allowswitch' => 1);
|
||||
if ($showadvanced) {
|
||||
$definitiontable = new core_role_define_role_table_advanced($systemcontext, 0);
|
||||
} else {
|
||||
@@ -151,8 +150,7 @@ if ($action === 'add' and $resettype !== 'none') {
|
||||
'contextlevels' => $data->contextlevels,
|
||||
'allowassign' => $data->allowassign,
|
||||
'allowoverride' => $data->allowoverride,
|
||||
'allowswitch' => $data->allowswitch,
|
||||
'allowview' => $data->allowview);
|
||||
'allowswitch' => $data->allowswitch);
|
||||
if ($showadvanced) {
|
||||
$definitiontable = new core_role_define_role_table_advanced($systemcontext, $roleid);
|
||||
} else {
|
||||
|
||||
@@ -29,7 +29,6 @@ $toprow[] = new tabobject('manage', new moodle_url('/admin/roles/manage.php'), g
|
||||
$toprow[] = new tabobject('assign', new moodle_url('/admin/roles/allow.php', array('mode'=>'assign')), get_string('allowassign', 'core_role'));
|
||||
$toprow[] = new tabobject('override', new moodle_url('/admin/roles/allow.php', array('mode'=>'override')), get_string('allowoverride', 'core_role'));
|
||||
$toprow[] = new tabobject('switch', new moodle_url('/admin/roles/allow.php', array('mode'=>'switch')), get_string('allowswitch', 'core_role'));
|
||||
$toprow[] = new tabobject('view', new moodle_url('/admin/roles/allow.php', ['mode' => 'view']), get_string('allowview', 'core_role'));
|
||||
|
||||
echo $OUTPUT->tabtree($toprow, $currenttab);
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
<xs:element ref="allowassign" minOccurs="0"/>
|
||||
<xs:element ref="allowoverride" minOccurs="0"/>
|
||||
<xs:element ref="allowswitch" minOccurs="0"/>
|
||||
<xs:element ref="allowview" minOccurs="0"/>
|
||||
<xs:element ref="permissions" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
@@ -46,13 +45,6 @@
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="allowview">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="unbounded" minOccurs="0" ref="shortname"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="permissions">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
|
||||
@@ -44,7 +44,7 @@ class core_role_preset_testcase extends advanced_testcase {
|
||||
$contextlevels = get_role_contextlevels($role->id);
|
||||
$this->assertEquals(array_values($contextlevels), array_values($info['contextlevels']));
|
||||
|
||||
foreach (array('assign', 'override', 'switch', 'view') as $type) {
|
||||
foreach (array('assign', 'override', 'switch') as $type) {
|
||||
$records = $DB->get_records('role_allow_'.$type, array('roleid'=>$role->id), "allow$type ASC");
|
||||
$allows = array();
|
||||
foreach ($records as $record) {
|
||||
|
||||
+12
-60
@@ -35,7 +35,10 @@ try {
|
||||
// Continue, we return an error later depending on the requested action.
|
||||
}
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
if ($action) {
|
||||
require_sesskey();
|
||||
|
||||
if ($areaid) {
|
||||
// We need to check that the area exists.
|
||||
@@ -45,76 +48,45 @@ if ($action) {
|
||||
}
|
||||
}
|
||||
|
||||
// All actions but enable/disable need the search engine to be ready.
|
||||
if ($action !== 'enable' && $action !== 'disable') {
|
||||
// All actions but enable/disable need the search engine to be ready.
|
||||
if (!empty($searchmanagererror)) {
|
||||
throw $searchmanagererror;
|
||||
}
|
||||
|
||||
// Show confirm prompt for all these actions as they may be inadvisable, or may cause
|
||||
// an interruption in search functionality, on production systems.
|
||||
if (!optional_param('confirm', 0, PARAM_INT)) {
|
||||
// Display confirmation prompt.
|
||||
$a = null;
|
||||
if ($areaid) {
|
||||
$a = html_writer::tag('strong', $area->get_visible_name());
|
||||
}
|
||||
|
||||
$actionparams = ['sesskey' => sesskey(), 'action' => $action, 'confirm' => 1];
|
||||
if ($areaid) {
|
||||
$actionparams['areaid'] = $areaid;
|
||||
}
|
||||
$actionurl = new moodle_url('/admin/searchareas.php', $actionparams);
|
||||
$cancelurl = new moodle_url('/admin/searchareas.php');
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->confirm(get_string('confirm_' . $action, 'search', $a),
|
||||
new single_button($actionurl, get_string('continue'), 'post', true),
|
||||
new single_button($cancelurl, get_string('cancel'), 'get'));
|
||||
echo $OUTPUT->footer();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// We are now taking an actual action, so require sesskey.
|
||||
require_sesskey();
|
||||
|
||||
switch ($action) {
|
||||
case 'enable':
|
||||
$area->set_enabled(true);
|
||||
\core\notification::add(get_string('searchareaenabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
echo $OUTPUT->notification(get_string('searchareaenabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
break;
|
||||
case 'disable':
|
||||
$area->set_enabled(false);
|
||||
\core\notification::add(get_string('searchareadisabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
echo $OUTPUT->notification(get_string('searchareadisabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
break;
|
||||
case 'delete':
|
||||
$search = \core_search\manager::instance();
|
||||
$search->delete_index($areaid);
|
||||
\core\notification::add(get_string('searchindexdeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
echo $OUTPUT->notification(get_string('searchindexdeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
break;
|
||||
case 'indexall':
|
||||
$searchmanager->index();
|
||||
\core\notification::add(get_string('searchindexupdated', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
echo $OUTPUT->notification(get_string('searchindexupdated', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
break;
|
||||
case 'reindexall':
|
||||
$searchmanager->index(true);
|
||||
\core\notification::add(get_string('searchreindexed', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
echo $OUTPUT->notification(get_string('searchreindexed', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
break;
|
||||
case 'deleteall':
|
||||
$searchmanager->delete_index();
|
||||
\core\notification::add(get_string('searchalldeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
echo $OUTPUT->notification(get_string('searchalldeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
|
||||
break;
|
||||
default:
|
||||
throw new moodle_exception('invalidaction');
|
||||
break;
|
||||
}
|
||||
|
||||
// Redirect back to the main page after taking action.
|
||||
redirect(new moodle_url('/admin/searchareas.php'));
|
||||
}
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
$searchareas = \core_search\manager::get_search_areas_list();
|
||||
if (empty($searchmanagererror)) {
|
||||
$areasconfig = $searchmanager->get_areas_config($searchareas);
|
||||
@@ -160,17 +132,7 @@ foreach ($searchareas as $area) {
|
||||
$laststatus = '';
|
||||
}
|
||||
$columns[] = $laststatus;
|
||||
$accesshide = html_writer::span($area->get_visible_name(), 'accesshide');
|
||||
$actions = [];
|
||||
$actions[] = $OUTPUT->pix_icon('t/delete', '') .
|
||||
html_writer::link(admin_searcharea_action_url('delete', $areaid),
|
||||
get_string('deleteindex', 'search', $accesshide));
|
||||
if ($area->supports_get_document_recordset()) {
|
||||
$actions[] = $OUTPUT->pix_icon('i/reload', '') . html_writer::link(
|
||||
new moodle_url('searchreindex.php', ['areaid' => $areaid]),
|
||||
get_string('gradualreindex', 'search', $accesshide));
|
||||
}
|
||||
$columns[] = html_writer::alist($actions, ['class' => 'unstyled list-unstyled']);
|
||||
$columns[] = html_writer::link(admin_searcharea_action_url('delete', $areaid), 'Delete index');
|
||||
|
||||
} else {
|
||||
$blankrow = new html_table_cell(get_string('searchnotavailable', 'admin'));
|
||||
@@ -203,13 +165,6 @@ echo $OUTPUT->single_button(admin_searcharea_action_url('deleteall'), get_string
|
||||
echo $OUTPUT->box_end();
|
||||
|
||||
echo html_writer::table($table);
|
||||
|
||||
if (empty($searchmanagererror)) {
|
||||
// Show information about queued index requests for specific contexts.
|
||||
$searchrenderer = $PAGE->get_renderer('core_search');
|
||||
echo $searchrenderer->render_index_requests_info($searchmanager->get_index_requests_info());
|
||||
}
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
/**
|
||||
@@ -220,12 +175,9 @@ echo $OUTPUT->footer();
|
||||
* @return moodle_url
|
||||
*/
|
||||
function admin_searcharea_action_url($action, $areaid = false) {
|
||||
$params = array('action' => $action);
|
||||
$params = array('action' => $action, 'sesskey' => sesskey());
|
||||
if ($areaid) {
|
||||
$params['areaid'] = $areaid;
|
||||
}
|
||||
if ($action === 'disable' || $action === 'enable') {
|
||||
$params['sesskey'] = sesskey();
|
||||
}
|
||||
return new moodle_url('/admin/searchareas.php', $params);
|
||||
}
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Adds a search area to the queue for indexing.
|
||||
*
|
||||
* @package core_search
|
||||
* @copyright 2017 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
require(__DIR__ . '/../config.php');
|
||||
|
||||
// Check access.
|
||||
require_once($CFG->libdir . '/adminlib.php');
|
||||
|
||||
admin_externalpage_setup('searchareas', '', null, (new moodle_url('/admin/searchreindex.php'))->out(false));
|
||||
|
||||
// Get area parameter and check it exists.
|
||||
$areaid = required_param('areaid', PARAM_ALPHAEXT);
|
||||
$area = \core_search\manager::get_search_area($areaid);
|
||||
if ($area === false) {
|
||||
throw new moodle_exception('invalidrequest');
|
||||
}
|
||||
$areaname = $area->get_visible_name();
|
||||
|
||||
// Start page output.
|
||||
$heading = get_string('gradualreindex', 'search', '');
|
||||
$PAGE->set_title($PAGE->title . ': ' . $heading);
|
||||
$PAGE->navbar->add($heading);
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading($heading);
|
||||
|
||||
// If sesskey is supplied, actually carry out the action.
|
||||
if (optional_param('sesskey', '', PARAM_ALPHANUM)) {
|
||||
require_sesskey();
|
||||
|
||||
// Get all contexts for search area. This query can take time in large cases.
|
||||
\core_php_time_limit::raise(0);
|
||||
$contextiterator = $area->get_contexts_to_reindex();
|
||||
|
||||
$progress = new \core\progress\display_if_slow('');
|
||||
$progress->start_progress($areaname);
|
||||
|
||||
// Request reindexing for each context (with low priority).
|
||||
$count = 0;
|
||||
foreach ($contextiterator as $context) {
|
||||
\core_php_time_limit::raise(30);
|
||||
\core_search\manager::request_index($context, $area->get_area_id(),
|
||||
\core_search\manager::INDEX_PRIORITY_REINDEXING);
|
||||
$progress->progress();
|
||||
$count++;
|
||||
}
|
||||
|
||||
// Unset the iterator which should close the recordset (if there is one).
|
||||
unset($contextiterator);
|
||||
|
||||
$progress->end_progress();
|
||||
|
||||
$a = (object)['name' => html_writer::tag('strong', $areaname), 'count' => $count];
|
||||
echo $OUTPUT->box(get_string('gradualreindex_queued', 'search', $a));
|
||||
|
||||
echo $OUTPUT->continue_button(new moodle_url('/admin/searchareas.php'));
|
||||
} else {
|
||||
// Display confirmation prompt.
|
||||
echo $OUTPUT->confirm(get_string('gradualreindex_confirm', 'search', html_writer::tag('strong', $areaname)),
|
||||
new single_button(new moodle_url('/admin/searchreindex.php', ['areaid' => $areaid,
|
||||
'sesskey' => sesskey()]), get_string('continue'), 'post', true),
|
||||
new single_button(new moodle_url('/admin/searchareas.php'), get_string('cancel'), 'get'));
|
||||
}
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
@@ -22,7 +22,6 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) { // sp
|
||||
$temp->add(new admin_setting_configcheckbox('allowuserthemes', new lang_string('allowuserthemes', 'admin'), new lang_string('configallowuserthemes', 'admin'), 0));
|
||||
$temp->add(new admin_setting_configcheckbox('allowcoursethemes', new lang_string('allowcoursethemes', 'admin'), new lang_string('configallowcoursethemes', 'admin'), 0));
|
||||
$temp->add(new admin_setting_configcheckbox('allowcategorythemes', new lang_string('allowcategorythemes', 'admin'), new lang_string('configallowcategorythemes', 'admin'), 0));
|
||||
$temp->add(new admin_setting_configcheckbox('allowcohortthemes', new lang_string('allowcohortthemes', 'admin'), new lang_string('configallowcohortthemes', 'admin'), 0));
|
||||
$temp->add(new admin_setting_configcheckbox('allowthemechangeonurl', new lang_string('allowthemechangeonurl', 'admin'), new lang_string('configallowthemechangeonurl', 'admin'), 0));
|
||||
$temp->add(new admin_setting_configcheckbox('allowuserblockhiding', new lang_string('allowuserblockhiding', 'admin'), new lang_string('configallowuserblockhiding', 'admin'), 1));
|
||||
$temp->add(new admin_setting_configcheckbox('allowblockstodock', new lang_string('allowblockstodock', 'admin'), new lang_string('configallowblockstodock', 'admin'), 1));
|
||||
|
||||
@@ -14,11 +14,6 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
|
||||
|
||||
$temp->add(new admin_setting_configcheckbox('dndallowtextandlinks', new lang_string('dndallowtextandlinks', 'admin'), new lang_string('configdndallowtextandlinks', 'admin'), 0));
|
||||
|
||||
$temp->add(new admin_setting_configexecutable('pathtosassc', new lang_string('pathtosassc', 'admin'), new lang_string('pathtosassc_help', 'admin'), ''));
|
||||
|
||||
$temp->add(new admin_setting_configcheckbox('forceclean', new lang_string('forceclean', 'core_admin'),
|
||||
new lang_string('forceclean_desc', 'core_admin'), 0));
|
||||
|
||||
$ADMIN->add('experimental', $temp);
|
||||
|
||||
// "debugging" settingpage
|
||||
|
||||
@@ -42,9 +42,6 @@ if (has_capability('moodle/grade:manage', $systemcontext)
|
||||
// enable publishing in exports/imports
|
||||
$temp->add(new admin_setting_configcheckbox('gradepublishing', new lang_string('gradepublishing', 'grades'), new lang_string('gradepublishing_help', 'grades'), 0));
|
||||
|
||||
$temp->add(new admin_setting_configcheckbox('grade_export_exportfeedback', new lang_string('exportfeedback', 'grades'),
|
||||
new lang_string('exportfeedback_desc', 'grades'), 0));
|
||||
|
||||
$temp->add(new admin_setting_configselect('grade_export_displaytype', new lang_string('gradeexportdisplaytype', 'grades'),
|
||||
new lang_string('gradeexportdisplaytype_desc', 'grades'), GRADE_DISPLAY_TYPE_REAL, $display_types));
|
||||
|
||||
|
||||
@@ -556,7 +556,7 @@ if ($hassiteconfig) {
|
||||
// Search engine selection.
|
||||
$temp->add(new admin_setting_heading('searchengineheading', new lang_string('searchengine', 'admin'), ''));
|
||||
$temp->add(new admin_setting_configselect('searchengine',
|
||||
new lang_string('selectsearchengine', 'admin'), '', 'simpledb', $engines));
|
||||
new lang_string('selectsearchengine', 'admin'), '', 'solr', $engines));
|
||||
$temp->add(new admin_setting_heading('searchoptionsheading', new lang_string('searchoptions', 'admin'), ''));
|
||||
$temp->add(new admin_setting_configcheckbox('searchindexwhendisabled',
|
||||
new lang_string('searchindexwhendisabled', 'admin'), new lang_string('searchindexwhendisabled_desc', 'admin'),
|
||||
|
||||
@@ -29,7 +29,7 @@ if ($hassiteconfig
|
||||
$choices['1'] = new lang_string('emaildisplayyes');
|
||||
$choices['2'] = new lang_string('emaildisplaycourse');
|
||||
$temp->add(new admin_setting_configselect('defaultpreference_maildisplay', new lang_string('emaildisplay'),
|
||||
new lang_string('emaildisplay_help'), 2, $choices));
|
||||
'', 2, $choices));
|
||||
|
||||
$choices = array();
|
||||
$choices['0'] = new lang_string('textformat');
|
||||
@@ -151,7 +151,6 @@ if ($hassiteconfig
|
||||
$temp->add(new admin_setting_configmultiselect('hiddenuserfields', new lang_string('hiddenuserfields', 'admin'),
|
||||
new lang_string('confighiddenuserfields', 'admin'), array(),
|
||||
array('description' => new lang_string('description'),
|
||||
'email' => new lang_string('email'),
|
||||
'city' => new lang_string('city'),
|
||||
'country' => new lang_string('country'),
|
||||
'timezone' => new lang_string('timezone'),
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
@core @core_admin
|
||||
Feature: Use the search areas admin screen
|
||||
In order to control search indexing
|
||||
As an admin
|
||||
I need to use the search areas admin screen
|
||||
|
||||
Background:
|
||||
Given I log in as "admin"
|
||||
And I navigate to "Plugins > Search > Search areas" in site administration
|
||||
|
||||
Scenario: Disable and enable a search area
|
||||
When I click on "Disable" "link" in the "Book - resource information" "table_row"
|
||||
Then I should see "Search area disabled" in the ".alert-success" "css_element"
|
||||
And I should see "Search area disabled" in the "Book - resource information" "table_row"
|
||||
|
||||
When I click on "Enable" "link" in the "Book - resource information" "table_row"
|
||||
Then I should see "Search area enabled" in the ".alert-success" "css_element"
|
||||
And I should not see "Search area disabled" in the "Book - resource information" "table_row"
|
||||
|
||||
# Note: Other scenarios are not currently easy to implement in Behat because there is no mock
|
||||
# search engine - we could add testing once Moodle has an internal database search engine.
|
||||
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Helper class that contains helper functions for cli scripts.
|
||||
*
|
||||
* @package tool_analytics
|
||||
* @copyright 2017 onwards Ankit Agarwal
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_analytics;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Helper class that contains helper functions for cli scripts.
|
||||
*
|
||||
* @package tool_analytics
|
||||
* @copyright 2017 onwards Ankit Agarwal
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class clihelper {
|
||||
|
||||
/**
|
||||
* List all models in the system. To be used from cli scripts.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function list_models() {
|
||||
cli_heading("List of models");
|
||||
echo str_pad(get_string('modelid', 'tool_analytics'), 15, ' ') . ' ' . str_pad(get_string('name'), 50, ' ') .
|
||||
' ' . str_pad(get_string('status'), 15, ' ') . "\n";
|
||||
$models = \core_analytics\manager::get_all_models();
|
||||
foreach ($models as $model) {
|
||||
$modelid = $model->get_id();
|
||||
$isenabled = $model->is_enabled() ? get_string('enabled', 'tool_analytics') : get_string('disabled', 'tool_analytics');
|
||||
$name = $model->get_target()->get_name();
|
||||
echo str_pad($modelid, 15, ' ') . ' ' . str_pad($name, 50, ' ') . ' ' . str_pad($isenabled, 15, ' ') . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,6 @@ $help = "Enables the provided model.
|
||||
|
||||
Options:
|
||||
--modelid Model id
|
||||
--list List models
|
||||
--timesplitting Time splitting method full class name
|
||||
-h, --help Print out this help
|
||||
|
||||
@@ -43,7 +42,6 @@ Example:
|
||||
list($options, $unrecognized) = cli_get_params(
|
||||
array(
|
||||
'help' => false,
|
||||
'list' => false,
|
||||
'modelid' => false,
|
||||
'timesplitting' => false
|
||||
),
|
||||
@@ -57,12 +55,7 @@ if ($options['help']) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($options['list'] || $options['modelid'] === false) {
|
||||
\tool_analytics\clihelper::list_models();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($options['timesplitting'] === false) {
|
||||
if ($options['modelid'] === false || $options['timesplitting'] === false) {
|
||||
echo $help;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ $help = "Evaluates the provided model.
|
||||
|
||||
Options:
|
||||
--modelid Model id
|
||||
--list List models
|
||||
--non-interactive Not interactive questions
|
||||
--timesplitting Restrict the evaluation to 1 single time splitting method (Optional)
|
||||
--filter Analyser dependant. e.g. A courseid would evaluate the model using a single course (Optional)
|
||||
@@ -48,7 +47,6 @@ list($options, $unrecognized) = cli_get_params(
|
||||
array(
|
||||
'help' => false,
|
||||
'modelid' => false,
|
||||
'list' => false,
|
||||
'timesplitting' => false,
|
||||
'reuse-prev-analysed' => true,
|
||||
'non-interactive' => false,
|
||||
@@ -64,8 +62,8 @@ if ($options['help']) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($options['list'] || $options['modelid'] === false) {
|
||||
\tool_analytics\clihelper::list_models();
|
||||
if ($options['modelid'] === false) {
|
||||
echo $help;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ $string['clearmodelpredictions'] = 'Are you sure you want to clear all "{$a}" pr
|
||||
$string['clienablemodel'] = 'You can enable the model by selecting a time-splitting method by its ID. Note that you can also enable it later using the web interface (\'none\' to exit).';
|
||||
$string['clievaluationandpredictions'] = 'A scheduled task iterates through enabled models and gets predictions. Models evaluation via the web interface is disabled. You can allow these processes to be executed manually via the web interface by disabling the <a href="{$a}">\'onlycli\'</a> analytics setting.';
|
||||
$string['clievaluationandpredictionsnoadmin'] = 'A scheduled task iterates through enabled models and gets predictions. Models evaluation via the web interface is disabled. It may be enabled by a site administrator.';
|
||||
$string['disabled'] = 'Disabled';
|
||||
$string['editmodel'] = 'Edit "{$a}" model';
|
||||
$string['edittrainedwarning'] = 'This model has already been trained. Note that changing its indicators or its time-splitting method will delete its previous predictions and start generating new predictions.';
|
||||
$string['enabled'] = 'Enabled';
|
||||
@@ -67,7 +66,6 @@ $string['invalidanalysablestable'] = 'Invalid site analysable elements table';
|
||||
$string['invalidprediction'] = 'Invalid to get predictions';
|
||||
$string['invalidtraining'] = 'Invalid to train the model';
|
||||
$string['loginfo'] = 'Log extra info';
|
||||
$string['modelid'] = 'Model ID';
|
||||
$string['modelinvalidanalysables'] = 'Invalid analysable elements for "{$a}" model';
|
||||
$string['modelresults'] = '{$a} results';
|
||||
$string['modeltimesplitting'] = 'Time splitting';
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2018050800; // Requires this Moodle version.
|
||||
$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2017110800; // Requires this Moodle version.
|
||||
$plugin->component = 'tool_analytics'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018051400;
|
||||
$plugin->requires = 2018050800;
|
||||
$plugin->version = 2017111300;
|
||||
$plugin->requires = 2017110800;
|
||||
$plugin->component = 'tool_assignmentupgrade';
|
||||
$plugin->dependencies = array('mod_assign' => 2018050800);
|
||||
$plugin->dependencies = array('mod_assign' => 2017110800);
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018051400;
|
||||
$plugin->requires = 2018050800;
|
||||
$plugin->version = 2017111300;
|
||||
$plugin->requires = 2017110800;
|
||||
$plugin->component = 'tool_availabilityconditions';
|
||||
|
||||
@@ -139,7 +139,7 @@ class tool_behat_renderer extends plugin_renderer_base {
|
||||
$msg = get_string('wrongbehatsetup', 'tool_behat', $a);
|
||||
|
||||
// Error box including generic error string + specific error msg.
|
||||
$html .= $this->output->box_start('box errorbox alert alert-danger');
|
||||
$html .= $this->output->box_start('box errorbox');
|
||||
$html .= html_writer::tag('div', $msg);
|
||||
$html .= $this->output->box_end();
|
||||
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2018050800; // Requires this Moodle version
|
||||
$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2017110800; // Requires this Moodle version
|
||||
$plugin->component = 'tool_behat'; // Full name of the plugin (used for diagnostics)
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2018050800; // Requires this Moodle version.
|
||||
$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2017110800; // Requires this Moodle version.
|
||||
$plugin->component = 'tool_capability'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -94,8 +94,7 @@ class cohort_role_assignments_table extends table_sql {
|
||||
'idnumber' => $data->cohortidnumber,
|
||||
'description' => $data->cohortdescription,
|
||||
'visible' => $data->cohortvisible,
|
||||
'name' => $data->cohortname,
|
||||
'theme' => $data->cohorttheme
|
||||
'name' => $data->cohortname
|
||||
);
|
||||
$context = context_helper::instance_by_id($data->cohortcontextid);
|
||||
|
||||
@@ -170,7 +169,7 @@ class cohort_role_assignments_table extends table_sql {
|
||||
protected function get_sql_and_params($count = false) {
|
||||
$fields = 'uca.id, uca.cohortid, uca.userid, uca.roleid, ';
|
||||
$fields .= 'c.name as cohortname, c.idnumber as cohortidnumber, c.contextid as cohortcontextid, ';
|
||||
$fields .= 'c.visible as cohortvisible, c.description as cohortdescription, c.theme as cohorttheme, ';
|
||||
$fields .= 'c.visible as cohortvisible, c.description as cohortdescription, ';
|
||||
|
||||
// Add extra user fields that we need for the graded user.
|
||||
$extrafields = get_extra_user_fields($this->context);
|
||||
|
||||
@@ -31,15 +31,13 @@
|
||||
* idnumber cohort idnumber field
|
||||
* description cohort description field
|
||||
* visible cohort visible field
|
||||
* theme cohort theme field
|
||||
|
||||
Example context (json):
|
||||
{ "id": "1",
|
||||
"name": "Cohort 1",
|
||||
"visible": true,
|
||||
"idnumber": "014",
|
||||
"description": "Some users",
|
||||
"theme": "clean"
|
||||
"description": "Some users"
|
||||
}
|
||||
}}
|
||||
{{> tool_lp/form-cohort-selector-suggestion }}
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
$plugin->version = 2018051400; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2018050800; // Requires this Moodle version.
|
||||
$plugin->version = 2017111300; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2017110800; // Requires this Moodle version.
|
||||
$plugin->component = 'tool_cohortroles'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
$plugin->dependencies = array(
|
||||
|
||||
@@ -29,6 +29,9 @@ defined('MOODLE_INTERNAL') || die();
|
||||
function xmldb_tool_customlang_upgrade($oldversion) {
|
||||
global $CFG;
|
||||
|
||||
// Moodle v3.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
|
||||
@@ -25,6 +25,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018051400;
|
||||
$plugin->requires = 2018050800;
|
||||
$plugin->version = 2017111300;
|
||||
$plugin->requires = 2017110800;
|
||||
$plugin->component = 'tool_customlang'; // Full name of the plugin (used for diagnostics)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
define(["jquery","core/str","core/ajax","core/notification","core/modal_factory","core/modal_events","core/fragment"],function(a,b,c,d,e,f,g){var h={CATEGORY_LINK:'[data-add-element="category"]'},i=function(a){this.contextId=a;var c=[{key:"addcategory",component:"tool_dataprivacy"},{key:"save",component:"admin"}];this.strings=b.get_strings(c),this.registerEventListeners()};return i.prototype.contextId=0,i.prototype.strings=0,i.prototype.registerEventListeners=function(){var b=a(h.CATEGORY_LINK);b.on("click",function(){return this.strings.then(function(a){e.create({type:e.types.SAVE_CANCEL,title:a[0],body:""},b).done(function(b){this.setupFormModal(b,a[1])}.bind(this))}.bind(this)).fail(d.exception)}.bind(this))},i.prototype.getBody=function(a){var b=null;return"undefined"!=typeof a&&(b={jsonformdata:JSON.stringify(a)}),g.loadFragment("tool_dataprivacy","addcategory_form",this.contextId,b)},i.prototype.setupFormModal=function(a,b){a.setLarge(),a.setSaveButtonText(b),a.getRoot().on(f.hidden,this.destroy.bind(this)),a.setBody(this.getBody()),a.getRoot().on(f.save,this.submitForm.bind(this)),a.getRoot().on("submit","form",this.submitFormAjax.bind(this)),this.modal=a,a.show()},i.prototype.submitForm=function(a){a.preventDefault(),this.modal.getRoot().find("form").submit()},i.prototype.submitFormAjax=function(a){a.preventDefault();var b=this.modal.getRoot().find("form").serialize();c.call([{methodname:"tool_dataprivacy_create_category_form",args:{jsonformdata:JSON.stringify(b)},done:function(a){a.validationerrors?this.modal.setBody(this.getBody(b)):this.close()}.bind(this),fail:d.exception}])},i.prototype.close=function(){this.destroy(),document.location.reload()},i.prototype.destroy=function(){Y.use("moodle-core-formchangechecker",function(){M.core_formchangechecker.reset_form_dirty_state()}),this.modal.destroy()},i.prototype.removeListeners=function(){a(h.CATEGORY_LINK).off("click")},{getInstance:function(a){return new i(a)}}});
|
||||
@@ -1 +0,0 @@
|
||||
define(["jquery","core/str","core/ajax","core/notification","core/modal_factory","core/modal_events","core/fragment"],function(a,b,c,d,e,f,g){var h={PURPOSE_LINK:'[data-add-element="purpose"]'},i=function(a){this.contextId=a;var c=[{key:"addpurpose",component:"tool_dataprivacy"},{key:"save",component:"admin"}];this.strings=b.get_strings(c),this.registerEventListeners()};return i.prototype.contextId=0,i.prototype.strings=0,i.prototype.registerEventListeners=function(){var b=a(h.PURPOSE_LINK);b.on("click",function(){return this.strings.then(function(a){e.create({type:e.types.SAVE_CANCEL,title:a[0],body:""},b).done(function(b){this.setupFormModal(b,a[1])}.bind(this))}.bind(this)).fail(d.exception)}.bind(this))},i.prototype.getBody=function(a){var b=null;return"undefined"!=typeof a&&(b={jsonformdata:JSON.stringify(a)}),g.loadFragment("tool_dataprivacy","addpurpose_form",this.contextId,b)},i.prototype.setupFormModal=function(a,b){a.setLarge(),a.setSaveButtonText(b),a.getRoot().on(f.hidden,this.destroy.bind(this)),a.setBody(this.getBody()),a.getRoot().on(f.save,this.submitForm.bind(this)),a.getRoot().on("submit","form",this.submitFormAjax.bind(this)),this.modal=a,a.show()},i.prototype.submitForm=function(a){a.preventDefault(),this.modal.getRoot().find("form").submit()},i.prototype.submitFormAjax=function(a){a.preventDefault();var b=this.modal.getRoot().find("form").serialize();c.call([{methodname:"tool_dataprivacy_create_purpose_form",args:{jsonformdata:JSON.stringify(b)},done:function(a){a.validationerrors?this.modal.setBody(this.getBody(b)):this.close()}.bind(this),fail:d.exception}])},i.prototype.close=function(){this.destroy(),document.location.reload()},i.prototype.destroy=function(){Y.use("moodle-core-formchangechecker",function(){M.core_formchangechecker.reset_form_dirty_state()}),this.modal.destroy()},i.prototype.removeListeners=function(){a(h.PURPOSE_LINK).off("click")},{getInstance:function(a){return new i(a)}}});
|
||||
@@ -1 +0,0 @@
|
||||
define(["jquery","core/ajax","core/notification","core/str","core/modal_factory","core/modal_events"],function(a,b,c,d,e,f){var g={DELETE:'[data-action="deletecategory"]'},h=function(){this.registerEvents()};return h.prototype.registerEvents=function(){a(g.DELETE).click(function(g){g.preventDefault();var h=a(this).data("id"),i=a(this).data("name"),j=[{key:"deletecategory",component:"tool_dataprivacy",param:i},{key:"deletecategorytext",component:"tool_dataprivacy",param:i}];d.get_strings(j).then(function(d){var g=d[0],i=d[1];return e.create({title:g,body:i,type:e.types.SAVE_CANCEL}).then(function(d){return d.setSaveButtonText(g),d.getRoot().on(f.save,function(){var d={methodname:"tool_dataprivacy_delete_category",args:{id:h}};b.call([d])[0].done(function(b){b.result?a('tr[data-categoryid="'+h+'"]').remove():c.addNotification({message:b.warnings[0].message,type:"error"})}).fail(c.exception)}),d.getRoot().on(f.hidden,function(){d.destroy()}),d})}).done(function(a){a.show()}).fail(c.exception)})},{init:function(){return new h}}});
|
||||
@@ -1 +0,0 @@
|
||||
define(["jquery","core/ajax","core/notification","core/str","core/modal_factory","core/modal_events"],function(a,b,c,d,e,f){function g(a){var g=[{key:"confirm",component:"moodle"},{key:"confirmcontextdeletion",component:"tool_dataprivacy"}],h="tool_dataprivacy_confirm_contexts_for_deletion",i="";d.get_strings(g).then(function(a){i=a[0];var b=a[1];return e.create({title:i,body:b,type:e.types.SAVE_CANCEL})}).then(function(d){return d.setSaveButtonText(i),d.getRoot().on(f.save,function(){var d={ids:a},e={methodname:h,args:d};b.call([e])[0].done(function(a){a.result?window.location.reload():c.addNotification({message:a.warnings[0].message,type:"error"})}).fail(c.exception)}),d.getRoot().on(f.hidden,function(){d.destroy()}),d}).done(function(a){a.show()}).fail(c.exception)}var h={MARK_FOR_DELETION:'[data-action="markfordeletion"]',SELECT_ALL:'[data-action="selectall"]'},i={SELECTCONTEXT:".selectcontext"},j=function(){this.registerEvents()};return j.prototype.registerEvents=function(){a(h.MARK_FOR_DELETION).click(function(b){b.preventDefault();var c=[];a(i.SELECTCONTEXT).each(function(){var b=a(this);b.is(":checked")&&c.push(b.val())}),g(c)}),a(h.SELECT_ALL).change(function(b){b.preventDefault();var c=a(this);c.is(":checked")?a(i.SELECTCONTEXT).attr("checked","checked"):a(i.SELECTCONTEXT).removeAttr("checked")})},j});
|
||||
@@ -1 +0,0 @@
|
||||
define(["jquery","core/str","core/ajax","core/notification","core/templates","core/modal_factory","core/modal_events","core/fragment","tool_dataprivacy/add_purpose","tool_dataprivacy/add_category"],function(a,b,c,d,e,f,g,h,i,j){var k={TREE_NODES:"[data-context-tree-node=1]",FORM_CONTAINER:"#context-form-container"},l=function(a,b,c){this.systemContextId=a,this.currentContextLevel=b,this.currentContextId=c,this.init()};return l.prototype.systemContextId=0,l.prototype.currentContextLevel=0,l.prototype.currentContextId=0,l.prototype.addpurpose=null,l.prototype.addcategory=null,l.prototype.init=function(){this.addpurpose=i.getInstance(this.systemContextId),this.addcategory=j.getInstance(this.systemContextId);var a=[{key:"changessaved",component:"moodle"},{key:"contextpurposecategorysaved",component:"tool_dataprivacy"},{key:"noblockstoload",component:"tool_dataprivacy"},{key:"noactivitiestoload",component:"tool_dataprivacy"},{key:"nocoursestoload",component:"tool_dataprivacy"}];this.strings=b.get_strings(a),this.registerEventListeners(),this.currentContextId?this.loadForm("context_form",[this.currentContextId],this.submitContextFormAjax.bind(this)):this.loadForm("contextlevel_form",[this.currentContextLevel],this.submitContextLevelFormAjax.bind(this))},l.prototype.registerEventListeners=function(){a(k.TREE_NODES).on("click",function(b){b.preventDefault();var c=a(b.currentTarget);a(k.TREE_NODES).removeClass("active"),c.addClass("active");var d=c.data("contextlevel"),e=c.data("contextid");if(d)window.history.pushState({},null,"?contextlevel="+d),this.addpurpose.removeListeners(),this.addcategory.removeListeners(),this.currentContextLevel=d,this.loadForm("contextlevel_form",[this.currentContextLevel],this.submitContextLevelFormAjax.bind(this));else if(e)window.history.pushState({},null,"?contextid="+e),this.addpurpose.removeListeners(),this.addcategory.removeListeners(),this.currentContextId=e,this.loadForm("context_form",[this.currentContextId],this.submitContextFormAjax.bind(this));else{var f=c.data("expandcontextid"),g=c.data("expandelement"),h=c.data("expanded");g&&(h?this.collapse(c):!c.data("loaded")&&f&&g?(c.find("> i").removeClass("fa-plus"),c.find("> i").addClass("fa-circle-o-notch fa-spin"),this.loadExtra(c,f,g)):this.expand(c))}}.bind(this))},l.prototype.removeListeners=function(){a(k.TREE_NODES).off("click")},l.prototype.loadForm=function(b,c,f){this.clearForm();var g=h.loadFragment("tool_dataprivacy",b,this.systemContextId,c);g.done(function(b,c){a(k.FORM_CONTAINER).html(b),e.runTemplateJS(c),this.addpurpose.registerEventListeners(),this.addcategory.registerEventListeners(),a(k.FORM_CONTAINER).on("submit","form",f)}.bind(this)).fail(d.exception)},l.prototype.clearForm=function(){Y.use("moodle-core-formchangechecker",function(){M.core_formchangechecker.reset_form_dirty_state()}),a(k.FORM_CONTAINER).off("submit","form")},l.prototype.submitForm=function(b){b.preventDefault(),a(k.FORM_CONTAINER).find("form").submit()},l.prototype.submitContextLevelFormAjax=function(a){this.submitFormAjax(a,"tool_dataprivacy_set_contextlevel_form")},l.prototype.submitContextFormAjax=function(a){this.submitFormAjax(a,"tool_dataprivacy_set_context_form")},l.prototype.submitFormAjax=function(b,e){b.preventDefault();var f=a(k.FORM_CONTAINER).find("form").serialize();return this.strings.then(function(a){c.call([{methodname:e,args:{jsonformdata:JSON.stringify(f)},done:function(){d.alert(a[0],a[1])},fail:d.exception}])})["catch"](d.exception)},l.prototype.loadExtra=function(a,b,f){c.call([{methodname:"tool_dataprivacy_tree_extra_branches",args:{contextid:b,element:f},done:function(b){return 0==b.branches.length?void this.noElements(a,f):void e.render("tool_dataprivacy/context_tree_branches",b).then(function(b){a.after(b),this.removeListeners(),this.registerEventListeners(),this.expand(a),a.data("loaded",1)}.bind(this)).fail(d.exception)}.bind(this),fail:d.exception}])},l.prototype.noElements=function(a,b){a.data("expandcontextid",""),a.data("expandelement",""),this.strings.then(function(c){var d=2;"module"==b?d=3:"course"==b&&(d=4),a.text(c[d])}).fail(d.exception)},l.prototype.collapse=function(a){a.data("expanded",0),a.siblings("nav").addClass("hidden"),a.find("> i").removeClass("fa-minus"),a.find("> i").addClass("fa-plus")},l.prototype.expand=function(a){a.data("expanded",1),a.siblings("nav").removeClass("hidden"),a.find("> i").removeClass("fa-plus"),a.find("> i").removeClass("fa-circle-o-notch fa-spin"),a.find("> i").addClass("fa-minus")},{init:function(a,b,c){return new l(a,b,c)}}});
|
||||
@@ -1 +0,0 @@
|
||||
define(["jquery","core/notification","core/custom_interaction_events","core/modal","core/modal_registry","tool_dataprivacy/events"],function(a,b,c,d,e,f){var g=!1,h={APPROVE_BUTTON:'[data-action="approve"]',DENY_BUTTON:'[data-action="deny"]'},i=function(a){d.call(this,a),this.getFooter().find(h.APPROVE_BUTTON).length||b.exception({message:"No approve button found"}),this.getFooter().find(h.DENY_BUTTON).length||b.exception({message:"No deny button found"})};return i.TYPE="tool_dataprivacy-data_request",i.prototype=Object.create(d.prototype),i.prototype.constructor=i,i.prototype.registerEventListeners=function(){d.prototype.registerEventListeners.call(this),this.getModal().on(c.events.activate,h.APPROVE_BUTTON,function(b,c){var d=a.Event(f.approve);this.getRoot().trigger(d,this),d.isDefaultPrevented()||(this.hide(),c.originalEvent.preventDefault())}.bind(this)),this.getModal().on(c.events.activate,h.DENY_BUTTON,function(b,c){var d=a.Event(f.deny);this.getRoot().trigger(d,this),d.isDefaultPrevented()||(this.hide(),c.originalEvent.preventDefault())}.bind(this))},g||(e.register(i.TYPE,i,"tool_dataprivacy/data_request_modal"),g=!0),i});
|
||||
@@ -1 +0,0 @@
|
||||
define(["jquery"],function(a){var b={PURPOSE_SELECT:"#id_purposeid",RETENTION_FIELD_BOOST:"#id_error_retention_current",RETENTION_FIELD_CLEAN:"#fitem_id_retention_current [data-fieldtype=static]"},c=function(a){this.purposeRetentionPeriods=a,this.registerEventListeners()},d=function(){a(b.PURPOSE_SELECT).off("change")};return c.prototype.purposeRetentionPeriods=[],c.prototype.registerEventListeners=function(){a(b.PURPOSE_SELECT).on("change",function(c){var d=a(c.currentTarget).val(),e=this.purposeRetentionPeriods[d],f=a(b.RETENTION_FIELD_CLEAN);if(f.length>0)f.text(e);else{var g=a(b.RETENTION_FIELD_BOOST),h=g.siblings();h.length>0&&h.text(e)}}.bind(this))},{init:function(a){return d(),new c(a)}}});
|
||||
@@ -1 +0,0 @@
|
||||
define([],function(){return{approve:"tool_dataprivacy-data_request:approve",deny:"tool_dataprivacy-data_request:deny"}});
|
||||
@@ -1 +0,0 @@
|
||||
define(["jquery","core/url","core/str"],function(a,b,c){var d=a('<img alt="" src="'+b.imageUrl("t/expanded")+'"/>'),e=a('<img alt="" src="'+b.imageUrl("t/collapsed")+'"/>');return{expandCollapse:function(a,b){a.hasClass("hide")?(a.removeClass("hide"),a.addClass("visible"),a.attr("aria-expanded",!0),b.find(":header i.fa").removeClass("fa-plus-square"),b.find(":header i.fa").addClass("fa-minus-square"),b.find(":header img.icon").attr("src",d.attr("src"))):(a.removeClass("visible"),a.addClass("hide"),a.attr("aria-expanded",!1),b.find(":header i.fa").removeClass("fa-minus-square"),b.find(":header i.fa").addClass("fa-plus-square"),b.find(":header img.icon").attr("src",e.attr("src")))},expandCollapseAll:function(b){var f="visible"==b?"hide":"visible",g="visible"==b,h="visible"==b?"fa-plus-square":"fa-minus-square",i="visible"==b?"fa-minus-square":"fa-plus-square",j="visible"==b?d.attr("src"):e.attr("src");a("."+f).each(function(){a(this).removeClass(f),a(this).addClass(b),a(this).attr("aria-expanded",g)}),a(".tool_dataprivacy-expand-all").data("visibilityState",f),c.get_string(f,"tool_dataprivacy").then(function(b){a(".tool_dataprivacy-expand-all").html(b)})["catch"](Notification.exception),a(":header i.fa").each(function(){a(this).removeClass(h),a(this).addClass(i)}),a(":header img.icon").each(function(){a(this).attr("src",j)})}}});
|
||||
@@ -1 +0,0 @@
|
||||
define(["jquery","core/ajax","core/templates"],function(a,b,c){return{processResults:function(b,c){var d=[];return a.each(c,function(a,b){d.push({value:b.id,label:b._label})}),d},transport:function(d,e,f,g){var h;h=b.call([{methodname:"tool_dataprivacy_get_users",args:{query:e}}]),h[0].then(function(b){var d=[],e=0;return a.each(b,function(a,b){d.push(c.render("tool_dataprivacy/form-user-selector-suggestion",b))}),a.when.apply(a.when,d).then(function(){var c=arguments;a.each(b,function(a,b){b._label=c[e],e++}),f(b)})}).fail(g)}}});
|
||||
@@ -1 +0,0 @@
|
||||
define(["jquery","core/ajax","core/notification","core/str","core/modal_factory","core/modal_events","core/templates"],function(a,b,c,d,e,f,g){function h(a){var e={methodname:"tool_dataprivacy_contact_dpo",args:{message:a}},f="success";b.call([e])[0].then(function(a){return a.result?d.get_string("requestsubmitted","tool_dataprivacy"):(f="error",a.warnings.join("<br>"))}).done(function(a){c.addNotification({message:a,type:f})}).fail(c.exception)}var i={CANCEL_REQUEST:'[data-action="cancel"]',CONTACT_DPO:'[data-action="contactdpo"]'},j=function(){this.registerEvents()};return j.prototype.registerEvents=function(){a(i.CANCEL_REQUEST).click(function(g){g.preventDefault();var h=a(this).data("requestid"),i=[{key:"cancelrequest",component:"tool_dataprivacy"},{key:"cancelrequestconfirmation",component:"tool_dataprivacy"}];d.get_strings(i).then(function(a){var d=a[0],g=a[1];return e.create({title:d,body:g,type:e.types.SAVE_CANCEL}).then(function(a){return a.setSaveButtonText(d),a.getRoot().on(f.save,function(){var a={requestid:h},d={methodname:"tool_dataprivacy_cancel_data_request",args:a};b.call([d])[0].done(function(a){a.result?window.location.reload():c.addNotification({message:a.warnings[0].message,type:"error"})}).fail(c.exception)}),a.getRoot().on(f.hidden,function(){a.destroy()}),a})}).done(function(a){a.show()}).fail(c.exception)}),a(i.CONTACT_DPO).click(function(b){b.preventDefault();var i=a(this).data("replytoemail"),j=[{key:"contactdataprotectionofficer",component:"tool_dataprivacy"},{key:"send",component:"tool_dataprivacy"}],k="";d.get_strings(j).then(function(a){var b=a[0];k=a[1];var c={replytoemail:i};return e.create({title:b,body:g.render("tool_dataprivacy/contact_dpo",c),type:e.types.SAVE_CANCEL,large:!0})}).done(function(b){b.setSaveButtonText(k),b.getRoot().on(f.save,function(b){var c=a("#message").val().trim();0===c.length?(b.preventDefault(),a('[data-region="messageinput"]').addClass("has-danger notifyproblem"),a("#id_error_message").removeAttr("hidden")):h(c)}),b.getRoot().on(f.hidden,function(){b.destroy()}),b.show()}).fail(c.exception)})},{init:function(){return new j}}});
|
||||
@@ -1 +0,0 @@
|
||||
define(["jquery","core/ajax","core/notification","core/str","core/modal_factory","core/modal_events"],function(a,b,c,d,e,f){var g={DELETE:'[data-action="deletepurpose"]'},h=function(){this.registerEvents()};return h.prototype.registerEvents=function(){a(g.DELETE).click(function(g){g.preventDefault();var h=a(this).data("id"),i=a(this).data("name"),j=[{key:"deletepurpose",component:"tool_dataprivacy",param:i},{key:"deletepurposetext",component:"tool_dataprivacy",param:i}];d.get_strings(j).then(function(d){var g=d[0],i=d[1];return e.create({title:g,body:i,type:e.types.SAVE_CANCEL}).then(function(d){return d.setSaveButtonText(g),d.getRoot().on(f.save,function(){var d={methodname:"tool_dataprivacy_delete_purpose",args:{id:h}};b.call([d])[0].done(function(b){b.result?a('tr[data-purposeid="'+h+'"]').remove():c.addNotification({message:b.warnings[0].message,type:"error"})}).fail(c.exception)}),d.getRoot().on(f.hidden,function(){d.destroy()}),d})}).done(function(a){a.show()}).fail(c.exception)})},{init:function(){return new h}}});
|
||||
@@ -1 +0,0 @@
|
||||
define(["jquery","core/ajax","core/notification","core/str","core/modal_factory","core/modal_events","core/templates","tool_dataprivacy/data_request_modal","tool_dataprivacy/events"],function(a,b,c,d,e,f,g,h,i){function j(a,g){var h=[],j="";switch(a){case i.approve:h=[{key:"approverequest",component:"tool_dataprivacy"},{key:"confirmapproval",component:"tool_dataprivacy"}],j="tool_dataprivacy_approve_data_request";break;case i.deny:h=[{key:"denyrequest",component:"tool_dataprivacy"},{key:"confirmdenial",component:"tool_dataprivacy"}],j="tool_dataprivacy_deny_data_request"}var k="";d.get_strings(h).then(function(a){k=a[0];var b=a[1];return e.create({title:k,body:b,type:e.types.SAVE_CANCEL})}).then(function(a){return a.setSaveButtonText(k),a.getRoot().on(f.save,function(){var a={requestid:g},d={methodname:j,args:a};b.call([d])[0].done(function(a){a.result?window.location.reload():c.addNotification({message:a.warnings[0].message,type:"error"})}).fail(c.exception)}),a.getRoot().on(f.hidden,function(){a.destroy()}),a}).done(function(a){a.show()}).fail(c.exception)}var k={APPROVE_REQUEST:'[data-action="approve"]',DENY_REQUEST:'[data-action="deny"]',VIEW_REQUEST:'[data-action="view"]'},l=function(){this.registerEvents()};return l.prototype.registerEvents=function(){a(k.VIEW_REQUEST).click(function(d){d.preventDefault();var k=a(this).data("requestid"),l={requestid:k},m={methodname:"tool_dataprivacy_get_data_request",args:l},n=b.call([m]),o="",p=e.types.DEFAULT;a.when(n[0]).then(function(a){return a.result?(2==a.result.status&&(p=h.TYPE),o=a.result.typename,g.render("tool_dataprivacy/request_details",a.result)):(c.addNotification({message:a.warnings[0].message,type:"error"}),!1)}).then(function(a){return e.create({title:o,body:a,type:p,large:!0}).then(function(a){return a.getRoot().on(i.approve,function(){j(i.approve,k)}),a.getRoot().on(i.deny,function(){j(i.deny,k)}),a.getRoot().on(f.hidden,function(){a.destroy()}),a})}).done(function(a){a.show()}).fail(c.exception)}),a(k.APPROVE_REQUEST).click(function(b){b.preventDefault();var c=a(this).data("requestid");j(i.approve,c)}),a(k.DENY_REQUEST).click(function(b){b.preventDefault();var c=a(this).data("requestid");j(i.deny,c)})},l});
|
||||
@@ -1,172 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Module to add categories.
|
||||
*
|
||||
* @module tool_dataprivacy/add_category
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/str', 'core/ajax', 'core/notification', 'core/modal_factory', 'core/modal_events', 'core/fragment'],
|
||||
function($, Str, Ajax, Notification, ModalFactory, ModalEvents, Fragment) {
|
||||
|
||||
var SELECTORS = {
|
||||
CATEGORY_LINK: '[data-add-element="category"]',
|
||||
};
|
||||
|
||||
var AddCategory = function(contextId) {
|
||||
this.contextId = contextId;
|
||||
|
||||
var stringKeys = [
|
||||
{
|
||||
key: 'addcategory',
|
||||
component: 'tool_dataprivacy'
|
||||
},
|
||||
{
|
||||
key: 'save',
|
||||
component: 'admin'
|
||||
}
|
||||
];
|
||||
this.strings = Str.get_strings(stringKeys);
|
||||
|
||||
this.registerEventListeners();
|
||||
};
|
||||
|
||||
/**
|
||||
* @var {int} contextId
|
||||
* @private
|
||||
*/
|
||||
AddCategory.prototype.contextId = 0;
|
||||
|
||||
/**
|
||||
* @var {Promise}
|
||||
* @private
|
||||
*/
|
||||
AddCategory.prototype.strings = 0;
|
||||
|
||||
AddCategory.prototype.registerEventListeners = function() {
|
||||
|
||||
var trigger = $(SELECTORS.CATEGORY_LINK);
|
||||
trigger.on('click', function() {
|
||||
return this.strings.then(function(strings) {
|
||||
ModalFactory.create({
|
||||
type: ModalFactory.types.SAVE_CANCEL,
|
||||
title: strings[0],
|
||||
body: '',
|
||||
}, trigger).done(function(modal) {
|
||||
this.setupFormModal(modal, strings[1]);
|
||||
}.bind(this));
|
||||
}.bind(this))
|
||||
.fail(Notification.exception);
|
||||
}.bind(this));
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @method getBody
|
||||
* @param {Object} formdata
|
||||
* @private
|
||||
* @return {Promise}
|
||||
*/
|
||||
AddCategory.prototype.getBody = function(formdata) {
|
||||
|
||||
var params = null;
|
||||
if (typeof formdata !== "undefined") {
|
||||
params = {jsonformdata: JSON.stringify(formdata)};
|
||||
}
|
||||
// Get the content of the modal.
|
||||
return Fragment.loadFragment('tool_dataprivacy', 'addcategory_form', this.contextId, params);
|
||||
};
|
||||
|
||||
AddCategory.prototype.setupFormModal = function(modal, saveText) {
|
||||
modal.setLarge();
|
||||
|
||||
modal.setSaveButtonText(saveText);
|
||||
|
||||
// We want to reset the form every time it is opened.
|
||||
modal.getRoot().on(ModalEvents.hidden, this.destroy.bind(this));
|
||||
|
||||
modal.setBody(this.getBody());
|
||||
|
||||
// We catch the modal save event, and use it to submit the form inside the modal.
|
||||
// Triggering a form submission will give JS validation scripts a chance to check for errors.
|
||||
modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));
|
||||
// We also catch the form submit event and use it to submit the form with ajax.
|
||||
modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));
|
||||
|
||||
this.modal = modal;
|
||||
|
||||
modal.show();
|
||||
};
|
||||
|
||||
/**
|
||||
* This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.
|
||||
*
|
||||
* @method submitForm
|
||||
* @param {Event} e Form submission event.
|
||||
* @private
|
||||
*/
|
||||
AddCategory.prototype.submitForm = function(e) {
|
||||
e.preventDefault();
|
||||
this.modal.getRoot().find('form').submit();
|
||||
};
|
||||
|
||||
AddCategory.prototype.submitFormAjax = function(e) {
|
||||
// We don't want to do a real form submission.
|
||||
e.preventDefault();
|
||||
|
||||
// Convert all the form elements values to a serialised string.
|
||||
var formData = this.modal.getRoot().find('form').serialize();
|
||||
|
||||
Ajax.call([{
|
||||
methodname: 'tool_dataprivacy_create_category_form',
|
||||
args: {jsonformdata: JSON.stringify(formData)},
|
||||
done: function(data) {
|
||||
if (data.validationerrors) {
|
||||
this.modal.setBody(this.getBody(formData));
|
||||
} else {
|
||||
this.close();
|
||||
}
|
||||
}.bind(this),
|
||||
fail: Notification.exception
|
||||
}]);
|
||||
};
|
||||
|
||||
AddCategory.prototype.close = function() {
|
||||
this.destroy();
|
||||
document.location.reload();
|
||||
};
|
||||
|
||||
AddCategory.prototype.destroy = function() {
|
||||
Y.use('moodle-core-formchangechecker', function() {
|
||||
M.core_formchangechecker.reset_form_dirty_state();
|
||||
});
|
||||
this.modal.destroy();
|
||||
};
|
||||
|
||||
AddCategory.prototype.removeListeners = function() {
|
||||
$(SELECTORS.CATEGORY_LINK).off('click');
|
||||
};
|
||||
|
||||
return /** @alias module:tool_dataprivacy/add_category */ {
|
||||
getInstance: function(contextId) {
|
||||
return new AddCategory(contextId);
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Module to add purposes.
|
||||
*
|
||||
* @module tool_dataprivacy/add_purpose
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/str', 'core/ajax', 'core/notification', 'core/modal_factory', 'core/modal_events', 'core/fragment'],
|
||||
function($, Str, Ajax, Notification, ModalFactory, ModalEvents, Fragment) {
|
||||
|
||||
var SELECTORS = {
|
||||
PURPOSE_LINK: '[data-add-element="purpose"]',
|
||||
};
|
||||
|
||||
var AddPurpose = function(contextId) {
|
||||
this.contextId = contextId;
|
||||
|
||||
var stringKeys = [
|
||||
{
|
||||
key: 'addpurpose',
|
||||
component: 'tool_dataprivacy'
|
||||
},
|
||||
{
|
||||
key: 'save',
|
||||
component: 'admin'
|
||||
}
|
||||
];
|
||||
this.strings = Str.get_strings(stringKeys);
|
||||
|
||||
this.registerEventListeners();
|
||||
};
|
||||
|
||||
/**
|
||||
* @var {int} contextId
|
||||
* @private
|
||||
*/
|
||||
AddPurpose.prototype.contextId = 0;
|
||||
|
||||
/**
|
||||
* @var {Promise}
|
||||
* @private
|
||||
*/
|
||||
AddPurpose.prototype.strings = 0;
|
||||
|
||||
AddPurpose.prototype.registerEventListeners = function() {
|
||||
|
||||
var trigger = $(SELECTORS.PURPOSE_LINK);
|
||||
trigger.on('click', function() {
|
||||
return this.strings.then(function(strings) {
|
||||
ModalFactory.create({
|
||||
type: ModalFactory.types.SAVE_CANCEL,
|
||||
title: strings[0],
|
||||
body: '',
|
||||
}, trigger).done(function(modal) {
|
||||
this.setupFormModal(modal, strings[1]);
|
||||
}.bind(this));
|
||||
}.bind(this))
|
||||
.fail(Notification.exception);
|
||||
}.bind(this));
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @method getBody
|
||||
* @param {Object} formdata
|
||||
* @private
|
||||
* @return {Promise}
|
||||
*/
|
||||
AddPurpose.prototype.getBody = function(formdata) {
|
||||
|
||||
var params = null;
|
||||
if (typeof formdata !== "undefined") {
|
||||
params = {jsonformdata: JSON.stringify(formdata)};
|
||||
}
|
||||
// Get the content of the modal.
|
||||
return Fragment.loadFragment('tool_dataprivacy', 'addpurpose_form', this.contextId, params);
|
||||
};
|
||||
|
||||
AddPurpose.prototype.setupFormModal = function(modal, saveText) {
|
||||
modal.setLarge();
|
||||
|
||||
modal.setSaveButtonText(saveText);
|
||||
|
||||
// We want to reset the form every time it is opened.
|
||||
modal.getRoot().on(ModalEvents.hidden, this.destroy.bind(this));
|
||||
|
||||
modal.setBody(this.getBody());
|
||||
|
||||
// We catch the modal save event, and use it to submit the form inside the modal.
|
||||
// Triggering a form submission will give JS validation scripts a chance to check for errors.
|
||||
modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));
|
||||
// We also catch the form submit event and use it to submit the form with ajax.
|
||||
modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));
|
||||
|
||||
this.modal = modal;
|
||||
|
||||
modal.show();
|
||||
};
|
||||
|
||||
/**
|
||||
* This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.
|
||||
*
|
||||
* @method submitForm
|
||||
* @param {Event} e Form submission event.
|
||||
* @private
|
||||
*/
|
||||
AddPurpose.prototype.submitForm = function(e) {
|
||||
e.preventDefault();
|
||||
this.modal.getRoot().find('form').submit();
|
||||
};
|
||||
|
||||
AddPurpose.prototype.submitFormAjax = function(e) {
|
||||
// We don't want to do a real form submission.
|
||||
e.preventDefault();
|
||||
|
||||
// Convert all the form elements values to a serialised string.
|
||||
var formData = this.modal.getRoot().find('form').serialize();
|
||||
|
||||
Ajax.call([{
|
||||
methodname: 'tool_dataprivacy_create_purpose_form',
|
||||
args: {jsonformdata: JSON.stringify(formData)},
|
||||
done: function(data) {
|
||||
if (data.validationerrors) {
|
||||
this.modal.setBody(this.getBody(formData));
|
||||
} else {
|
||||
this.close();
|
||||
}
|
||||
}.bind(this),
|
||||
|
||||
fail: Notification.exception
|
||||
}]);
|
||||
};
|
||||
|
||||
AddPurpose.prototype.close = function() {
|
||||
this.destroy();
|
||||
document.location.reload();
|
||||
};
|
||||
|
||||
AddPurpose.prototype.destroy = function() {
|
||||
Y.use('moodle-core-formchangechecker', function() {
|
||||
M.core_formchangechecker.reset_form_dirty_state();
|
||||
});
|
||||
this.modal.destroy();
|
||||
};
|
||||
|
||||
AddPurpose.prototype.removeListeners = function() {
|
||||
$(SELECTORS.PURPOSE_LINK).off('click');
|
||||
};
|
||||
|
||||
return /** @alias module:tool_dataprivacy/add_purpose */ {
|
||||
getInstance: function(contextId) {
|
||||
return new AddPurpose(contextId);
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* AMD module for categories actions.
|
||||
*
|
||||
* @module tool_dataprivacy/categoriesactions
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define([
|
||||
'jquery',
|
||||
'core/ajax',
|
||||
'core/notification',
|
||||
'core/str',
|
||||
'core/modal_factory',
|
||||
'core/modal_events'],
|
||||
function($, Ajax, Notification, Str, ModalFactory, ModalEvents) {
|
||||
|
||||
/**
|
||||
* List of action selectors.
|
||||
*
|
||||
* @type {{DELETE: string}}
|
||||
*/
|
||||
var ACTIONS = {
|
||||
DELETE: '[data-action="deletecategory"]',
|
||||
};
|
||||
|
||||
/**
|
||||
* CategoriesActions class.
|
||||
*/
|
||||
var CategoriesActions = function() {
|
||||
this.registerEvents();
|
||||
};
|
||||
|
||||
/**
|
||||
* Register event listeners.
|
||||
*/
|
||||
CategoriesActions.prototype.registerEvents = function() {
|
||||
$(ACTIONS.DELETE).click(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var id = $(this).data('id');
|
||||
var categoryname = $(this).data('name');
|
||||
var stringkeys = [
|
||||
{
|
||||
key: 'deletecategory',
|
||||
component: 'tool_dataprivacy',
|
||||
param: categoryname
|
||||
},
|
||||
{
|
||||
key: 'deletecategorytext',
|
||||
component: 'tool_dataprivacy',
|
||||
param: categoryname
|
||||
}
|
||||
];
|
||||
|
||||
Str.get_strings(stringkeys).then(function(langStrings) {
|
||||
var title = langStrings[0];
|
||||
var confirmMessage = langStrings[1];
|
||||
return ModalFactory.create({
|
||||
title: title,
|
||||
body: confirmMessage,
|
||||
type: ModalFactory.types.SAVE_CANCEL
|
||||
}).then(function(modal) {
|
||||
modal.setSaveButtonText(title);
|
||||
|
||||
// Handle save event.
|
||||
modal.getRoot().on(ModalEvents.save, function() {
|
||||
|
||||
var request = {
|
||||
methodname: 'tool_dataprivacy_delete_category',
|
||||
args: {'id': id}
|
||||
};
|
||||
|
||||
Ajax.call([request])[0].done(function(data) {
|
||||
if (data.result) {
|
||||
$('tr[data-categoryid="' + id + '"]').remove();
|
||||
} else {
|
||||
Notification.addNotification({
|
||||
message: data.warnings[0].message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}).fail(Notification.exception);
|
||||
});
|
||||
|
||||
// Handle hidden event.
|
||||
modal.getRoot().on(ModalEvents.hidden, function() {
|
||||
// Destroy when hidden.
|
||||
modal.destroy();
|
||||
});
|
||||
|
||||
return modal;
|
||||
});
|
||||
}).done(function(modal) {
|
||||
modal.show();
|
||||
|
||||
}).fail(Notification.exception);
|
||||
});
|
||||
};
|
||||
|
||||
return /** @alias module:tool_dataprivacy/categoriesactions */ {
|
||||
// Public variables and functions.
|
||||
|
||||
/**
|
||||
* Initialise the module.
|
||||
*
|
||||
* @method init
|
||||
* @return {CategoriesActions}
|
||||
*/
|
||||
'init': function() {
|
||||
return new CategoriesActions();
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,156 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Request actions.
|
||||
*
|
||||
* @module tool_dataprivacy/data_deletion
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define([
|
||||
'jquery',
|
||||
'core/ajax',
|
||||
'core/notification',
|
||||
'core/str',
|
||||
'core/modal_factory',
|
||||
'core/modal_events'],
|
||||
function($, Ajax, Notification, Str, ModalFactory, ModalEvents) {
|
||||
|
||||
/**
|
||||
* List of action selectors.
|
||||
*
|
||||
* @type {{MARK_FOR_DELETION: string}}
|
||||
* @type {{SELECT_ALL: string}}
|
||||
*/
|
||||
var ACTIONS = {
|
||||
MARK_FOR_DELETION: '[data-action="markfordeletion"]',
|
||||
SELECT_ALL: '[data-action="selectall"]',
|
||||
};
|
||||
|
||||
/**
|
||||
* List of selectors.
|
||||
*
|
||||
* @type {{SELECTCONTEXT: string}}
|
||||
*/
|
||||
var SELECTORS = {
|
||||
SELECTCONTEXT: '.selectcontext',
|
||||
};
|
||||
|
||||
/**
|
||||
* DataDeletionActions class.
|
||||
*/
|
||||
var DataDeletionActions = function() {
|
||||
this.registerEvents();
|
||||
};
|
||||
|
||||
/**
|
||||
* Register event listeners.
|
||||
*/
|
||||
DataDeletionActions.prototype.registerEvents = function() {
|
||||
$(ACTIONS.MARK_FOR_DELETION).click(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var selectedIds = [];
|
||||
$(SELECTORS.SELECTCONTEXT).each(function() {
|
||||
var checkbox = $(this);
|
||||
if (checkbox.is(':checked')) {
|
||||
selectedIds.push(checkbox.val());
|
||||
}
|
||||
});
|
||||
showConfirmation(selectedIds);
|
||||
});
|
||||
|
||||
$(ACTIONS.SELECT_ALL).change(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var selectallnone = $(this);
|
||||
if (selectallnone.is(':checked')) {
|
||||
$(SELECTORS.SELECTCONTEXT).attr('checked', 'checked');
|
||||
} else {
|
||||
$(SELECTORS.SELECTCONTEXT).removeAttr('checked');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the confirmation dialogue.
|
||||
*
|
||||
* @param {Array} ids The array of expired context record IDs.
|
||||
*/
|
||||
function showConfirmation(ids) {
|
||||
var keys = [
|
||||
{
|
||||
key: 'confirm',
|
||||
component: 'moodle'
|
||||
},
|
||||
{
|
||||
key: 'confirmcontextdeletion',
|
||||
component: 'tool_dataprivacy'
|
||||
}
|
||||
];
|
||||
var wsfunction = 'tool_dataprivacy_confirm_contexts_for_deletion';
|
||||
|
||||
var modalTitle = '';
|
||||
Str.get_strings(keys).then(function(langStrings) {
|
||||
modalTitle = langStrings[0];
|
||||
var confirmMessage = langStrings[1];
|
||||
return ModalFactory.create({
|
||||
title: modalTitle,
|
||||
body: confirmMessage,
|
||||
type: ModalFactory.types.SAVE_CANCEL
|
||||
});
|
||||
}).then(function(modal) {
|
||||
modal.setSaveButtonText(modalTitle);
|
||||
|
||||
// Handle save event.
|
||||
modal.getRoot().on(ModalEvents.save, function() {
|
||||
// Confirm the request.
|
||||
var params = {
|
||||
'ids': ids
|
||||
};
|
||||
|
||||
var request = {
|
||||
methodname: wsfunction,
|
||||
args: params
|
||||
};
|
||||
|
||||
Ajax.call([request])[0].done(function(data) {
|
||||
if (data.result) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
Notification.addNotification({
|
||||
message: data.warnings[0].message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}).fail(Notification.exception);
|
||||
});
|
||||
|
||||
// Handle hidden event.
|
||||
modal.getRoot().on(ModalEvents.hidden, function() {
|
||||
// Destroy when hidden.
|
||||
modal.destroy();
|
||||
});
|
||||
|
||||
return modal;
|
||||
}).done(function(modal) {
|
||||
modal.show();
|
||||
}).fail(Notification.exception);
|
||||
}
|
||||
|
||||
return DataDeletionActions;
|
||||
});
|
||||
@@ -1,317 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Request actions.
|
||||
*
|
||||
* @module tool_dataprivacy/data_registry
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/str', 'core/ajax', 'core/notification', 'core/templates', 'core/modal_factory',
|
||||
'core/modal_events', 'core/fragment', 'tool_dataprivacy/add_purpose', 'tool_dataprivacy/add_category'],
|
||||
function($, Str, Ajax, Notification, Templates, ModalFactory, ModalEvents, Fragment, AddPurpose, AddCategory) {
|
||||
|
||||
var SELECTORS = {
|
||||
TREE_NODES: '[data-context-tree-node=1]',
|
||||
FORM_CONTAINER: '#context-form-container',
|
||||
};
|
||||
|
||||
var DataRegistry = function(systemContextId, initContextLevel, initContextId) {
|
||||
this.systemContextId = systemContextId;
|
||||
this.currentContextLevel = initContextLevel;
|
||||
this.currentContextId = initContextId;
|
||||
this.init();
|
||||
};
|
||||
|
||||
/**
|
||||
* @var {int} systemContextId
|
||||
* @private
|
||||
*/
|
||||
DataRegistry.prototype.systemContextId = 0;
|
||||
|
||||
/**
|
||||
* @var {int} currentContextLevel
|
||||
* @private
|
||||
*/
|
||||
DataRegistry.prototype.currentContextLevel = 0;
|
||||
|
||||
/**
|
||||
* @var {int} currentContextId
|
||||
* @private
|
||||
*/
|
||||
DataRegistry.prototype.currentContextId = 0;
|
||||
|
||||
/**
|
||||
* @var {AddPurpose} addpurpose
|
||||
* @private
|
||||
*/
|
||||
DataRegistry.prototype.addpurpose = null;
|
||||
|
||||
/**
|
||||
* @var {AddCategory} addcategory
|
||||
* @private
|
||||
*/
|
||||
DataRegistry.prototype.addcategory = null;
|
||||
|
||||
DataRegistry.prototype.init = function() {
|
||||
// Add purpose and category modals always at system context.
|
||||
this.addpurpose = AddPurpose.getInstance(this.systemContextId);
|
||||
this.addcategory = AddCategory.getInstance(this.systemContextId);
|
||||
|
||||
var stringKeys = [
|
||||
{
|
||||
key: 'changessaved',
|
||||
component: 'moodle'
|
||||
}, {
|
||||
key: 'contextpurposecategorysaved',
|
||||
component: 'tool_dataprivacy'
|
||||
}, {
|
||||
key: 'noblockstoload',
|
||||
component: 'tool_dataprivacy'
|
||||
}, {
|
||||
key: 'noactivitiestoload',
|
||||
component: 'tool_dataprivacy'
|
||||
}, {
|
||||
key: 'nocoursestoload',
|
||||
component: 'tool_dataprivacy'
|
||||
}
|
||||
];
|
||||
this.strings = Str.get_strings(stringKeys);
|
||||
|
||||
this.registerEventListeners();
|
||||
|
||||
// Load the default context level form.
|
||||
if (this.currentContextId) {
|
||||
this.loadForm('context_form', [this.currentContextId], this.submitContextFormAjax.bind(this));
|
||||
} else {
|
||||
this.loadForm('contextlevel_form', [this.currentContextLevel], this.submitContextLevelFormAjax.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
DataRegistry.prototype.registerEventListeners = function() {
|
||||
$(SELECTORS.TREE_NODES).on('click', function(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
var trigger = $(ev.currentTarget);
|
||||
|
||||
// Active node.
|
||||
$(SELECTORS.TREE_NODES).removeClass('active');
|
||||
trigger.addClass('active');
|
||||
|
||||
var contextLevel = trigger.data('contextlevel');
|
||||
var contextId = trigger.data('contextid');
|
||||
if (contextLevel) {
|
||||
// Context level level.
|
||||
|
||||
window.history.pushState({}, null, '?contextlevel=' + contextLevel);
|
||||
|
||||
// Remove previous add purpose and category listeners to avoid memory leaks.
|
||||
this.addpurpose.removeListeners();
|
||||
this.addcategory.removeListeners();
|
||||
|
||||
// Load the context level form.
|
||||
this.currentContextLevel = contextLevel;
|
||||
this.loadForm('contextlevel_form', [this.currentContextLevel], this.submitContextLevelFormAjax.bind(this));
|
||||
} else if (contextId) {
|
||||
// Context instance level.
|
||||
|
||||
window.history.pushState({}, null, '?contextid=' + contextId);
|
||||
|
||||
// Remove previous add purpose and category listeners to avoid memory leaks.
|
||||
this.addpurpose.removeListeners();
|
||||
this.addcategory.removeListeners();
|
||||
|
||||
// Load the context level form.
|
||||
this.currentContextId = contextId;
|
||||
this.loadForm('context_form', [this.currentContextId], this.submitContextFormAjax.bind(this));
|
||||
} else {
|
||||
// Expandable nodes.
|
||||
|
||||
var expandContextId = trigger.data('expandcontextid');
|
||||
var expandElement = trigger.data('expandelement');
|
||||
var expanded = trigger.data('expanded');
|
||||
|
||||
// Extra checking that there is an expandElement because we remove it after loading 0 branches.
|
||||
if (expandElement) {
|
||||
|
||||
if (!expanded) {
|
||||
if (trigger.data('loaded') || !expandContextId || !expandElement) {
|
||||
this.expand(trigger);
|
||||
} else {
|
||||
|
||||
trigger.find('> i').removeClass('fa-plus');
|
||||
trigger.find('> i').addClass('fa-circle-o-notch fa-spin');
|
||||
this.loadExtra(trigger, expandContextId, expandElement);
|
||||
}
|
||||
} else {
|
||||
this.collapse(trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
DataRegistry.prototype.removeListeners = function() {
|
||||
$(SELECTORS.TREE_NODES).off('click');
|
||||
};
|
||||
|
||||
DataRegistry.prototype.loadForm = function(fragmentName, fragmentArgs, formSubmitCallback) {
|
||||
|
||||
this.clearForm();
|
||||
|
||||
var fragment = Fragment.loadFragment('tool_dataprivacy', fragmentName, this.systemContextId, fragmentArgs);
|
||||
fragment.done(function(html, js) {
|
||||
|
||||
$(SELECTORS.FORM_CONTAINER).html(html);
|
||||
Templates.runTemplateJS(js);
|
||||
|
||||
this.addpurpose.registerEventListeners();
|
||||
this.addcategory.registerEventListeners();
|
||||
|
||||
// We also catch the form submit event and use it to submit the form with ajax.
|
||||
$(SELECTORS.FORM_CONTAINER).on('submit', 'form', formSubmitCallback);
|
||||
|
||||
}.bind(this)).fail(Notification.exception);
|
||||
};
|
||||
|
||||
DataRegistry.prototype.clearForm = function() {
|
||||
// For the previously loaded form.
|
||||
Y.use('moodle-core-formchangechecker', function() {
|
||||
M.core_formchangechecker.reset_form_dirty_state();
|
||||
});
|
||||
|
||||
// Remove previous listeners.
|
||||
$(SELECTORS.FORM_CONTAINER).off('submit', 'form');
|
||||
};
|
||||
|
||||
/**
|
||||
* This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.
|
||||
*
|
||||
* @method submitForm
|
||||
* @param {Event} e Form submission event.
|
||||
* @private
|
||||
*/
|
||||
DataRegistry.prototype.submitForm = function(e) {
|
||||
e.preventDefault();
|
||||
$(SELECTORS.FORM_CONTAINER).find('form').submit();
|
||||
};
|
||||
|
||||
DataRegistry.prototype.submitContextLevelFormAjax = function(e) {
|
||||
this.submitFormAjax(e, 'tool_dataprivacy_set_contextlevel_form');
|
||||
};
|
||||
|
||||
DataRegistry.prototype.submitContextFormAjax = function(e) {
|
||||
this.submitFormAjax(e, 'tool_dataprivacy_set_context_form');
|
||||
};
|
||||
|
||||
DataRegistry.prototype.submitFormAjax = function(e, saveMethodName) {
|
||||
// We don't want to do a real form submission.
|
||||
e.preventDefault();
|
||||
|
||||
// Convert all the form elements values to a serialised string.
|
||||
var formData = $(SELECTORS.FORM_CONTAINER).find('form').serialize();
|
||||
return this.strings.then(function(strings) {
|
||||
Ajax.call([{
|
||||
methodname: saveMethodName,
|
||||
args: {jsonformdata: JSON.stringify(formData)},
|
||||
done: function() {
|
||||
Notification.alert(strings[0], strings[1]);
|
||||
},
|
||||
fail: Notification.exception
|
||||
}]);
|
||||
}).catch(Notification.exception);
|
||||
|
||||
};
|
||||
|
||||
DataRegistry.prototype.loadExtra = function(parentNode, expandContextId, expandElement) {
|
||||
|
||||
Ajax.call([{
|
||||
methodname: 'tool_dataprivacy_tree_extra_branches',
|
||||
args: {
|
||||
contextid: expandContextId,
|
||||
element: expandElement,
|
||||
},
|
||||
done: function(data) {
|
||||
if (data.branches.length == 0) {
|
||||
this.noElements(parentNode, expandElement);
|
||||
return;
|
||||
}
|
||||
Templates.render('tool_dataprivacy/context_tree_branches', data)
|
||||
.then(function(html) {
|
||||
parentNode.after(html);
|
||||
this.removeListeners();
|
||||
this.registerEventListeners();
|
||||
this.expand(parentNode);
|
||||
parentNode.data('loaded', 1);
|
||||
return;
|
||||
}.bind(this))
|
||||
.fail(Notification.exception);
|
||||
}.bind(this),
|
||||
fail: Notification.exception
|
||||
}]);
|
||||
};
|
||||
|
||||
DataRegistry.prototype.noElements = function(node, expandElement) {
|
||||
node.data('expandcontextid', '');
|
||||
node.data('expandelement', '');
|
||||
this.strings.then(function(strings) {
|
||||
|
||||
// 2 = blocks, 3 = activities, 4 = courses (although courses is not likely really).
|
||||
var key = 2;
|
||||
if (expandElement == 'module') {
|
||||
key = 3;
|
||||
} else if (expandElement == 'course') {
|
||||
key = 4;
|
||||
}
|
||||
node.text(strings[key]);
|
||||
return;
|
||||
}).fail(Notification.exception);
|
||||
};
|
||||
|
||||
DataRegistry.prototype.collapse = function(node) {
|
||||
node.data('expanded', 0);
|
||||
node.siblings('nav').addClass('hidden');
|
||||
node.find('> i').removeClass('fa-minus');
|
||||
node.find('> i').addClass('fa-plus');
|
||||
};
|
||||
|
||||
DataRegistry.prototype.expand = function(node) {
|
||||
node.data('expanded', 1);
|
||||
node.siblings('nav').removeClass('hidden');
|
||||
node.find('> i').removeClass('fa-plus');
|
||||
// Also remove the spinning one if data was just loaded.
|
||||
node.find('> i').removeClass('fa-circle-o-notch fa-spin');
|
||||
node.find('> i').addClass('fa-minus');
|
||||
};
|
||||
return /** @alias module:tool_dataprivacy/data_registry */ {
|
||||
|
||||
/**
|
||||
* Initialise the page.
|
||||
*
|
||||
* @param {Number} systemContextId
|
||||
* @param {Number} initContextLevel
|
||||
* @param {Number} initContextId
|
||||
* @return {DataRegistry}
|
||||
*/
|
||||
init: function(systemContextId, initContextLevel, initContextId) {
|
||||
return new DataRegistry(systemContextId, initContextLevel, initContextId);
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Request actions.
|
||||
*
|
||||
* @module tool_dataprivacy/data_request_modal
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/notification', 'core/custom_interaction_events', 'core/modal', 'core/modal_registry',
|
||||
'tool_dataprivacy/events'],
|
||||
function($, Notification, CustomEvents, Modal, ModalRegistry, DataPrivacyEvents) {
|
||||
|
||||
var registered = false;
|
||||
var SELECTORS = {
|
||||
APPROVE_BUTTON: '[data-action="approve"]',
|
||||
DENY_BUTTON: '[data-action="deny"]',
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor for the Modal.
|
||||
*
|
||||
* @param {object} root The root jQuery element for the modal
|
||||
*/
|
||||
var ModalDataRequest = function(root) {
|
||||
Modal.call(this, root);
|
||||
|
||||
if (!this.getFooter().find(SELECTORS.APPROVE_BUTTON).length) {
|
||||
Notification.exception({message: 'No approve button found'});
|
||||
}
|
||||
|
||||
if (!this.getFooter().find(SELECTORS.DENY_BUTTON).length) {
|
||||
Notification.exception({message: 'No deny button found'});
|
||||
}
|
||||
};
|
||||
|
||||
ModalDataRequest.TYPE = 'tool_dataprivacy-data_request';
|
||||
ModalDataRequest.prototype = Object.create(Modal.prototype);
|
||||
ModalDataRequest.prototype.constructor = ModalDataRequest;
|
||||
|
||||
/**
|
||||
* Set up all of the event handling for the modal.
|
||||
*
|
||||
* @method registerEventListeners
|
||||
*/
|
||||
ModalDataRequest.prototype.registerEventListeners = function() {
|
||||
// Apply parent event listeners.
|
||||
Modal.prototype.registerEventListeners.call(this);
|
||||
|
||||
this.getModal().on(CustomEvents.events.activate, SELECTORS.APPROVE_BUTTON, function(e, data) {
|
||||
var approveEvent = $.Event(DataPrivacyEvents.approve);
|
||||
this.getRoot().trigger(approveEvent, this);
|
||||
|
||||
if (!approveEvent.isDefaultPrevented()) {
|
||||
this.hide();
|
||||
data.originalEvent.preventDefault();
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
this.getModal().on(CustomEvents.events.activate, SELECTORS.DENY_BUTTON, function(e, data) {
|
||||
var denyEvent = $.Event(DataPrivacyEvents.deny);
|
||||
this.getRoot().trigger(denyEvent, this);
|
||||
|
||||
if (!denyEvent.isDefaultPrevented()) {
|
||||
this.hide();
|
||||
data.originalEvent.preventDefault();
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
// Automatically register with the modal registry the first time this module is imported so that you can create modals
|
||||
// of this type using the modal factory.
|
||||
if (!registered) {
|
||||
ModalRegistry.register(ModalDataRequest.TYPE, ModalDataRequest, 'tool_dataprivacy/data_request_modal');
|
||||
registered = true;
|
||||
}
|
||||
|
||||
return ModalDataRequest;
|
||||
});
|
||||
@@ -1,92 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Module to update the displayed retention period.
|
||||
*
|
||||
* @module tool_dataprivacy/effective_retention_period
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery'],
|
||||
function($) {
|
||||
|
||||
var SELECTORS = {
|
||||
PURPOSE_SELECT: '#id_purposeid',
|
||||
RETENTION_FIELD_BOOST: '#id_error_retention_current',
|
||||
RETENTION_FIELD_CLEAN: '#fitem_id_retention_current [data-fieldtype=static]',
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor for the retention period display.
|
||||
*
|
||||
* @param {Array} purposeRetentionPeriods Associative array of purposeids with effective retention period at this context
|
||||
*/
|
||||
var EffectiveRetentionPeriod = function(purposeRetentionPeriods) {
|
||||
this.purposeRetentionPeriods = purposeRetentionPeriods;
|
||||
this.registerEventListeners();
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the current 'change' listeners.
|
||||
*
|
||||
* Useful when a new form is loaded.
|
||||
*/
|
||||
var removeListeners = function() {
|
||||
$(SELECTORS.PURPOSE_SELECT).off('change');
|
||||
};
|
||||
|
||||
/**
|
||||
* @var {Array} purposeRetentionPeriods
|
||||
* @private
|
||||
*/
|
||||
EffectiveRetentionPeriod.prototype.purposeRetentionPeriods = [];
|
||||
|
||||
/**
|
||||
* Add purpose change listeners.
|
||||
*
|
||||
* @method registerEventListeners
|
||||
*/
|
||||
EffectiveRetentionPeriod.prototype.registerEventListeners = function() {
|
||||
|
||||
$(SELECTORS.PURPOSE_SELECT).on('change', function(ev) {
|
||||
var selected = $(ev.currentTarget).val();
|
||||
var selectedPurpose = this.purposeRetentionPeriods[selected];
|
||||
|
||||
var cleanSelector = $(SELECTORS.RETENTION_FIELD_CLEAN);
|
||||
if (cleanSelector.length > 0) {
|
||||
cleanSelector.text(selectedPurpose);
|
||||
} else {
|
||||
var boostSelector = $(SELECTORS.RETENTION_FIELD_BOOST);
|
||||
var retentionField = boostSelector.siblings();
|
||||
if (retentionField.length > 0) {
|
||||
retentionField.text(selectedPurpose);
|
||||
}
|
||||
}
|
||||
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
return /** @alias module:tool_dataprivacy/effective_retention_period */ {
|
||||
init: function(purposeRetentionPeriods) {
|
||||
// Remove previously attached listeners.
|
||||
removeListeners();
|
||||
return new EffectiveRetentionPeriod(purposeRetentionPeriods);
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contain the events the data privacy tool can fire.
|
||||
*
|
||||
* @module tool_dataprivacy/events
|
||||
* @class events
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define([], function() {
|
||||
return {
|
||||
approve: 'tool_dataprivacy-data_request:approve',
|
||||
deny: 'tool_dataprivacy-data_request:deny',
|
||||
};
|
||||
});
|
||||
@@ -1,89 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Potential user selector module.
|
||||
*
|
||||
* @module tool_dataprivacy/expand_contract
|
||||
* @class page-expand-contract
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Adrian Greeve
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery', 'core/url', 'core/str'], function($, url, str) {
|
||||
|
||||
var expandedImage = $('<img alt="" src="' + url.imageUrl('t/expanded') + '"/>');
|
||||
var collapsedImage = $('<img alt="" src="' + url.imageUrl('t/collapsed') + '"/>');
|
||||
|
||||
return /** @alias module:tool_dataprivacy/expand-collapse */ {
|
||||
/**
|
||||
* Expand or collapse a selected node.
|
||||
*
|
||||
* @param {object} targetnode The node that we want to expand / collapse
|
||||
* @param {object} thisnode The node that was clicked.
|
||||
* @return {null}
|
||||
*/
|
||||
expandCollapse: function(targetnode, thisnode) {
|
||||
if (targetnode.hasClass('hide')) {
|
||||
targetnode.removeClass('hide');
|
||||
targetnode.addClass('visible');
|
||||
targetnode.attr('aria-expanded', true);
|
||||
thisnode.find(':header i.fa').removeClass('fa-plus-square');
|
||||
thisnode.find(':header i.fa').addClass('fa-minus-square');
|
||||
thisnode.find(':header img.icon').attr('src', expandedImage.attr('src'));
|
||||
} else {
|
||||
targetnode.removeClass('visible');
|
||||
targetnode.addClass('hide');
|
||||
targetnode.attr('aria-expanded', false);
|
||||
thisnode.find(':header i.fa').removeClass('fa-minus-square');
|
||||
thisnode.find(':header i.fa').addClass('fa-plus-square');
|
||||
thisnode.find(':header img.icon').attr('src', collapsedImage.attr('src'));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Expand or collapse all nodes on this page.
|
||||
*
|
||||
* @param {string} nextstate The next state to change to.
|
||||
* @return {null}
|
||||
*/
|
||||
expandCollapseAll: function(nextstate) {
|
||||
var currentstate = (nextstate == 'visible') ? 'hide' : 'visible';
|
||||
var ariaexpandedstate = (nextstate == 'visible') ? true : false;
|
||||
var iconclassnow = (nextstate == 'visible') ? 'fa-plus-square' : 'fa-minus-square';
|
||||
var iconclassnext = (nextstate == 'visible') ? 'fa-minus-square' : 'fa-plus-square';
|
||||
var imagenow = (nextstate == 'visible') ? expandedImage.attr('src') : collapsedImage.attr('src');
|
||||
$('.' + currentstate).each(function() {
|
||||
$(this).removeClass(currentstate);
|
||||
$(this).addClass(nextstate);
|
||||
$(this).attr('aria-expanded', ariaexpandedstate);
|
||||
});
|
||||
$('.tool_dataprivacy-expand-all').data('visibilityState', currentstate);
|
||||
|
||||
str.get_string(currentstate, 'tool_dataprivacy').then(function(langString) {
|
||||
$('.tool_dataprivacy-expand-all').html(langString);
|
||||
}).catch(Notification.exception);
|
||||
|
||||
$(':header i.fa').each(function() {
|
||||
$(this).removeClass(iconclassnow);
|
||||
$(this).addClass(iconclassnext);
|
||||
});
|
||||
$(':header img.icon').each(function() {
|
||||
$(this).attr('src', imagenow);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,76 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Potential user selector module.
|
||||
*
|
||||
* @module tool_dataprivacy/form-user-selector
|
||||
* @class form-user-selector
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {
|
||||
|
||||
return /** @alias module:tool_dataprivacy/form-user-selector */ {
|
||||
|
||||
processResults: function(selector, results) {
|
||||
var users = [];
|
||||
$.each(results, function(index, user) {
|
||||
users.push({
|
||||
value: user.id,
|
||||
label: user._label
|
||||
});
|
||||
});
|
||||
return users;
|
||||
},
|
||||
|
||||
transport: function(selector, query, success, failure) {
|
||||
var promise;
|
||||
|
||||
promise = Ajax.call([{
|
||||
methodname: 'tool_dataprivacy_get_users',
|
||||
args: {
|
||||
query: query
|
||||
}
|
||||
}]);
|
||||
|
||||
promise[0].then(function(results) {
|
||||
var promises = [],
|
||||
i = 0;
|
||||
|
||||
// Render the label.
|
||||
$.each(results, function(index, user) {
|
||||
promises.push(Templates.render('tool_dataprivacy/form-user-selector-suggestion', user));
|
||||
});
|
||||
|
||||
// Apply the label to the results.
|
||||
return $.when.apply($.when, promises).then(function() {
|
||||
var args = arguments;
|
||||
$.each(results, function(index, user) {
|
||||
user._label = args[i];
|
||||
i++;
|
||||
});
|
||||
success(results);
|
||||
return;
|
||||
});
|
||||
|
||||
}).fail(failure);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
@@ -1,221 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* AMD module to enable users to manage their own data requests.
|
||||
*
|
||||
* @module tool_dataprivacy/myrequestactions
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define([
|
||||
'jquery',
|
||||
'core/ajax',
|
||||
'core/notification',
|
||||
'core/str',
|
||||
'core/modal_factory',
|
||||
'core/modal_events',
|
||||
'core/templates'],
|
||||
function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates) {
|
||||
|
||||
/**
|
||||
* List of action selectors.
|
||||
*
|
||||
* @type {{CANCEL_REQUEST: string}}
|
||||
* @type {{CONTACT_DPO: string}}
|
||||
*/
|
||||
var ACTIONS = {
|
||||
CANCEL_REQUEST: '[data-action="cancel"]',
|
||||
CONTACT_DPO: '[data-action="contactdpo"]',
|
||||
};
|
||||
|
||||
/**
|
||||
* MyRequestActions class.
|
||||
*/
|
||||
var MyRequestActions = function() {
|
||||
this.registerEvents();
|
||||
};
|
||||
|
||||
/**
|
||||
* Register event listeners.
|
||||
*/
|
||||
MyRequestActions.prototype.registerEvents = function() {
|
||||
$(ACTIONS.CANCEL_REQUEST).click(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var requestId = $(this).data('requestid');
|
||||
var stringkeys = [
|
||||
{
|
||||
key: 'cancelrequest',
|
||||
component: 'tool_dataprivacy'
|
||||
},
|
||||
{
|
||||
key: 'cancelrequestconfirmation',
|
||||
component: 'tool_dataprivacy'
|
||||
}
|
||||
];
|
||||
|
||||
Str.get_strings(stringkeys).then(function(langStrings) {
|
||||
var title = langStrings[0];
|
||||
var confirmMessage = langStrings[1];
|
||||
return ModalFactory.create({
|
||||
title: title,
|
||||
body: confirmMessage,
|
||||
type: ModalFactory.types.SAVE_CANCEL
|
||||
}).then(function(modal) {
|
||||
modal.setSaveButtonText(title);
|
||||
|
||||
// Handle save event.
|
||||
modal.getRoot().on(ModalEvents.save, function() {
|
||||
// Cancel the request.
|
||||
var params = {
|
||||
'requestid': requestId
|
||||
};
|
||||
|
||||
var request = {
|
||||
methodname: 'tool_dataprivacy_cancel_data_request',
|
||||
args: params
|
||||
};
|
||||
|
||||
Ajax.call([request])[0].done(function(data) {
|
||||
if (data.result) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
Notification.addNotification({
|
||||
message: data.warnings[0].message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}).fail(Notification.exception);
|
||||
});
|
||||
|
||||
// Handle hidden event.
|
||||
modal.getRoot().on(ModalEvents.hidden, function() {
|
||||
// Destroy when hidden.
|
||||
modal.destroy();
|
||||
});
|
||||
|
||||
return modal;
|
||||
});
|
||||
}).done(function(modal) {
|
||||
// Show the modal!
|
||||
modal.show();
|
||||
|
||||
}).fail(Notification.exception);
|
||||
});
|
||||
|
||||
$(ACTIONS.CONTACT_DPO).click(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var replyToEmail = $(this).data('replytoemail');
|
||||
|
||||
var keys = [
|
||||
{
|
||||
key: 'contactdataprotectionofficer',
|
||||
component: 'tool_dataprivacy'
|
||||
},
|
||||
{
|
||||
key: 'send',
|
||||
component: 'tool_dataprivacy'
|
||||
},
|
||||
];
|
||||
|
||||
var sendButtonText = '';
|
||||
Str.get_strings(keys).then(function(langStrings) {
|
||||
var modalTitle = langStrings[0];
|
||||
sendButtonText = langStrings[1];
|
||||
var context = {
|
||||
'replytoemail': replyToEmail
|
||||
};
|
||||
return ModalFactory.create({
|
||||
title: modalTitle,
|
||||
body: Templates.render('tool_dataprivacy/contact_dpo', context),
|
||||
type: ModalFactory.types.SAVE_CANCEL,
|
||||
large: true
|
||||
});
|
||||
}).done(function(modal) {
|
||||
modal.setSaveButtonText(sendButtonText);
|
||||
|
||||
// Handle send event.
|
||||
modal.getRoot().on(ModalEvents.save, function(e) {
|
||||
var message = $('#message').val().trim();
|
||||
if (message.length === 0) {
|
||||
e.preventDefault();
|
||||
// Show validation error when the message is empty.
|
||||
$('[data-region="messageinput"]').addClass('has-danger notifyproblem');
|
||||
$('#id_error_message').removeAttr('hidden');
|
||||
} else {
|
||||
// Send the message.
|
||||
sendMessageToDPO(message);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle hidden event.
|
||||
modal.getRoot().on(ModalEvents.hidden, function() {
|
||||
// Destroy when hidden.
|
||||
modal.destroy();
|
||||
});
|
||||
|
||||
// Show the modal!
|
||||
modal.show();
|
||||
}).fail(Notification.exception);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Send message to the Data Protection Officer.
|
||||
*
|
||||
* @param {String} message The message to send.
|
||||
*/
|
||||
function sendMessageToDPO(message) {
|
||||
var request = {
|
||||
methodname: 'tool_dataprivacy_contact_dpo',
|
||||
args: {
|
||||
message: message
|
||||
}
|
||||
};
|
||||
|
||||
var requestType = 'success';
|
||||
Ajax.call([request])[0].then(function(data) {
|
||||
if (data.result) {
|
||||
return Str.get_string('requestsubmitted', 'tool_dataprivacy');
|
||||
}
|
||||
requestType = 'error';
|
||||
return data.warnings.join('<br>');
|
||||
|
||||
}).done(function(message) {
|
||||
Notification.addNotification({
|
||||
message: message,
|
||||
type: requestType
|
||||
});
|
||||
|
||||
}).fail(Notification.exception);
|
||||
}
|
||||
|
||||
return /** @alias module:tool_dataprivacy/myrequestactions */ {
|
||||
// Public variables and functions.
|
||||
|
||||
/**
|
||||
* Initialise the unified user filter.
|
||||
*
|
||||
* @method init
|
||||
* @return {MyRequestActions}
|
||||
*/
|
||||
'init': function() {
|
||||
return new MyRequestActions();
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,129 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* AMD module for purposes actions.
|
||||
*
|
||||
* @module tool_dataprivacy/purposesactions
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define([
|
||||
'jquery',
|
||||
'core/ajax',
|
||||
'core/notification',
|
||||
'core/str',
|
||||
'core/modal_factory',
|
||||
'core/modal_events'],
|
||||
function($, Ajax, Notification, Str, ModalFactory, ModalEvents) {
|
||||
|
||||
/**
|
||||
* List of action selectors.
|
||||
*
|
||||
* @type {{DELETE: string}}
|
||||
*/
|
||||
var ACTIONS = {
|
||||
DELETE: '[data-action="deletepurpose"]',
|
||||
};
|
||||
|
||||
/**
|
||||
* PurposesActions class.
|
||||
*/
|
||||
var PurposesActions = function() {
|
||||
this.registerEvents();
|
||||
};
|
||||
|
||||
/**
|
||||
* Register event listeners.
|
||||
*/
|
||||
PurposesActions.prototype.registerEvents = function() {
|
||||
$(ACTIONS.DELETE).click(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var id = $(this).data('id');
|
||||
var purposename = $(this).data('name');
|
||||
var stringkeys = [
|
||||
{
|
||||
key: 'deletepurpose',
|
||||
component: 'tool_dataprivacy',
|
||||
param: purposename
|
||||
},
|
||||
{
|
||||
key: 'deletepurposetext',
|
||||
component: 'tool_dataprivacy',
|
||||
param: purposename
|
||||
}
|
||||
];
|
||||
|
||||
Str.get_strings(stringkeys).then(function(langStrings) {
|
||||
var title = langStrings[0];
|
||||
var confirmMessage = langStrings[1];
|
||||
return ModalFactory.create({
|
||||
title: title,
|
||||
body: confirmMessage,
|
||||
type: ModalFactory.types.SAVE_CANCEL
|
||||
}).then(function(modal) {
|
||||
modal.setSaveButtonText(title);
|
||||
|
||||
// Handle save event.
|
||||
modal.getRoot().on(ModalEvents.save, function() {
|
||||
|
||||
var request = {
|
||||
methodname: 'tool_dataprivacy_delete_purpose',
|
||||
args: {'id': id}
|
||||
};
|
||||
|
||||
Ajax.call([request])[0].done(function(data) {
|
||||
if (data.result) {
|
||||
$('tr[data-purposeid="' + id + '"]').remove();
|
||||
} else {
|
||||
Notification.addNotification({
|
||||
message: data.warnings[0].message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}).fail(Notification.exception);
|
||||
});
|
||||
|
||||
// Handle hidden event.
|
||||
modal.getRoot().on(ModalEvents.hidden, function() {
|
||||
// Destroy when hidden.
|
||||
modal.destroy();
|
||||
});
|
||||
|
||||
return modal;
|
||||
});
|
||||
}).done(function(modal) {
|
||||
modal.show();
|
||||
|
||||
}).fail(Notification.exception);
|
||||
});
|
||||
};
|
||||
|
||||
return /** @alias module:tool_dataprivacy/purposesactions */ {
|
||||
// Public variables and functions.
|
||||
|
||||
/**
|
||||
* Initialise the module.
|
||||
*
|
||||
* @method init
|
||||
* @return {PurposesActions}
|
||||
*/
|
||||
'init': function() {
|
||||
return new PurposesActions();
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,227 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Request actions.
|
||||
*
|
||||
* @module tool_dataprivacy/requestactions
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define([
|
||||
'jquery',
|
||||
'core/ajax',
|
||||
'core/notification',
|
||||
'core/str',
|
||||
'core/modal_factory',
|
||||
'core/modal_events',
|
||||
'core/templates',
|
||||
'tool_dataprivacy/data_request_modal',
|
||||
'tool_dataprivacy/events'],
|
||||
function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, ModalDataRequest, DataPrivacyEvents) {
|
||||
|
||||
/**
|
||||
* List of action selectors.
|
||||
*
|
||||
* @type {{APPROVE_REQUEST: string}}
|
||||
* @type {{DENY_REQUEST: string}}
|
||||
* @type {{VIEW_REQUEST: string}}
|
||||
*/
|
||||
var ACTIONS = {
|
||||
APPROVE_REQUEST: '[data-action="approve"]',
|
||||
DENY_REQUEST: '[data-action="deny"]',
|
||||
VIEW_REQUEST: '[data-action="view"]'
|
||||
};
|
||||
|
||||
/**
|
||||
* RequestActions class.
|
||||
*/
|
||||
var RequestActions = function() {
|
||||
this.registerEvents();
|
||||
};
|
||||
|
||||
/**
|
||||
* Register event listeners.
|
||||
*/
|
||||
RequestActions.prototype.registerEvents = function() {
|
||||
$(ACTIONS.VIEW_REQUEST).click(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var requestId = $(this).data('requestid');
|
||||
|
||||
// Cancel the request.
|
||||
var params = {
|
||||
'requestid': requestId
|
||||
};
|
||||
|
||||
var request = {
|
||||
methodname: 'tool_dataprivacy_get_data_request',
|
||||
args: params
|
||||
};
|
||||
|
||||
var promises = Ajax.call([request]);
|
||||
var modalTitle = '';
|
||||
var modalType = ModalFactory.types.DEFAULT;
|
||||
$.when(promises[0]).then(function(data) {
|
||||
if (data.result) {
|
||||
// Check if the status is awaiting approval.
|
||||
if (data.result.status == 2) {
|
||||
modalType = ModalDataRequest.TYPE;
|
||||
}
|
||||
modalTitle = data.result.typename;
|
||||
return Templates.render('tool_dataprivacy/request_details', data.result);
|
||||
}
|
||||
// Fail.
|
||||
Notification.addNotification({
|
||||
message: data.warnings[0].message,
|
||||
type: 'error'
|
||||
});
|
||||
return false;
|
||||
|
||||
}).then(function(html) {
|
||||
return ModalFactory.create({
|
||||
title: modalTitle,
|
||||
body: html,
|
||||
type: modalType,
|
||||
large: true
|
||||
}).then(function(modal) {
|
||||
// Handle approve event.
|
||||
modal.getRoot().on(DataPrivacyEvents.approve, function() {
|
||||
showConfirmation(DataPrivacyEvents.approve, requestId);
|
||||
});
|
||||
|
||||
// Handle deny event.
|
||||
modal.getRoot().on(DataPrivacyEvents.deny, function() {
|
||||
showConfirmation(DataPrivacyEvents.deny, requestId);
|
||||
});
|
||||
|
||||
// Handle hidden event.
|
||||
modal.getRoot().on(ModalEvents.hidden, function() {
|
||||
// Destroy when hidden.
|
||||
modal.destroy();
|
||||
});
|
||||
|
||||
return modal;
|
||||
});
|
||||
}).done(function(modal) {
|
||||
// Show the modal!
|
||||
modal.show();
|
||||
}).fail(Notification.exception);
|
||||
});
|
||||
|
||||
$(ACTIONS.APPROVE_REQUEST).click(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var requestId = $(this).data('requestid');
|
||||
showConfirmation(DataPrivacyEvents.approve, requestId);
|
||||
});
|
||||
|
||||
$(ACTIONS.DENY_REQUEST).click(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var requestId = $(this).data('requestid');
|
||||
showConfirmation(DataPrivacyEvents.deny, requestId);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the confirmation dialogue.
|
||||
*
|
||||
* @param {String} action The action name.
|
||||
* @param {Number} requestId The request ID.
|
||||
*/
|
||||
function showConfirmation(action, requestId) {
|
||||
var keys = [];
|
||||
var wsfunction = '';
|
||||
switch (action) {
|
||||
case DataPrivacyEvents.approve:
|
||||
keys = [
|
||||
{
|
||||
key: 'approverequest',
|
||||
component: 'tool_dataprivacy'
|
||||
},
|
||||
{
|
||||
key: 'confirmapproval',
|
||||
component: 'tool_dataprivacy'
|
||||
}
|
||||
];
|
||||
wsfunction = 'tool_dataprivacy_approve_data_request';
|
||||
break;
|
||||
case DataPrivacyEvents.deny:
|
||||
keys = [
|
||||
{
|
||||
key: 'denyrequest',
|
||||
component: 'tool_dataprivacy'
|
||||
},
|
||||
{
|
||||
key: 'confirmdenial',
|
||||
component: 'tool_dataprivacy'
|
||||
}
|
||||
];
|
||||
wsfunction = 'tool_dataprivacy_deny_data_request';
|
||||
break;
|
||||
}
|
||||
|
||||
var modalTitle = '';
|
||||
Str.get_strings(keys).then(function(langStrings) {
|
||||
modalTitle = langStrings[0];
|
||||
var confirmMessage = langStrings[1];
|
||||
return ModalFactory.create({
|
||||
title: modalTitle,
|
||||
body: confirmMessage,
|
||||
type: ModalFactory.types.SAVE_CANCEL
|
||||
});
|
||||
}).then(function(modal) {
|
||||
modal.setSaveButtonText(modalTitle);
|
||||
|
||||
// Handle save event.
|
||||
modal.getRoot().on(ModalEvents.save, function() {
|
||||
// Confirm the request.
|
||||
var params = {
|
||||
'requestid': requestId
|
||||
};
|
||||
|
||||
var request = {
|
||||
methodname: wsfunction,
|
||||
args: params
|
||||
};
|
||||
|
||||
Ajax.call([request])[0].done(function(data) {
|
||||
if (data.result) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
Notification.addNotification({
|
||||
message: data.warnings[0].message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}).fail(Notification.exception);
|
||||
});
|
||||
|
||||
// Handle hidden event.
|
||||
modal.getRoot().on(ModalEvents.hidden, function() {
|
||||
// Destroy when hidden.
|
||||
modal.destroy();
|
||||
});
|
||||
|
||||
return modal;
|
||||
}).done(function(modal) {
|
||||
modal.show();
|
||||
}).fail(Notification.exception);
|
||||
}
|
||||
|
||||
return RequestActions;
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This page lets users manage categories.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once(__DIR__ . '/../../../config.php');
|
||||
|
||||
$url = new moodle_url("/admin/tool/dataprivacy/categories.php");
|
||||
$title = get_string('editcategories', 'tool_dataprivacy');
|
||||
|
||||
\tool_dataprivacy\page_helper::setup($url, $title, 'dataregistry');
|
||||
|
||||
$output = $PAGE->get_renderer('tool_dataprivacy');
|
||||
echo $output->header();
|
||||
|
||||
$categories = \tool_dataprivacy\api::get_categories();
|
||||
$renderable = new \tool_dataprivacy\output\categories($categories);
|
||||
|
||||
echo $output->render($renderable);
|
||||
echo $output->footer();
|
||||
@@ -1,942 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class containing helper methods for processing data requests.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy;
|
||||
|
||||
use coding_exception;
|
||||
use context_system;
|
||||
use core\invalid_persistent_exception;
|
||||
use core\message\message;
|
||||
use core\task\manager;
|
||||
use core_privacy\local\request\approved_contextlist;
|
||||
use core_privacy\local\request\contextlist_collection;
|
||||
use core_user;
|
||||
use dml_exception;
|
||||
use moodle_exception;
|
||||
use moodle_url;
|
||||
use required_capability_exception;
|
||||
use stdClass;
|
||||
use tool_dataprivacy\external\data_request_exporter;
|
||||
use tool_dataprivacy\local\helper;
|
||||
use tool_dataprivacy\task\initiate_data_request_task;
|
||||
use tool_dataprivacy\task\process_data_request_task;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class containing helper methods for processing data requests.
|
||||
*
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class api {
|
||||
|
||||
/** Data export request type. */
|
||||
const DATAREQUEST_TYPE_EXPORT = 1;
|
||||
|
||||
/** Data deletion request type. */
|
||||
const DATAREQUEST_TYPE_DELETE = 2;
|
||||
|
||||
/** Other request type. Usually of enquiries to the DPO. */
|
||||
const DATAREQUEST_TYPE_OTHERS = 3;
|
||||
|
||||
/** Newly submitted and we haven't yet started finding out where they have data. */
|
||||
const DATAREQUEST_STATUS_PENDING = 0;
|
||||
|
||||
/** Newly submitted and we have started to find the location of data. */
|
||||
const DATAREQUEST_STATUS_PREPROCESSING = 1;
|
||||
|
||||
/** Metadata ready and awaiting review and approval by the Data Protection officer. */
|
||||
const DATAREQUEST_STATUS_AWAITING_APPROVAL = 2;
|
||||
|
||||
/** Request approved and will be processed soon. */
|
||||
const DATAREQUEST_STATUS_APPROVED = 3;
|
||||
|
||||
/** The request is now being processed. */
|
||||
const DATAREQUEST_STATUS_PROCESSING = 4;
|
||||
|
||||
/** Data request completed. */
|
||||
const DATAREQUEST_STATUS_COMPLETE = 5;
|
||||
|
||||
/** Data request cancelled by the user. */
|
||||
const DATAREQUEST_STATUS_CANCELLED = 6;
|
||||
|
||||
/** Data request rejected by the DPO. */
|
||||
const DATAREQUEST_STATUS_REJECTED = 7;
|
||||
|
||||
/**
|
||||
* Determines whether the user can contact the site's Data Protection Officer via Moodle.
|
||||
*
|
||||
* @return boolean True when tool_dataprivacy|contactdataprotectionofficer is enabled.
|
||||
* @throws dml_exception
|
||||
*/
|
||||
public static function can_contact_dpo() {
|
||||
return get_config('tool_dataprivacy', 'contactdataprotectionofficer') == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check's whether the current user has the capability to manage data requests.
|
||||
*
|
||||
* @param int $userid The user ID.
|
||||
* @return bool
|
||||
* @throws coding_exception
|
||||
* @throws dml_exception
|
||||
*/
|
||||
public static function can_manage_data_requests($userid) {
|
||||
$context = context_system::instance();
|
||||
|
||||
// A user can manage data requests if he/she has the site DPO role and has the capability to manage data requests.
|
||||
return self::is_site_dpo($userid) && has_capability('tool/dataprivacy:managedatarequests', $context, $userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current user can manage the data registry at the provided id.
|
||||
*
|
||||
* @param int $contextid Fallback to system context id.
|
||||
* @throws \required_capability_exception
|
||||
* @return null
|
||||
*/
|
||||
public static function check_can_manage_data_registry($contextid = false) {
|
||||
if ($contextid) {
|
||||
$context = \context_helper::instance_by_id($contextid);
|
||||
} else {
|
||||
$context = \context_system::instance();
|
||||
}
|
||||
|
||||
require_capability('tool/dataprivacy:managedataregistry', $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the list of users with the Data Protection Officer role.
|
||||
*
|
||||
* @throws dml_exception
|
||||
*/
|
||||
public static function get_site_dpos() {
|
||||
// Get role(s) that can manage data requests.
|
||||
$dporoles = explode(',', get_config('tool_dataprivacy', 'dporoles'));
|
||||
|
||||
$dpos = [];
|
||||
$context = context_system::instance();
|
||||
foreach ($dporoles as $roleid) {
|
||||
if (empty($roleid)) {
|
||||
continue;
|
||||
}
|
||||
$allnames = get_all_user_name_fields(true, 'u');
|
||||
$fields = 'u.id, u.confirmed, u.username, '. $allnames . ', ' .
|
||||
'u.maildisplay, u.mailformat, u.maildigest, u.email, u.emailstop, u.city, '.
|
||||
'u.country, u.picture, u.idnumber, u.department, u.institution, '.
|
||||
'u.lang, u.timezone, u.lastaccess, u.mnethostid, u.auth, u.suspended, u.deleted, ' .
|
||||
'r.name AS rolename, r.sortorder, '.
|
||||
'r.shortname AS roleshortname, rn.name AS rolecoursealias';
|
||||
// Fetch users that can manage data requests.
|
||||
$dpos += get_role_users($roleid, $context, false, $fields);
|
||||
}
|
||||
|
||||
// If the site has no data protection officer, defer to site admin(s).
|
||||
if (empty($dpos)) {
|
||||
$dpos = get_admins();
|
||||
}
|
||||
return $dpos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given user is a site DPO.
|
||||
*
|
||||
* @param int $userid The user ID.
|
||||
* @return bool
|
||||
* @throws dml_exception
|
||||
*/
|
||||
public static function is_site_dpo($userid) {
|
||||
$dpos = self::get_site_dpos();
|
||||
return array_key_exists($userid, $dpos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lodges a data request and sends the request details to the site Data Protection Officer(s).
|
||||
*
|
||||
* @param int $foruser The user whom the request is being made for.
|
||||
* @param int $type The request type.
|
||||
* @param string $comments Request comments.
|
||||
* @return data_request
|
||||
* @throws invalid_persistent_exception
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public static function create_data_request($foruser, $type, $comments = '') {
|
||||
global $USER;
|
||||
|
||||
$datarequest = new data_request();
|
||||
// The user the request is being made for.
|
||||
$datarequest->set('userid', $foruser);
|
||||
|
||||
$requestinguser = $USER->id;
|
||||
// Check when the user is making a request on behalf of another.
|
||||
if ($requestinguser != $foruser) {
|
||||
if (self::is_site_dpo($requestinguser)) {
|
||||
// The user making the request is a DPO. Should be fine.
|
||||
$datarequest->set('dpo', $requestinguser);
|
||||
} else {
|
||||
// If not a DPO, only users with the capability to make data requests for the user should be allowed.
|
||||
// (e.g. users with the Parent role, etc).
|
||||
if (!api::can_create_data_request_for_user($foruser)) {
|
||||
$forusercontext = \context_user::instance($foruser);
|
||||
throw new required_capability_exception($forusercontext,
|
||||
'tool/dataprivacy:makedatarequestsforchildren', 'nopermissions', '');
|
||||
}
|
||||
}
|
||||
}
|
||||
// The user making the request.
|
||||
$datarequest->set('requestedby', $requestinguser);
|
||||
// Set status.
|
||||
$datarequest->set('status', self::DATAREQUEST_STATUS_PENDING);
|
||||
// Set request type.
|
||||
$datarequest->set('type', $type);
|
||||
// Set request comments.
|
||||
$datarequest->set('comments', $comments);
|
||||
|
||||
// Store subject access request.
|
||||
$datarequest->create();
|
||||
|
||||
// Fire an ad hoc task to initiate the data request process.
|
||||
$task = new initiate_data_request_task();
|
||||
$task->set_custom_data(['requestid' => $datarequest->get('id')]);
|
||||
manager::queue_adhoc_task($task, true);
|
||||
|
||||
return $datarequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the list of the data requests.
|
||||
*
|
||||
* If user ID is provided, it fetches the data requests for the user.
|
||||
* Otherwise, it fetches all of the data requests, provided that the user has the capability to manage data requests.
|
||||
* (e.g. Users with the Data Protection Officer roles)
|
||||
*
|
||||
* @param int $userid The User ID.
|
||||
* @return data_request[]
|
||||
* @throws dml_exception
|
||||
*/
|
||||
public static function get_data_requests($userid = 0) {
|
||||
global $DB, $USER;
|
||||
$results = [];
|
||||
$sort = 'status ASC, timemodified ASC';
|
||||
if ($userid) {
|
||||
// Get the data requests for the user or data requests made by the user.
|
||||
$select = "(userid = :userid OR requestedby = :requestedby)";
|
||||
$params = [
|
||||
'userid' => $userid,
|
||||
'requestedby' => $userid
|
||||
];
|
||||
|
||||
// Build a list of user IDs that the user is allowed to make data requests for.
|
||||
// Of course, the user should be included in this list.
|
||||
$alloweduserids = [$userid];
|
||||
// Get any users that the user can make data requests for.
|
||||
if ($children = helper::get_children_of_user($userid)) {
|
||||
// Get the list of user IDs of the children and merge to the allowed user IDs.
|
||||
$alloweduserids = array_merge($alloweduserids, array_keys($children));
|
||||
}
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($alloweduserids, SQL_PARAMS_NAMED);
|
||||
$select .= " AND userid $insql";
|
||||
$params = array_merge($params, $inparams);
|
||||
|
||||
$results = data_request::get_records_select($select, $params, $sort);
|
||||
} else {
|
||||
// If the current user is one of the site's Data Protection Officers, then fetch all data requests.
|
||||
if (self::is_site_dpo($USER->id)) {
|
||||
$results = data_request::get_records(null, $sort, '');
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether there is already an existing pending/in-progress data request for a user for a given request type.
|
||||
*
|
||||
* @param int $userid The user ID.
|
||||
* @param int $type The request type.
|
||||
* @return bool
|
||||
* @throws coding_exception
|
||||
* @throws dml_exception
|
||||
*/
|
||||
public static function has_ongoing_request($userid, $type) {
|
||||
global $DB;
|
||||
|
||||
// Check if the user already has an incomplete data request of the same type.
|
||||
$nonpendingstatuses = [
|
||||
self::DATAREQUEST_STATUS_COMPLETE,
|
||||
self::DATAREQUEST_STATUS_CANCELLED,
|
||||
self::DATAREQUEST_STATUS_REJECTED,
|
||||
];
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($nonpendingstatuses, SQL_PARAMS_NAMED);
|
||||
$select = 'type = :type AND userid = :userid AND status NOT ' . $insql;
|
||||
$params = array_merge([
|
||||
'type' => $type,
|
||||
'userid' => $userid
|
||||
], $inparams);
|
||||
|
||||
return data_request::record_exists_select($select, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a request is active or not based on its status.
|
||||
*
|
||||
* @param int $status The request status.
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_active($status) {
|
||||
// List of statuses which doesn't require any further processing.
|
||||
$finalstatuses = [
|
||||
self::DATAREQUEST_STATUS_COMPLETE,
|
||||
self::DATAREQUEST_STATUS_CANCELLED,
|
||||
self::DATAREQUEST_STATUS_REJECTED,
|
||||
];
|
||||
|
||||
return !in_array($status, $finalstatuses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the data request for a given request ID.
|
||||
*
|
||||
* @param int $requestid The request identifier.
|
||||
* @param int $status The request status.
|
||||
* @param int $dpoid The user ID of the Data Protection Officer
|
||||
* @param string $comment The comment about the status update.
|
||||
* @return bool
|
||||
* @throws invalid_persistent_exception
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public static function update_request_status($requestid, $status, $dpoid = 0, $comment = '') {
|
||||
// Update the request.
|
||||
$datarequest = new data_request($requestid);
|
||||
$datarequest->set('status', $status);
|
||||
if ($dpoid) {
|
||||
$datarequest->set('dpo', $dpoid);
|
||||
}
|
||||
$datarequest->set('dpocomment', $comment);
|
||||
return $datarequest->update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a request based on the request ID.
|
||||
*
|
||||
* @param int $requestid The request identifier
|
||||
* @return data_request
|
||||
*/
|
||||
public static function get_request($requestid) {
|
||||
return new data_request($requestid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approves a data request based on the request ID.
|
||||
*
|
||||
* @param int $requestid The request identifier
|
||||
* @return bool
|
||||
* @throws coding_exception
|
||||
* @throws dml_exception
|
||||
* @throws invalid_persistent_exception
|
||||
* @throws required_capability_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public static function approve_data_request($requestid) {
|
||||
global $USER;
|
||||
|
||||
// Check first whether the user can manage data requests.
|
||||
if (!self::can_manage_data_requests($USER->id)) {
|
||||
$context = context_system::instance();
|
||||
throw new required_capability_exception($context, 'tool/dataprivacy:managedatarequests', 'nopermissions', '');
|
||||
}
|
||||
|
||||
// Check if request is already awaiting for approval.
|
||||
$request = new data_request($requestid);
|
||||
if ($request->get('status') != self::DATAREQUEST_STATUS_AWAITING_APPROVAL) {
|
||||
throw new moodle_exception('errorrequestnotwaitingforapproval', 'tool_dataprivacy');
|
||||
}
|
||||
|
||||
// Update the status and the DPO.
|
||||
$result = self::update_request_status($requestid, self::DATAREQUEST_STATUS_APPROVED, $USER->id);
|
||||
|
||||
// Approve all the contexts attached to the request.
|
||||
// Currently, approving the request implicitly approves all associated contexts, but this may change in future, allowing
|
||||
// users to selectively approve certain contexts only.
|
||||
self::update_request_contexts_with_status($requestid, contextlist_context::STATUS_APPROVED);
|
||||
|
||||
// Fire an ad hoc task to initiate the data request process.
|
||||
$task = new process_data_request_task();
|
||||
$task->set_custom_data(['requestid' => $requestid]);
|
||||
if ($request->get('type') == self::DATAREQUEST_TYPE_EXPORT) {
|
||||
$task->set_userid($request->get('userid'));
|
||||
}
|
||||
manager::queue_adhoc_task($task, true);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rejects a data request based on the request ID.
|
||||
*
|
||||
* @param int $requestid The request identifier
|
||||
* @return bool
|
||||
* @throws coding_exception
|
||||
* @throws dml_exception
|
||||
* @throws invalid_persistent_exception
|
||||
* @throws required_capability_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public static function deny_data_request($requestid) {
|
||||
global $USER;
|
||||
|
||||
if (!self::can_manage_data_requests($USER->id)) {
|
||||
$context = context_system::instance();
|
||||
throw new required_capability_exception($context, 'tool/dataprivacy:managedatarequests', 'nopermissions', '');
|
||||
}
|
||||
|
||||
// Check if request is already awaiting for approval.
|
||||
$request = new data_request($requestid);
|
||||
if ($request->get('status') != self::DATAREQUEST_STATUS_AWAITING_APPROVAL) {
|
||||
throw new moodle_exception('errorrequestnotwaitingforapproval', 'tool_dataprivacy');
|
||||
}
|
||||
|
||||
// Update the status and the DPO.
|
||||
return self::update_request_status($requestid, self::DATAREQUEST_STATUS_REJECTED, $USER->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the site's Data Protection Officer about a request.
|
||||
*
|
||||
* @param stdClass $dpo The DPO user record
|
||||
* @param data_request $request The data request
|
||||
* @return int|false
|
||||
* @throws coding_exception
|
||||
* @throws dml_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public static function notify_dpo($dpo, data_request $request) {
|
||||
global $PAGE, $SITE;
|
||||
|
||||
$output = $PAGE->get_renderer('tool_dataprivacy');
|
||||
|
||||
$usercontext = \context_user::instance($request->get('requestedby'));
|
||||
$requestexporter = new data_request_exporter($request, ['context' => $usercontext]);
|
||||
$requestdata = $requestexporter->export($output);
|
||||
|
||||
// Create message to send to the Data Protection Officer(s).
|
||||
$typetext = null;
|
||||
$typetext = $requestdata->typename;
|
||||
$subject = get_string('datarequestemailsubject', 'tool_dataprivacy', $typetext);
|
||||
|
||||
$requestedby = $requestdata->requestedbyuser;
|
||||
$datarequestsurl = new moodle_url('/admin/tool/dataprivacy/datarequests.php');
|
||||
$message = new message();
|
||||
$message->courseid = $SITE->id;
|
||||
$message->component = 'tool_dataprivacy';
|
||||
$message->name = 'contactdataprotectionofficer';
|
||||
$message->userfrom = $requestedby;
|
||||
$message->replyto = $requestedby->email;
|
||||
$message->replytoname = $requestedby->fullname;
|
||||
$message->subject = $subject;
|
||||
$message->fullmessageformat = FORMAT_HTML;
|
||||
$message->notification = 1;
|
||||
$message->contexturl = $datarequestsurl;
|
||||
$message->contexturlname = get_string('datarequests', 'tool_dataprivacy');
|
||||
|
||||
// Prepare the context data for the email message body.
|
||||
$messagetextdata = [
|
||||
'requestedby' => $requestedby->fullname,
|
||||
'requesttype' => $typetext,
|
||||
'requestdate' => userdate($requestdata->timecreated),
|
||||
'requestcomments' => $requestdata->messagehtml,
|
||||
'datarequestsurl' => $datarequestsurl
|
||||
];
|
||||
$requestingfor = $requestdata->foruser;
|
||||
if ($requestedby->id == $requestingfor->id) {
|
||||
$messagetextdata['requestfor'] = $messagetextdata['requestedby'];
|
||||
} else {
|
||||
$messagetextdata['requestfor'] = $requestingfor->fullname;
|
||||
}
|
||||
|
||||
// Email the data request to the Data Protection Officer(s)/Admin(s).
|
||||
$messagetextdata['dponame'] = fullname($dpo);
|
||||
// Render message email body.
|
||||
$messagehtml = $output->render_from_template('tool_dataprivacy/data_request_email', $messagetextdata);
|
||||
$message->userto = $dpo;
|
||||
$message->fullmessage = html_to_text($messagehtml);
|
||||
$message->fullmessagehtml = $messagehtml;
|
||||
|
||||
// Send message.
|
||||
return message_send($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a non-DPO user can make a data request for another user.
|
||||
*
|
||||
* @param int $user The user ID of the target user.
|
||||
* @param int $requester The user ID of the user making the request.
|
||||
* @return bool
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public static function can_create_data_request_for_user($user, $requester = null) {
|
||||
$usercontext = \context_user::instance($user);
|
||||
return has_capability('tool/dataprivacy:makedatarequestsforchildren', $usercontext, $requester);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new data purpose.
|
||||
*
|
||||
* @param stdClass $record
|
||||
* @return \tool_dataprivacy\purpose.
|
||||
*/
|
||||
public static function create_purpose(stdClass $record) {
|
||||
self::check_can_manage_data_registry();
|
||||
|
||||
$purpose = new purpose(0, $record);
|
||||
$purpose->create();
|
||||
|
||||
return $purpose;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing data purpose.
|
||||
*
|
||||
* @param stdClass $record
|
||||
* @return \tool_dataprivacy\purpose.
|
||||
*/
|
||||
public static function update_purpose(stdClass $record) {
|
||||
self::check_can_manage_data_registry();
|
||||
|
||||
if (!isset($record->sensitivedatareasons)) {
|
||||
$record->sensitivedatareasons = '';
|
||||
}
|
||||
|
||||
$purpose = new purpose($record->id);
|
||||
$purpose->from_record($record);
|
||||
|
||||
$result = $purpose->update();
|
||||
|
||||
return $purpose;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a data purpose.
|
||||
*
|
||||
* @param int $id
|
||||
* @return bool
|
||||
*/
|
||||
public static function delete_purpose($id) {
|
||||
self::check_can_manage_data_registry();
|
||||
|
||||
$purpose = new purpose($id);
|
||||
if ($purpose->is_used()) {
|
||||
throw new \moodle_exception('Purpose with id ' . $id . ' can not be deleted because it is used.');
|
||||
}
|
||||
return $purpose->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all system data purposes.
|
||||
*
|
||||
* @return \tool_dataprivacy\purpose[]
|
||||
*/
|
||||
public static function get_purposes() {
|
||||
self::check_can_manage_data_registry();
|
||||
|
||||
return purpose::get_records([], 'name', 'ASC');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new data category.
|
||||
*
|
||||
* @param stdClass $record
|
||||
* @return \tool_dataprivacy\category.
|
||||
*/
|
||||
public static function create_category(stdClass $record) {
|
||||
self::check_can_manage_data_registry();
|
||||
|
||||
$category = new category(0, $record);
|
||||
$category->create();
|
||||
|
||||
return $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing data category.
|
||||
*
|
||||
* @param stdClass $record
|
||||
* @return \tool_dataprivacy\category.
|
||||
*/
|
||||
public static function update_category(stdClass $record) {
|
||||
self::check_can_manage_data_registry();
|
||||
|
||||
$category = new category($record->id);
|
||||
$category->from_record($record);
|
||||
|
||||
$result = $category->update();
|
||||
|
||||
return $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a data category.
|
||||
*
|
||||
* @param int $id
|
||||
* @return bool
|
||||
*/
|
||||
public static function delete_category($id) {
|
||||
self::check_can_manage_data_registry();
|
||||
|
||||
$category = new category($id);
|
||||
if ($category->is_used()) {
|
||||
throw new \moodle_exception('Category with id ' . $id . ' can not be deleted because it is used.');
|
||||
}
|
||||
return $category->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all system data categories.
|
||||
*
|
||||
* @return \tool_dataprivacy\category[]
|
||||
*/
|
||||
public static function get_categories() {
|
||||
self::check_can_manage_data_registry();
|
||||
|
||||
return category::get_records([], 'name', 'ASC');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context instance purpose and category.
|
||||
*
|
||||
* @param \stdClass $record
|
||||
* @return \tool_dataprivacy\context_instance
|
||||
*/
|
||||
public static function set_context_instance($record) {
|
||||
self::check_can_manage_data_registry($record->contextid);
|
||||
|
||||
if ($instance = context_instance::get_record_by_contextid($record->contextid, false)) {
|
||||
// Update.
|
||||
$instance->from_record($record);
|
||||
|
||||
if (empty($record->purposeid) && empty($record->categoryid)) {
|
||||
// We accept one of them to be null but we delete it if both are null.
|
||||
self::unset_context_instance($instance);
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Add.
|
||||
$instance = new context_instance(0, $record);
|
||||
}
|
||||
$instance->save();
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets the context instance record.
|
||||
*
|
||||
* @param \tool_dataprivacy\context_instance $instance
|
||||
* @return null
|
||||
*/
|
||||
public static function unset_context_instance(context_instance $instance) {
|
||||
self::check_can_manage_data_registry($instance->get('contextid'));
|
||||
$instance->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context level purpose and category.
|
||||
*
|
||||
* @throws \coding_exception
|
||||
* @param \stdClass $record
|
||||
* @return contextlevel
|
||||
*/
|
||||
public static function set_contextlevel($record) {
|
||||
global $DB;
|
||||
|
||||
// Only manager at system level can set this.
|
||||
self::check_can_manage_data_registry();
|
||||
|
||||
if ($record->contextlevel != CONTEXT_SYSTEM && $record->contextlevel != CONTEXT_USER) {
|
||||
throw new \coding_exception('Only context system and context user can set a contextlevel ' .
|
||||
'purpose and retention');
|
||||
}
|
||||
|
||||
if ($contextlevel = contextlevel::get_record_by_contextlevel($record->contextlevel, false)) {
|
||||
// Update.
|
||||
$contextlevel->from_record($record);
|
||||
} else {
|
||||
// Add.
|
||||
$contextlevel = new contextlevel(0, $record);
|
||||
}
|
||||
$contextlevel->save();
|
||||
|
||||
// We sync with their defaults as we removed these options from the defaults page.
|
||||
$classname = \context_helper::get_class_for_level($record->contextlevel);
|
||||
list($purposevar, $categoryvar) = data_registry::var_names_from_context($classname);
|
||||
set_config($purposevar, $record->purposeid, 'tool_dataprivacy');
|
||||
set_config($categoryvar, $record->categoryid, 'tool_dataprivacy');
|
||||
|
||||
return $contextlevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the effective category given a context instance.
|
||||
*
|
||||
* @param \context $context
|
||||
* @param int $forcedvalue Use this categoryid value as if this was this context instance category.
|
||||
* @return category|false
|
||||
*/
|
||||
public static function get_effective_context_category(\context $context, $forcedvalue=false) {
|
||||
self::check_can_manage_data_registry($context->id);
|
||||
if (!data_registry::defaults_set()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return data_registry::get_effective_context_value($context, 'category', $forcedvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the effective purpose given a context instance.
|
||||
*
|
||||
* @param \context $context
|
||||
* @param int $forcedvalue Use this purposeid value as if this was this context instance purpose.
|
||||
* @return purpose|false
|
||||
*/
|
||||
public static function get_effective_context_purpose(\context $context, $forcedvalue=false) {
|
||||
self::check_can_manage_data_registry($context->id);
|
||||
if (!data_registry::defaults_set()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return data_registry::get_effective_context_value($context, 'purpose', $forcedvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the effective category given a context level.
|
||||
*
|
||||
* @param int $contextlevel
|
||||
* @param int $forcedvalue Use this categoryid value as if this was this context level category.
|
||||
* @return category|false
|
||||
*/
|
||||
public static function get_effective_contextlevel_category($contextlevel, $forcedvalue=false) {
|
||||
self::check_can_manage_data_registry(\context_system::instance()->id);
|
||||
if (!data_registry::defaults_set()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return data_registry::get_effective_contextlevel_value($contextlevel, 'category', $forcedvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the effective purpose given a context level.
|
||||
*
|
||||
* @param int $contextlevel
|
||||
* @param int $forcedvalue Use this purposeid value as if this was this context level purpose.
|
||||
* @return purpose|false
|
||||
*/
|
||||
public static function get_effective_contextlevel_purpose($contextlevel, $forcedvalue=false) {
|
||||
self::check_can_manage_data_registry(\context_system::instance()->id);
|
||||
if (!data_registry::defaults_set()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return data_registry::get_effective_contextlevel_value($contextlevel, 'purpose', $forcedvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an expired context record for the provided context id.
|
||||
*
|
||||
* @param int $contextid
|
||||
* @return \tool_dataprivacy\expired_context
|
||||
*/
|
||||
public static function create_expired_context($contextid) {
|
||||
self::check_can_manage_data_registry();
|
||||
|
||||
$record = (object)[
|
||||
'contextid' => $contextid,
|
||||
'status' => expired_context::STATUS_EXPIRED,
|
||||
];
|
||||
$expiredctx = new expired_context(0, $record);
|
||||
$expiredctx->save();
|
||||
|
||||
return $expiredctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an expired context record.
|
||||
*
|
||||
* @param int $id The tool_dataprivacy_ctxexpire id.
|
||||
* @return bool True on success.
|
||||
*/
|
||||
public static function delete_expired_context($id) {
|
||||
self::check_can_manage_data_registry();
|
||||
|
||||
$expiredcontext = new expired_context($id);
|
||||
return $expiredcontext->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the status of an expired context.
|
||||
*
|
||||
* @param \tool_dataprivacy\expired_context $expiredctx
|
||||
* @param int $status
|
||||
* @return null
|
||||
*/
|
||||
public static function set_expired_context_status(expired_context $expiredctx, $status) {
|
||||
self::check_can_manage_data_registry();
|
||||
|
||||
$expiredctx->set('status', $status);
|
||||
$expiredctx->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the contexts from the contextlist_collection to the request with the status provided.
|
||||
*
|
||||
* @param contextlist_collection $clcollection a collection of contextlists for all components.
|
||||
* @param int $requestid the id of the request.
|
||||
* @param int $status the status to set the contexts to.
|
||||
*/
|
||||
public static function add_request_contexts_with_status(contextlist_collection $clcollection, int $requestid, int $status) {
|
||||
$request = new data_request($requestid);
|
||||
foreach ($clcollection as $contextlist) {
|
||||
// Convert the \core_privacy\local\request\contextlist into a contextlist persistent and store it.
|
||||
$clp = \tool_dataprivacy\contextlist::from_contextlist($contextlist);
|
||||
$clp->create();
|
||||
$contextlistid = $clp->get('id');
|
||||
|
||||
// Store the associated contexts in the contextlist.
|
||||
foreach ($contextlist->get_contextids() as $contextid) {
|
||||
if ($request->get('type') == static::DATAREQUEST_TYPE_DELETE) {
|
||||
$context = \context::instance_by_id($contextid);
|
||||
if (($purpose = static::get_effective_context_purpose($context)) && !empty($purpose->get('protected'))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$context = new contextlist_context();
|
||||
$context->set('contextid', $contextid)
|
||||
->set('contextlistid', $contextlistid)
|
||||
->set('status', $status)
|
||||
->create();
|
||||
}
|
||||
|
||||
// Create the relation to the request.
|
||||
$requestcontextlist = request_contextlist::create_relation($requestid, $contextlistid);
|
||||
$requestcontextlist->create();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the status of all contexts associated with the request.
|
||||
*
|
||||
* @param int $requestid the requestid to which the contexts belong.
|
||||
* @param int $status the status to set to.
|
||||
* @throws \dml_exception if the requestid is invalid.
|
||||
* @throws \moodle_exception if the status is invalid.
|
||||
*/
|
||||
public static function update_request_contexts_with_status(int $requestid, int $status) {
|
||||
// Validate contextlist_context status using the persistent's attribute validation.
|
||||
$contextlistcontext = new contextlist_context();
|
||||
$contextlistcontext->set('status', $status);
|
||||
if (array_key_exists('status', $contextlistcontext->get_errors())) {
|
||||
throw new moodle_exception("Invalid contextlist_context status: $status");
|
||||
}
|
||||
|
||||
// Validate requestid using the persistent's record validation.
|
||||
// A dml_exception is thrown if the record is missing.
|
||||
$datarequest = new data_request($requestid);
|
||||
|
||||
// Bulk update the status of the request contexts.
|
||||
global $DB;
|
||||
|
||||
$select = "SELECT ctx.id as id
|
||||
FROM {" . request_contextlist::TABLE . "} rcl
|
||||
JOIN {" . contextlist::TABLE . "} cl ON rcl.contextlistid = cl.id
|
||||
JOIN {" . contextlist_context::TABLE . "} ctx ON cl.id = ctx.contextlistid
|
||||
WHERE rcl.requestid = ?";
|
||||
|
||||
// Fetch records IDs to be updated and update by chunks, if applicable (limit of 1000 records per update).
|
||||
$limit = 1000;
|
||||
$idstoupdate = $DB->get_fieldset_sql($select, [$requestid]);
|
||||
$count = count($idstoupdate);
|
||||
$idchunks = $idstoupdate;
|
||||
if ($count > $limit) {
|
||||
$idchunks = array_chunk($idstoupdate, $limit);
|
||||
}
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
$initialparams = [$status];
|
||||
foreach ($idchunks as $chunk) {
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($chunk);
|
||||
$update = "UPDATE {" . contextlist_context::TABLE . "}
|
||||
SET status = ?
|
||||
WHERE id $insql";
|
||||
$params = array_merge($initialparams, $inparams);
|
||||
$DB->execute($update, $params);
|
||||
}
|
||||
$transaction->allow_commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all request contextlists having at least on approved context, and returns them as in a contextlist_collection.
|
||||
*
|
||||
* @param data_request $request the data request with which the contextlists are associated.
|
||||
* @return contextlist_collection the collection of approved_contextlist objects.
|
||||
*/
|
||||
public static function get_approved_contextlist_collection_for_request(data_request $request) : contextlist_collection {
|
||||
$foruser = core_user::get_user($request->get('userid'));
|
||||
|
||||
// Fetch all approved contextlists and create the core_privacy\local\request\contextlist objects here.
|
||||
global $DB;
|
||||
$sql = "SELECT cl.component, ctx.contextid
|
||||
FROM {" . request_contextlist::TABLE . "} rcl
|
||||
JOIN {" . contextlist::TABLE . "} cl ON rcl.contextlistid = cl.id
|
||||
JOIN {" . contextlist_context::TABLE . "} ctx ON cl.id = ctx.contextlistid
|
||||
WHERE rcl.requestid = ?
|
||||
AND ctx.status = ?
|
||||
ORDER BY cl.component, ctx.contextid";
|
||||
|
||||
// Create the approved contextlist collection object.
|
||||
$lastcomponent = null;
|
||||
$approvedcollection = new contextlist_collection($foruser->id);
|
||||
|
||||
$rs = $DB->get_recordset_sql($sql, [$request->get('id'), contextlist_context::STATUS_APPROVED]);
|
||||
foreach ($rs as $record) {
|
||||
// If we encounter a new component, and we've built up contexts for the last, then add the approved_contextlist for the
|
||||
// last (the one we've just finished with) and reset the context array for the next one.
|
||||
if ($lastcomponent != $record->component) {
|
||||
if (!empty($contexts)) {
|
||||
$approvedcollection->add_contextlist(new approved_contextlist($foruser, $lastcomponent, $contexts));
|
||||
}
|
||||
$contexts = [];
|
||||
}
|
||||
|
||||
$contexts[] = $record->contextid;
|
||||
$lastcomponent = $record->component;
|
||||
}
|
||||
$rs->close();
|
||||
|
||||
// The data for the last component contextlist won't have been written yet, so write it now.
|
||||
if (!empty($contexts)) {
|
||||
$approvedcollection->add_contextlist(new approved_contextlist($foruser, $lastcomponent, $contexts));
|
||||
}
|
||||
|
||||
return $approvedcollection;
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class for loading/storing data categories from the DB.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
|
||||
|
||||
/**
|
||||
* Class for loading/storing data categories from the DB.
|
||||
*
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class category extends \core\persistent {
|
||||
|
||||
/**
|
||||
* Database table.
|
||||
*/
|
||||
const TABLE = 'tool_dataprivacy_category';
|
||||
|
||||
/**
|
||||
* Return the definition of the properties of this model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_properties() {
|
||||
return array(
|
||||
'name' => array(
|
||||
'type' => PARAM_TEXT,
|
||||
'description' => 'The category name.',
|
||||
),
|
||||
'description' => array(
|
||||
'type' => PARAM_RAW,
|
||||
'description' => 'The category description.',
|
||||
'null' => NULL_ALLOWED,
|
||||
'default' => '',
|
||||
),
|
||||
'descriptionformat' => array(
|
||||
'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN),
|
||||
'type' => PARAM_INT,
|
||||
'default' => FORMAT_HTML
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this category used?.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function is_used() {
|
||||
|
||||
if (\tool_dataprivacy\contextlevel::is_category_used($this->get('id')) ||
|
||||
\tool_dataprivacy\context_instance::is_category_used($this->get('id'))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$pluginconfig = get_config('tool_dataprivacy');
|
||||
$levels = \context_helper::get_all_levels();
|
||||
foreach ($levels as $level => $classname) {
|
||||
|
||||
list($unused, $categoryvar) = \tool_dataprivacy\data_registry::var_names_from_context($classname);
|
||||
if (!empty($pluginconfig->{$categoryvar}) && $pluginconfig->{$categoryvar} == $this->get('id')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class for loading/storing context instances data from the DB.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class for loading/storing context instances data from the DB.
|
||||
*
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class context_instance extends \core\persistent {
|
||||
|
||||
/**
|
||||
* Database table.
|
||||
*/
|
||||
const TABLE = 'tool_dataprivacy_ctxinstance';
|
||||
|
||||
/**
|
||||
* Not set value.
|
||||
*/
|
||||
const NOTSET = 0;
|
||||
|
||||
/**
|
||||
* Inherit value.
|
||||
*/
|
||||
const INHERIT = -1;
|
||||
|
||||
/**
|
||||
* Return the definition of the properties of this model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_properties() {
|
||||
return array(
|
||||
'contextid' => array(
|
||||
'type' => PARAM_INT,
|
||||
'description' => 'The context id.',
|
||||
),
|
||||
'purposeid' => array(
|
||||
'type' => PARAM_INT,
|
||||
'description' => 'The purpose id.',
|
||||
'null' => NULL_ALLOWED,
|
||||
),
|
||||
'categoryid' => array(
|
||||
'type' => PARAM_INT,
|
||||
'description' => 'The category id.',
|
||||
'null' => NULL_ALLOWED,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance by contextid.
|
||||
*
|
||||
* @param mixed $contextid
|
||||
* @param mixed $exception
|
||||
* @return null
|
||||
*/
|
||||
public static function get_record_by_contextid($contextid, $exception = true) {
|
||||
global $DB;
|
||||
|
||||
if (!$record = $DB->get_record(self::TABLE, array('contextid' => $contextid))) {
|
||||
if (!$exception) {
|
||||
return false;
|
||||
} else {
|
||||
throw new \dml_missing_record_exception(self::TABLE);
|
||||
}
|
||||
}
|
||||
|
||||
return new static(0, $record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the provided purpose used by any context instance?
|
||||
*
|
||||
* @param int $purposeid
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_purpose_used($purposeid) {
|
||||
global $DB;
|
||||
return $DB->record_exists(self::TABLE, array('purposeid' => $purposeid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the provided category used by any context instance?
|
||||
*
|
||||
* @param int $categoryid
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_category_used($categoryid) {
|
||||
global $DB;
|
||||
return $DB->record_exists(self::TABLE, array('categoryid' => $categoryid));
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class for loading/storing context level data from the DB.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class for loading/storing context level data from the DB.
|
||||
*
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class contextlevel extends \core\persistent {
|
||||
|
||||
/**
|
||||
* Database table.
|
||||
*/
|
||||
const TABLE = 'tool_dataprivacy_ctxlevel';
|
||||
|
||||
/**
|
||||
* Return the definition of the properties of this model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_properties() {
|
||||
return array(
|
||||
'contextlevel' => array(
|
||||
'type' => PARAM_INT,
|
||||
'description' => 'The context level.',
|
||||
),
|
||||
'purposeid' => array(
|
||||
'type' => PARAM_INT,
|
||||
'description' => 'The purpose id.',
|
||||
),
|
||||
'categoryid' => array(
|
||||
'type' => PARAM_INT,
|
||||
'description' => 'The category id.',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance by contextlevel.
|
||||
*
|
||||
* @param mixed $contextlevel
|
||||
* @param mixed $exception
|
||||
* @return null
|
||||
*/
|
||||
public static function get_record_by_contextlevel($contextlevel, $exception = true) {
|
||||
global $DB;
|
||||
|
||||
$cache = \cache::make('tool_dataprivacy', 'contextlevel');
|
||||
if ($data = $cache->get($contextlevel)) {
|
||||
return new static(0, $data);
|
||||
}
|
||||
|
||||
if (!$record = $DB->get_record(self::TABLE, array('contextlevel' => $contextlevel))) {
|
||||
if (!$exception) {
|
||||
return false;
|
||||
} else {
|
||||
throw new \dml_missing_record_exception(self::TABLE);
|
||||
}
|
||||
}
|
||||
|
||||
return new static(0, $record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the provided purpose used by any contextlevel?
|
||||
*
|
||||
* @param int $purposeid
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_purpose_used($purposeid) {
|
||||
global $DB;
|
||||
return $DB->record_exists(self::TABLE, array('purposeid' => $purposeid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the provided category used by any contextlevel?
|
||||
*
|
||||
* @param int $categoryid
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_category_used($categoryid) {
|
||||
global $DB;
|
||||
return $DB->record_exists(self::TABLE, array('categoryid' => $categoryid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the new record to the cache.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected function after_create() {
|
||||
$cache = \cache::make('tool_dataprivacy', 'contextlevel');
|
||||
$cache->set($this->get('contextlevel'), $this->to_record());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the cache record.
|
||||
*
|
||||
* @param bool $result
|
||||
* @return null
|
||||
*/
|
||||
protected function after_update($result) {
|
||||
$cache = \cache::make('tool_dataprivacy', 'contextlevel');
|
||||
$cache->set($this->get('contextlevel'), $this->to_record());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes unnecessary stuff from db.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected function before_delete() {
|
||||
$cache = \cache::make('tool_dataprivacy', 'contextlevel');
|
||||
$cache->delete($this->get('contextlevel'));
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contains the contextlist persistent.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use core\persistent;
|
||||
|
||||
/**
|
||||
* The contextlist persistent.
|
||||
*
|
||||
* @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class contextlist extends persistent {
|
||||
|
||||
/** The table name this persistent object maps to. */
|
||||
const TABLE = 'tool_dataprivacy_contextlist';
|
||||
|
||||
/**
|
||||
* Return the definition of the properties of this model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_properties() {
|
||||
return [
|
||||
'component' => [
|
||||
'type' => PARAM_TEXT
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new contextlist persistent from an instance of \core_privacy\local\request\contextlist.
|
||||
*
|
||||
* @param \core_privacy\local\request\contextlist $contextlist the core privacy contextlist.
|
||||
* @return contextlist a contextlist persistent.
|
||||
*/
|
||||
public static function from_contextlist(\core_privacy\local\request\contextlist $contextlist) : contextlist {
|
||||
$contextlistpersistent = new contextlist();
|
||||
return $contextlistpersistent->set('component', $contextlist->get_component());
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contains the contextlist_context persistent.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use core\persistent;
|
||||
|
||||
/**
|
||||
* The contextlist_context persistent.
|
||||
*
|
||||
* @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class contextlist_context extends persistent {
|
||||
|
||||
/** The table name this persistent object maps to. */
|
||||
const TABLE = 'tool_dataprivacy_ctxlst_ctx';
|
||||
|
||||
/** This context is pending approval. */
|
||||
const STATUS_PENDING = 0;
|
||||
|
||||
/** This context has been approved. */
|
||||
const STATUS_APPROVED = 1;
|
||||
|
||||
/** This context has been rejected. */
|
||||
const STATUS_REJECTED = 2;
|
||||
|
||||
/**
|
||||
* Return the definition of the properties of this model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_properties() {
|
||||
return [
|
||||
'contextid' => [
|
||||
'type' => PARAM_INT
|
||||
],
|
||||
'contextlistid' => [
|
||||
'type' => PARAM_INT
|
||||
],
|
||||
'status' => [
|
||||
'choices' => [
|
||||
self::STATUS_PENDING,
|
||||
self::STATUS_APPROVED,
|
||||
self::STATUS_REJECTED,
|
||||
],
|
||||
'default' => self::STATUS_PENDING,
|
||||
'type' => PARAM_INT
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,355 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Data registry business logic methods. Mostly internal stuff.
|
||||
*
|
||||
* All methods should be considered part of the internal tool_dataprivacy API
|
||||
* unless something different is specified.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_dataprivacy;
|
||||
|
||||
use coding_exception;
|
||||
use tool_dataprivacy\purpose;
|
||||
use tool_dataprivacy\category;
|
||||
use tool_dataprivacy\contextlevel;
|
||||
use tool_dataprivacy\context_instance;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/coursecatlib.php');
|
||||
|
||||
/**
|
||||
* Data registry business logic methods. Mostly internal stuff.
|
||||
*
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class data_registry {
|
||||
|
||||
/**
|
||||
* @var array Inheritance between context levels.
|
||||
*/
|
||||
private static $contextlevelinheritance = [
|
||||
CONTEXT_USER => [CONTEXT_SYSTEM],
|
||||
CONTEXT_COURSECAT => [CONTEXT_SYSTEM],
|
||||
CONTEXT_COURSE => [CONTEXT_COURSECAT, CONTEXT_SYSTEM],
|
||||
CONTEXT_MODULE => [CONTEXT_COURSE, CONTEXT_COURSECAT, CONTEXT_SYSTEM],
|
||||
CONTEXT_BLOCK => [CONTEXT_COURSE, CONTEXT_COURSECAT, CONTEXT_SYSTEM],
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns purpose and category var names from a context class name
|
||||
*
|
||||
* @param string $classname
|
||||
* @return string[]
|
||||
*/
|
||||
public static function var_names_from_context($classname) {
|
||||
return [
|
||||
$classname . '_purpose',
|
||||
$classname . '_category',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default purpose id and category id for the provided context level.
|
||||
*
|
||||
* The caller code is responsible of checking that $contextlevel is an integer.
|
||||
*
|
||||
* @param int $contextlevel
|
||||
* @return int|false[]
|
||||
*/
|
||||
public static function get_defaults($contextlevel) {
|
||||
|
||||
$classname = \context_helper::get_class_for_level($contextlevel);
|
||||
list($purposevar, $categoryvar) = self::var_names_from_context($classname);
|
||||
|
||||
$purposeid = get_config('tool_dataprivacy', $purposevar);
|
||||
$categoryid = get_config('tool_dataprivacy', $categoryvar);
|
||||
|
||||
if (empty($purposeid)) {
|
||||
$purposeid = false;
|
||||
}
|
||||
if (empty($categoryid)) {
|
||||
$categoryid = false;
|
||||
}
|
||||
|
||||
return [$purposeid, $categoryid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Are data registry defaults set?
|
||||
*
|
||||
* At least the system defaults need to be set.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function defaults_set() {
|
||||
list($purposeid, $categoryid) = self::get_defaults(CONTEXT_SYSTEM);
|
||||
if (empty($purposeid) || empty($categoryid)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all site categories that are visible to the current user.
|
||||
*
|
||||
* @return \coursecat[]
|
||||
*/
|
||||
public static function get_site_categories() {
|
||||
global $DB;
|
||||
|
||||
if (method_exists('\coursecat', 'get_all')) {
|
||||
$categories = \coursecat::get_all(['returnhidden' => true]);
|
||||
} else {
|
||||
// Fallback (to be removed once this gets integrated into master).
|
||||
$ids = $DB->get_fieldset_select('course_categories', 'id', '');
|
||||
$categories = \coursecat::get_many($ids);
|
||||
}
|
||||
|
||||
foreach ($categories as $key => $category) {
|
||||
if (!$category->is_uservisible()) {
|
||||
unset($categories[$key]);
|
||||
}
|
||||
}
|
||||
return $categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the roles assigned to the provided level.
|
||||
*
|
||||
* Important to note that it returns course-level assigned roles
|
||||
* if the provided context level is below course.
|
||||
*
|
||||
* @param \context $context
|
||||
* @return array
|
||||
*/
|
||||
public static function get_subject_scope(\context $context) {
|
||||
|
||||
if ($contextcourse = $context->get_course_context(false)) {
|
||||
// Below course level we look at module or block level roles + course-assigned roles.
|
||||
$courseroles = get_roles_with_assignment_on_context($contextcourse);
|
||||
$roles = $courseroles + get_roles_with_assignment_on_context($context);
|
||||
} else {
|
||||
// We list category + system for others (we don't work with user instances so no need to work about them).
|
||||
$roles = get_roles_used_in_context($context);
|
||||
}
|
||||
|
||||
return array_map(function($role) {
|
||||
if ($role->name) {
|
||||
return $role->name;
|
||||
} else {
|
||||
return $role->shortname;
|
||||
}
|
||||
}, $roles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the effective value given a context instance
|
||||
*
|
||||
* @param \context $context
|
||||
* @param string $element 'category' or 'purpose'
|
||||
* @param int|false $forcedvalue Use this value as if this was this context instance value.
|
||||
* @return persistent|false It return a 'purpose' instance or a 'category' instance, depending on $element
|
||||
*/
|
||||
public static function get_effective_context_value(\context $context, $element, $forcedvalue=false) {
|
||||
|
||||
if ($element !== 'purpose' && $element !== 'category') {
|
||||
throw new coding_exception('Only \'purpose\' and \'category\' are supported.');
|
||||
}
|
||||
$fieldname = $element . 'id';
|
||||
|
||||
if ($forcedvalue === false) {
|
||||
$instance = context_instance::get_record_by_contextid($context->id, false);
|
||||
|
||||
if (!$instance) {
|
||||
// If the instance does not have a value defaults to not set, so we grab the context level default as its value.
|
||||
$instancevalue = context_instance::NOTSET;
|
||||
} else {
|
||||
$instancevalue = $instance->get($fieldname);
|
||||
}
|
||||
} else {
|
||||
$instancevalue = $forcedvalue;
|
||||
}
|
||||
|
||||
// Not set.
|
||||
if ($instancevalue == context_instance::NOTSET) {
|
||||
|
||||
// The effective value varies depending on the context level.
|
||||
if ($context->contextlevel == CONTEXT_USER) {
|
||||
// Use the context level value as we don't allow people to set specific instances values.
|
||||
return self::get_effective_contextlevel_value($context->contextlevel, $element);
|
||||
} else {
|
||||
// Use the default context level value.
|
||||
list($purposeid, $categoryid) = self::get_effective_default_contextlevel_purpose_and_category(
|
||||
$context->contextlevel
|
||||
);
|
||||
return self::get_element_instance($element, $$fieldname);
|
||||
}
|
||||
}
|
||||
|
||||
// Specific value for this context instance.
|
||||
if ($instancevalue != context_instance::INHERIT) {
|
||||
return self::get_element_instance($element, $instancevalue);
|
||||
}
|
||||
|
||||
// This context is using inherited so let's return the parent effective value.
|
||||
$parentcontext = $context->get_parent_context();
|
||||
if (!$parentcontext) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The forced value should not be transmitted to parent contexts.
|
||||
return self::get_effective_context_value($parentcontext, $element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the effective value for a context level.
|
||||
*
|
||||
* Note that this is different from the effective default context level
|
||||
* (see get_effective_default_contextlevel_purpose_and_category) as this is returning
|
||||
* the value set in the data registry, not in the defaults page.
|
||||
*
|
||||
* @param int $contextlevel
|
||||
* @param string $element 'category' or 'purpose'
|
||||
* @param int $forcedvalue Use this value as if this was this context level purpose.
|
||||
* @return \tool_dataprivacy\purpose|false
|
||||
*/
|
||||
public static function get_effective_contextlevel_value($contextlevel, $element, $forcedvalue = false) {
|
||||
|
||||
if ($element !== 'purpose' && $element !== 'category') {
|
||||
throw new coding_exception('Only \'purpose\' and \'category\' are supported.');
|
||||
}
|
||||
$fieldname = $element . 'id';
|
||||
|
||||
if ($contextlevel != CONTEXT_SYSTEM && $contextlevel != CONTEXT_USER) {
|
||||
throw new \coding_exception('Only context_system and context_user values can be retrieved, no other context levels ' .
|
||||
'have a purpose or a category.');
|
||||
}
|
||||
|
||||
if ($forcedvalue === false) {
|
||||
$instance = contextlevel::get_record_by_contextlevel($contextlevel, false);
|
||||
if (!$instance) {
|
||||
// If the context level does not have a value defaults to not set, so we grab the context level default as
|
||||
// its value.
|
||||
$instancevalue = context_instance::NOTSET;
|
||||
} else {
|
||||
$instancevalue = $instance->get($fieldname);
|
||||
}
|
||||
} else {
|
||||
$instancevalue = $forcedvalue;
|
||||
}
|
||||
|
||||
// Not set -> Use the default context level value.
|
||||
if ($instancevalue == context_instance::NOTSET) {
|
||||
list($purposeid, $categoryid) = self::get_effective_default_contextlevel_purpose_and_category($contextlevel);
|
||||
return self::get_element_instance($element, $$fieldname);
|
||||
}
|
||||
|
||||
// Specific value for this context instance.
|
||||
if ($instancevalue != context_instance::INHERIT) {
|
||||
return self::get_element_instance($element, $instancevalue);
|
||||
}
|
||||
|
||||
if ($contextlevel == CONTEXT_SYSTEM) {
|
||||
throw new coding_exception('Something went wrong, system defaults should be set and we should already have a value.');
|
||||
}
|
||||
|
||||
// If we reach this point is that we are inheriting so get the parent context level and repeat.
|
||||
$parentcontextlevel = reset(self::$contextlevelinheritance[$contextlevel]);
|
||||
|
||||
// Forced value are intentionally not passed as the force value should only affect the immediate context level.
|
||||
return self::get_effective_contextlevel_value($parentcontextlevel, $element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the effective default purpose and category for a context level.
|
||||
*
|
||||
* @param int $contextlevel
|
||||
* @param int $forcedpurposevalue Use this value as if this was this context level purpose.
|
||||
* @param int $forcedcategoryvalue Use this value as if this was this context level category.
|
||||
* @return int[]
|
||||
*/
|
||||
public static function get_effective_default_contextlevel_purpose_and_category($contextlevel, $forcedpurposevalue = false, $forcedcategoryvalue = false) {
|
||||
|
||||
list($purposeid, $categoryid) = self::get_defaults($contextlevel);
|
||||
|
||||
// Honour forced values.
|
||||
if ($forcedpurposevalue) {
|
||||
$purposeid = $forcedpurposevalue;
|
||||
}
|
||||
if ($forcedcategoryvalue) {
|
||||
$categoryid = $forcedcategoryvalue;
|
||||
}
|
||||
|
||||
// Not set == INHERIT for defaults.
|
||||
if ($purposeid == context_instance::INHERIT || $purposeid == context_instance::NOTSET) {
|
||||
$purposeid = false;
|
||||
}
|
||||
if ($categoryid == context_instance::INHERIT || $categoryid == context_instance::NOTSET) {
|
||||
$categoryid = false;
|
||||
}
|
||||
|
||||
if ($contextlevel != CONTEXT_SYSTEM && ($purposeid === false || $categoryid === false)) {
|
||||
foreach (self::$contextlevelinheritance[$contextlevel] as $parent) {
|
||||
|
||||
list($parentpurposeid, $parentcategoryid) = self::get_defaults($parent);
|
||||
// Not set == INHERIT for defaults.
|
||||
if ($parentpurposeid == context_instance::INHERIT || $parentpurposeid == context_instance::NOTSET) {
|
||||
$parentpurposeid = false;
|
||||
}
|
||||
if ($parentcategoryid == context_instance::INHERIT || $parentcategoryid == context_instance::NOTSET) {
|
||||
$parentcategoryid = false;
|
||||
}
|
||||
|
||||
if ($purposeid === false && $parentpurposeid) {
|
||||
$purposeid = $parentpurposeid;
|
||||
}
|
||||
|
||||
if ($categoryid === false && $parentcategoryid) {
|
||||
$categoryid = $parentcategoryid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// They may still be false, but we return anyway.
|
||||
return [$purposeid, $categoryid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the provided element.
|
||||
*
|
||||
* @throws \coding_exception
|
||||
* @param string $element The element name 'purpose' or 'category'
|
||||
* @param int $id The element id
|
||||
* @return \core\persistent
|
||||
*/
|
||||
private static function get_element_instance($element, $id) {
|
||||
|
||||
if ($element !== 'purpose' && $element !== 'category') {
|
||||
throw new coding_exception('No other elements than purpose and category are allowed');
|
||||
}
|
||||
|
||||
$classname = '\tool_dataprivacy\\' . $element;
|
||||
return new $classname($id);
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class for loading/storing data requests from the DB.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use core\persistent;
|
||||
|
||||
/**
|
||||
* Class for loading/storing competencies from the DB.
|
||||
*
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class data_request extends persistent {
|
||||
|
||||
/** The table name this persistent object maps to. */
|
||||
const TABLE = 'tool_dataprivacy_request';
|
||||
|
||||
/**
|
||||
* Return the definition of the properties of this model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_properties() {
|
||||
return [
|
||||
'type' => [
|
||||
'choices' => [
|
||||
api::DATAREQUEST_TYPE_EXPORT,
|
||||
api::DATAREQUEST_TYPE_DELETE,
|
||||
api::DATAREQUEST_TYPE_OTHERS,
|
||||
],
|
||||
'type' => PARAM_INT
|
||||
],
|
||||
'comments' => [
|
||||
'type' => PARAM_TEXT,
|
||||
'default' => ''
|
||||
],
|
||||
'commentsformat' => [
|
||||
'choices' => [
|
||||
FORMAT_HTML,
|
||||
FORMAT_MOODLE,
|
||||
FORMAT_PLAIN,
|
||||
FORMAT_MARKDOWN
|
||||
],
|
||||
'type' => PARAM_INT,
|
||||
'default' => FORMAT_PLAIN
|
||||
],
|
||||
'userid' => [
|
||||
'default' => 0,
|
||||
'type' => PARAM_INT
|
||||
],
|
||||
'requestedby' => [
|
||||
'default' => 0,
|
||||
'type' => PARAM_INT
|
||||
],
|
||||
'status' => [
|
||||
'default' => api::DATAREQUEST_STATUS_PENDING,
|
||||
'choices' => [
|
||||
api::DATAREQUEST_STATUS_PENDING,
|
||||
api::DATAREQUEST_STATUS_PREPROCESSING,
|
||||
api::DATAREQUEST_STATUS_AWAITING_APPROVAL,
|
||||
api::DATAREQUEST_STATUS_APPROVED,
|
||||
api::DATAREQUEST_STATUS_PROCESSING,
|
||||
api::DATAREQUEST_STATUS_COMPLETE,
|
||||
api::DATAREQUEST_STATUS_CANCELLED,
|
||||
api::DATAREQUEST_STATUS_REJECTED,
|
||||
],
|
||||
'type' => PARAM_INT
|
||||
],
|
||||
'dpo' => [
|
||||
'default' => 0,
|
||||
'type' => PARAM_INT,
|
||||
'null' => NULL_ALLOWED
|
||||
],
|
||||
'dpocomment' => [
|
||||
'default' => '',
|
||||
'type' => PARAM_TEXT,
|
||||
'null' => NULL_ALLOWED
|
||||
],
|
||||
'dpocommentformat' => [
|
||||
'choices' => [
|
||||
FORMAT_HTML,
|
||||
FORMAT_MOODLE,
|
||||
FORMAT_PLAIN,
|
||||
FORMAT_MARKDOWN
|
||||
],
|
||||
'type' => PARAM_INT,
|
||||
'default' => FORMAT_PLAIN
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class that represents an expired context.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy;
|
||||
use dml_exception;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class that represents an expired context.
|
||||
*
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class expired_context extends \core\persistent {
|
||||
|
||||
/**
|
||||
* Database table.
|
||||
*/
|
||||
const TABLE = 'tool_dataprivacy_ctxexpired';
|
||||
|
||||
/**
|
||||
* Expired contexts with no delete action scheduled.
|
||||
*/
|
||||
const STATUS_EXPIRED = 0;
|
||||
|
||||
/**
|
||||
* Expired contexts approved for deletion.
|
||||
*/
|
||||
const STATUS_APPROVED = 1;
|
||||
|
||||
/**
|
||||
* Already processed expired contexts.
|
||||
*/
|
||||
const STATUS_CLEANED = 2;
|
||||
|
||||
/**
|
||||
* Return the definition of the properties of this model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_properties() {
|
||||
return array(
|
||||
'contextid' => array(
|
||||
'type' => PARAM_INT,
|
||||
'description' => 'The context id.',
|
||||
),
|
||||
'status' => array(
|
||||
'choices' => [
|
||||
self::STATUS_EXPIRED,
|
||||
self::STATUS_APPROVED,
|
||||
self::STATUS_CLEANED,
|
||||
],
|
||||
'type' => PARAM_INT,
|
||||
'description' => 'The deletion status of the context.',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns expired_contexts instances that match the provided level and status.
|
||||
*
|
||||
* @param int $contextlevel The context level filter criterion.
|
||||
* @param bool $status The expired context record's status.
|
||||
* @param string $sort The sort column. Must match the column name in {tool_dataprivacy_ctxexpired} table
|
||||
* @param int $offset The query offset.
|
||||
* @param int $limit The query limit.
|
||||
* @return expired_context[]
|
||||
* @throws dml_exception
|
||||
*/
|
||||
public static function get_records_by_contextlevel($contextlevel = null, $status = false, $sort = 'timecreated',
|
||||
$offset = 0, $limit = 0) {
|
||||
global $DB;
|
||||
|
||||
$sql = "SELECT expiredctx.*
|
||||
FROM {" . self::TABLE . "} expiredctx
|
||||
JOIN {context} ctx
|
||||
ON ctx.id = expiredctx.contextid";
|
||||
$params = [];
|
||||
$conditions = [];
|
||||
|
||||
if (!empty($contextlevel)) {
|
||||
$conditions[] = "ctx.contextlevel = :contextlevel";
|
||||
$params['contextlevel'] = intval($contextlevel);
|
||||
}
|
||||
|
||||
if ($status !== false) {
|
||||
$conditions[] = "expiredctx.status = :status";
|
||||
$params['status'] = intval($status);
|
||||
}
|
||||
|
||||
if (!empty($conditions)) {
|
||||
$sql .= ' WHERE ' . implode(' AND ', $conditions);
|
||||
}
|
||||
$sql .= " ORDER BY expiredctx.{$sort}";
|
||||
|
||||
$records = $DB->get_records_sql($sql, $params, $offset, $limit);
|
||||
|
||||
// We return class instances.
|
||||
$instances = array();
|
||||
foreach ($records as $key => $record) {
|
||||
$instances[$key] = new static(0, $record);
|
||||
}
|
||||
|
||||
return $instances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of expired_contexts instances that match the provided level and status.
|
||||
*
|
||||
* @param int $contextlevel
|
||||
* @param bool $status
|
||||
* @return int
|
||||
* @throws dml_exception
|
||||
*/
|
||||
public static function get_record_count_by_contextlevel($contextlevel = null, $status = false) {
|
||||
global $DB;
|
||||
|
||||
$sql = "SELECT COUNT(1)
|
||||
FROM {" . self::TABLE . "} expiredctx
|
||||
JOIN {context} ctx
|
||||
ON ctx.id = expiredctx.contextid";
|
||||
|
||||
$conditions = [];
|
||||
$params = [];
|
||||
|
||||
if (!empty($contextlevel)) {
|
||||
$conditions[] = "ctx.contextlevel = :contextlevel";
|
||||
$params['contextlevel'] = intval($contextlevel);
|
||||
}
|
||||
|
||||
if ($status !== false) {
|
||||
$sql .= " AND expiredctx.status = :status";
|
||||
$params['status'] = intval($status);
|
||||
}
|
||||
if (!empty($conditions)) {
|
||||
$sql .= ' WHERE ' . implode(' AND ', $conditions);
|
||||
}
|
||||
|
||||
return $DB->count_records_sql($sql, $params);
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Expired contexts manager.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy;
|
||||
|
||||
use tool_dataprivacy\api;
|
||||
use tool_dataprivacy\purpose;
|
||||
use tool_dataprivacy\context_instance;
|
||||
use tool_dataprivacy\data_registry;
|
||||
use tool_dataprivacy\expired_context;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Expired contexts manager.
|
||||
*
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class expired_contexts_manager {
|
||||
|
||||
/**
|
||||
* Number of deleted contexts for each scheduled task run.
|
||||
*/
|
||||
const DELETE_LIMIT = 200;
|
||||
|
||||
/**
|
||||
* Returns the list of expired context instances.
|
||||
*
|
||||
* @return \stdClass[]
|
||||
*/
|
||||
abstract protected function get_expired_contexts();
|
||||
|
||||
/**
|
||||
* Specify with context levels this expired contexts manager is deleting.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
abstract protected function get_context_levels();
|
||||
|
||||
/**
|
||||
* Flag expired contexts as expired.
|
||||
*
|
||||
* @return int The number of contexts flagged as expired.
|
||||
*/
|
||||
public function flag_expired() {
|
||||
|
||||
if (!$this->check_requirements()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$contexts = $this->get_expired_contexts();
|
||||
foreach ($contexts as $context) {
|
||||
api::create_expired_context($context->id);
|
||||
}
|
||||
|
||||
return count($contexts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the expired contexts.
|
||||
*
|
||||
* @return int The number of deleted contexts.
|
||||
*/
|
||||
public function delete() {
|
||||
|
||||
$numprocessed = 0;
|
||||
|
||||
if (!$this->check_requirements()) {
|
||||
return $numprocessed;
|
||||
}
|
||||
|
||||
$privacymanager = new \core_privacy\manager();
|
||||
|
||||
foreach ($this->get_context_levels() as $level) {
|
||||
|
||||
$expiredcontexts = expired_context::get_records_by_contextlevel($level, expired_context::STATUS_APPROVED);
|
||||
|
||||
foreach ($expiredcontexts as $expiredctx) {
|
||||
|
||||
if (!$this->delete_expired_context($privacymanager, $expiredctx)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$numprocessed += 1;
|
||||
if ($numprocessed == self::DELETE_LIMIT) {
|
||||
// Close the recordset.
|
||||
$expiredcontexts->close();
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $numprocessed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes user data from the provided context.
|
||||
*
|
||||
* @param \core_privacy\manager $privacymanager
|
||||
* @param \tool_dataprivacy\expired_context $expiredctx
|
||||
* @return \context|false
|
||||
*/
|
||||
protected function delete_expired_context(\core_privacy\manager $privacymanager, \tool_dataprivacy\expired_context $expiredctx) {
|
||||
|
||||
$context = \context::instance_by_id($expiredctx->get('contextid'), IGNORE_MISSING);
|
||||
if (!$context) {
|
||||
api::delete_expired_context($expiredctx->get('contextid'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PHPUNIT_TEST) {
|
||||
mtrace('Deleting context ' . $context->id . ' - ' .
|
||||
shorten_text($context->get_context_name(true, true)));
|
||||
}
|
||||
|
||||
$privacymanager->delete_data_for_all_users_in_context($context);
|
||||
api::set_expired_context_status($expiredctx, expired_context::STATUS_CLEANED);
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the requirements to start deleting contexts are satisified.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function check_requirements() {
|
||||
api::check_can_manage_data_registry(\context_system::instance()->id);
|
||||
|
||||
if (!data_registry::defaults_set()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Expired contexts manager for CONTEXT_COURSE, CONTEXT_MODULE and CONTEXT_BLOCK.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy;
|
||||
|
||||
use tool_dataprivacy\purpose;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Expired contexts manager for CONTEXT_COURSE, CONTEXT_MODULE and CONTEXT_BLOCK.
|
||||
*
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class expired_course_related_contexts extends \tool_dataprivacy\expired_contexts_manager {
|
||||
|
||||
/**
|
||||
* Course-related context levels.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
protected function get_context_levels() {
|
||||
return [CONTEXT_MODULE, CONTEXT_BLOCK, CONTEXT_COURSE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a recordset with user context instances that are possibly expired (to be confirmed by get_recordset_callback).
|
||||
*
|
||||
* @return \stdClass[]
|
||||
*/
|
||||
protected function get_expired_contexts() {
|
||||
global $DB;
|
||||
|
||||
// Including context info + course end date + purposeid (this last one only if defined).
|
||||
$fields = 'ctx.id AS id, ctxcourse.enddate AS courseenddate, dpctx.purposeid AS purposeid, ' .
|
||||
\context_helper::get_preload_record_columns_sql('ctx');
|
||||
|
||||
// We want all contexts at course-dependant levels.
|
||||
$parentpath = $DB->sql_concat('ctxcourse.path', "'/%'");
|
||||
|
||||
// This SQL query returns all course-dependant contexts (including the course context)
|
||||
// which course end date already passed.
|
||||
$sql = "SELECT $fields
|
||||
FROM {context} ctx
|
||||
JOIN (
|
||||
SELECT c.enddate, subctx.path
|
||||
FROM {context} subctx
|
||||
JOIN {course} c
|
||||
ON subctx.contextlevel = ? AND subctx.instanceid = c.id
|
||||
WHERE c.enddate < ? AND c.enddate > 0
|
||||
) ctxcourse
|
||||
ON ctx.path LIKE {$parentpath} OR ctx.path = ctxcourse.path
|
||||
LEFT JOIN {tool_dataprivacy_ctxinstance} dpctx
|
||||
ON dpctx.contextid = ctx.id
|
||||
LEFT JOIN {tool_dataprivacy_ctxexpired} expiredctx
|
||||
ON ctx.id = expiredctx.contextid
|
||||
WHERE expiredctx.id IS NULL
|
||||
ORDER BY ctx.contextlevel DESC, ctx.path";
|
||||
$possiblyexpired = $DB->get_recordset_sql($sql, [CONTEXT_COURSE, time()]);
|
||||
|
||||
$expiredcontexts = [];
|
||||
$excludedcontextids = [];
|
||||
foreach ($possiblyexpired as $record) {
|
||||
|
||||
\context_helper::preload_from_record($record);
|
||||
|
||||
// No strict checking as the context may already be deleted (e.g. we just deleted a course,
|
||||
// module contexts below it will not exist).
|
||||
$context = \context::instance_by_id($record->id, false);
|
||||
if (!$context) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We pass the value we just got from SQL so get_effective_context_purpose don't need to query
|
||||
// the db again to retrieve it. If there is no tool_dataprovider_ctxinstance record
|
||||
// $record->purposeid will be null which is ok as it would force get_effective_context_purpose
|
||||
// to return the default purpose for the context context level (no db queries involved).
|
||||
$purposevalue = $record->purposeid !== null ? $record->purposeid : context_instance::NOTSET;
|
||||
|
||||
// It should be cheap as system purposes and context level purposes will be retrieved from a cache most of the time.
|
||||
$purpose = api::get_effective_context_purpose($context, $purposevalue);
|
||||
|
||||
$dt = new \DateTime();
|
||||
$dt->setTimestamp($record->courseenddate);
|
||||
$di = new \DateInterval($purpose->get('retentionperiod'));
|
||||
$dt->add($di);
|
||||
|
||||
if (time() < $dt->getTimestamp()) {
|
||||
// Exclude this context ID as it has not reached the retention period yet.
|
||||
$excludedcontextids[] = $context->id;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this context has children that have not yet expired.
|
||||
$hasunexpiredchildren = false;
|
||||
$children = $context->get_child_contexts();
|
||||
foreach ($children as $child) {
|
||||
if (in_array($child->id, $excludedcontextids)) {
|
||||
$hasunexpiredchildren = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($hasunexpiredchildren) {
|
||||
// Exclude this context ID as it has children that have not yet expired.
|
||||
$excludedcontextids[] = $context->id;
|
||||
continue;
|
||||
}
|
||||
|
||||
$expiredcontexts[$context->id] = $context;
|
||||
}
|
||||
|
||||
return $expiredcontexts;
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Expired contexts manager for CONTEXT_USER.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy;
|
||||
|
||||
use tool_dataprivacy\purpose;
|
||||
use tool_dataprivacy\context_instance;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Expired contexts manager for CONTEXT_USER.
|
||||
*
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class expired_user_contexts extends \tool_dataprivacy\expired_contexts_manager {
|
||||
|
||||
/**
|
||||
* Only user level.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
protected function get_context_levels() {
|
||||
return [CONTEXT_USER];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user context instances that are expired.
|
||||
*
|
||||
* @return \stdClass[]
|
||||
*/
|
||||
protected function get_expired_contexts() {
|
||||
global $DB;
|
||||
|
||||
// Including context info + last login timestamp.
|
||||
$fields = 'ctx.id AS id, ' . \context_helper::get_preload_record_columns_sql('ctx');
|
||||
|
||||
$purpose = api::get_effective_contextlevel_purpose(CONTEXT_USER);
|
||||
|
||||
// Calculate what is considered expired according to the context level effective purpose (= now + retention period).
|
||||
$expiredtime = new \DateTime();
|
||||
$retention = new \DateInterval($purpose->get('retentionperiod'));
|
||||
$expiredtime->sub($retention);
|
||||
|
||||
$sql = "SELECT $fields FROM {context} ctx
|
||||
JOIN {user} u ON ctx.contextlevel = ? AND ctx.instanceid = u.id
|
||||
LEFT JOIN {tool_dataprivacy_ctxexpired} expiredctx ON ctx.id = expiredctx.contextid
|
||||
WHERE u.lastaccess <= ? AND u.lastaccess > 0 AND expiredctx.id IS NULL
|
||||
ORDER BY ctx.path, ctx.contextlevel ASC";
|
||||
$possiblyexpired = $DB->get_recordset_sql($sql, [CONTEXT_USER, $expiredtime->getTimestamp()]);
|
||||
|
||||
$expiredcontexts = [];
|
||||
foreach ($possiblyexpired as $record) {
|
||||
|
||||
\context_helper::preload_from_record($record);
|
||||
|
||||
// No strict checking as the context may already be deleted (e.g. we just deleted a course,
|
||||
// module contexts below it will not exist).
|
||||
$context = \context::instance_by_id($record->id, false);
|
||||
if (!$context) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_siteadmin($context->instanceid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$courses = enrol_get_users_courses($context->instanceid, false, ['enddate']);
|
||||
foreach ($courses as $course) {
|
||||
if (!$course->enddate) {
|
||||
// We can not know it what is going on here, so we prefer to be conservative.
|
||||
continue 2;
|
||||
}
|
||||
|
||||
if ($course->enddate >= time()) {
|
||||
// Future or ongoing course.
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
$expiredcontexts[$context->id] = $context;
|
||||
}
|
||||
|
||||
return $expiredcontexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes user data from the provided context.
|
||||
*
|
||||
* Overwritten to delete the user.
|
||||
*
|
||||
* @param \core_privacy\manager $privacymanager
|
||||
* @param \tool_dataprivacy\expired_context $expiredctx
|
||||
* @return \context|false
|
||||
*/
|
||||
protected function delete_expired_context(\core_privacy\manager $privacymanager, \tool_dataprivacy\expired_context $expiredctx) {
|
||||
$context = \context::instance_by_id($expiredctx->get('contextid'), IGNORE_MISSING);
|
||||
if (!$context) {
|
||||
api::delete_expired_context($expiredctx->get('contextid'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PHPUNIT_TEST) {
|
||||
mtrace('Deleting context ' . $context->id . ' - ' .
|
||||
shorten_text($context->get_context_name(true, true)));
|
||||
}
|
||||
|
||||
// To ensure that all user data is deleted, instead of deleting by context, we run through and collect any stray
|
||||
// contexts for the user that may still exist and call delete_data_for_user().
|
||||
$user = \core_user::get_user($context->instanceid, '*', MUST_EXIST);
|
||||
$approvedlistcollection = new \core_privacy\local\request\contextlist_collection($user->id);
|
||||
$contextlistcollection = $privacymanager->get_contexts_for_userid($user->id);
|
||||
|
||||
foreach ($contextlistcollection as $contextlist) {
|
||||
$approvedlistcollection->add_contextlist(new \core_privacy\local\request\approved_contextlist(
|
||||
$user,
|
||||
$contextlist->get_component(),
|
||||
$contextlist->get_contextids()
|
||||
));
|
||||
}
|
||||
|
||||
$privacymanager->delete_data_for_user($approvedlistcollection);
|
||||
api::set_expired_context_status($expiredctx, expired_context::STATUS_CLEANED);
|
||||
|
||||
// Delete the user.
|
||||
delete_user($user);
|
||||
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,56 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class for exporting data category.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy\external;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use core\external\persistent_exporter;
|
||||
|
||||
/**
|
||||
* Class for exporting field data.
|
||||
*
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class category_exporter extends persistent_exporter {
|
||||
|
||||
/**
|
||||
* Defines the persistent class.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function define_class() {
|
||||
return \tool_dataprivacy\category::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of objects that are related.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_related() {
|
||||
return array(
|
||||
'context' => 'context',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class for exporting user evidence with all competencies.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy\external;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use coding_exception;
|
||||
use core\external\persistent_exporter;
|
||||
use core_user;
|
||||
use core_user\external\user_summary_exporter;
|
||||
use dml_exception;
|
||||
use moodle_exception;
|
||||
use renderer_base;
|
||||
use tool_dataprivacy\api;
|
||||
use tool_dataprivacy\data_request;
|
||||
use tool_dataprivacy\local\helper;
|
||||
|
||||
/**
|
||||
* Class for exporting user evidence with all competencies.
|
||||
*
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class data_request_exporter extends persistent_exporter {
|
||||
|
||||
/**
|
||||
* Class definition.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function define_class() {
|
||||
return data_request::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Related objects definition.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_related() {
|
||||
return [
|
||||
'context' => 'context',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Other properties definition.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_other_properties() {
|
||||
return [
|
||||
'foruser' => [
|
||||
'type' => user_summary_exporter::read_properties_definition(),
|
||||
],
|
||||
'requestedbyuser' => [
|
||||
'type' => user_summary_exporter::read_properties_definition(),
|
||||
'optional' => true
|
||||
],
|
||||
'dpouser' => [
|
||||
'type' => user_summary_exporter::read_properties_definition(),
|
||||
'optional' => true
|
||||
],
|
||||
'messagehtml' => [
|
||||
'type' => PARAM_RAW,
|
||||
'optional' => true
|
||||
],
|
||||
'typename' => [
|
||||
'type' => PARAM_TEXT,
|
||||
],
|
||||
'typenameshort' => [
|
||||
'type' => PARAM_TEXT,
|
||||
],
|
||||
'statuslabel' => [
|
||||
'type' => PARAM_TEXT,
|
||||
],
|
||||
'statuslabelclass' => [
|
||||
'type' => PARAM_TEXT,
|
||||
],
|
||||
'canreview' => [
|
||||
'type' => PARAM_BOOL,
|
||||
'optional' => true,
|
||||
'default' => false
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign values to the defined other properties.
|
||||
*
|
||||
* @param renderer_base $output The output renderer object.
|
||||
* @return array
|
||||
* @throws coding_exception
|
||||
* @throws dml_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
protected function get_other_values(renderer_base $output) {
|
||||
$values = [];
|
||||
|
||||
$foruserid = $this->persistent->get('userid');
|
||||
$user = core_user::get_user($foruserid, '*', MUST_EXIST);
|
||||
$userexporter = new user_summary_exporter($user);
|
||||
$values['foruser'] = $userexporter->export($output);
|
||||
|
||||
$requestedbyid = $this->persistent->get('requestedby');
|
||||
if ($requestedbyid != $foruserid) {
|
||||
$user = core_user::get_user($requestedbyid, '*', MUST_EXIST);
|
||||
$userexporter = new user_summary_exporter($user);
|
||||
$values['requestedbyuser'] = $userexporter->export($output);
|
||||
} else {
|
||||
$values['requestedbyuser'] = $values['foruser'];
|
||||
}
|
||||
|
||||
if (!empty($this->persistent->get('dpo'))) {
|
||||
$dpoid = $this->persistent->get('dpo');
|
||||
$user = core_user::get_user($dpoid, '*', MUST_EXIST);
|
||||
$userexporter = new user_summary_exporter($user);
|
||||
$values['dpouser'] = $userexporter->export($output);
|
||||
}
|
||||
|
||||
$values['messagehtml'] = text_to_html($this->persistent->get('comments'));
|
||||
|
||||
$values['typename'] = helper::get_request_type_string($this->persistent->get('type'));
|
||||
$values['typenameshort'] = helper::get_shortened_request_type_string($this->persistent->get('type'));
|
||||
|
||||
$values['canreview'] = false;
|
||||
$values['statuslabel'] = helper::get_request_status_string($this->persistent->get('status'));
|
||||
switch ($this->persistent->get('status')) {
|
||||
case api::DATAREQUEST_STATUS_PENDING:
|
||||
$values['statuslabelclass'] = 'label-default';
|
||||
break;
|
||||
case api::DATAREQUEST_STATUS_PREPROCESSING:
|
||||
$values['statuslabelclass'] = 'label-default';
|
||||
break;
|
||||
case api::DATAREQUEST_STATUS_AWAITING_APPROVAL:
|
||||
$values['statuslabelclass'] = 'label-info';
|
||||
// DPO can review the request once it's ready.
|
||||
$values['canreview'] = true;
|
||||
break;
|
||||
case api::DATAREQUEST_STATUS_APPROVED:
|
||||
$values['statuslabelclass'] = 'label-info';
|
||||
break;
|
||||
case api::DATAREQUEST_STATUS_PROCESSING:
|
||||
$values['statuslabelclass'] = 'label-info';
|
||||
break;
|
||||
case api::DATAREQUEST_STATUS_COMPLETE:
|
||||
$values['statuslabelclass'] = 'label-success';
|
||||
break;
|
||||
case api::DATAREQUEST_STATUS_CANCELLED:
|
||||
$values['statuslabelclass'] = 'label-warning';
|
||||
break;
|
||||
case api::DATAREQUEST_STATUS_REJECTED:
|
||||
$values['statuslabelclass'] = 'label-important';
|
||||
break;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class for exporting an object containing a name and a description.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy\external;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use core\external\exporter;
|
||||
|
||||
/**
|
||||
* Class that exports an object containing a name and a description.
|
||||
*
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class name_description_exporter extends exporter {
|
||||
|
||||
/**
|
||||
* Returns a list of objects that are related.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_related() {
|
||||
return array(
|
||||
'context' => 'context',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of properties.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_properties() {
|
||||
return [
|
||||
'name' => [
|
||||
'type' => PARAM_TEXT,
|
||||
],
|
||||
'description' => [
|
||||
'type' => PARAM_TEXT,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class for exporting data purpose.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy\external;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use coding_exception;
|
||||
use core\external\persistent_exporter;
|
||||
use DateInterval;
|
||||
use Exception;
|
||||
use renderer_base;
|
||||
use tool_dataprivacy\purpose;
|
||||
|
||||
/**
|
||||
* Class for exporting field data.
|
||||
*
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class purpose_exporter extends persistent_exporter {
|
||||
|
||||
/**
|
||||
* Defines the persistent class.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function define_class() {
|
||||
return purpose::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of objects that are related.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_related() {
|
||||
return array(
|
||||
'context' => 'context',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of additional properties.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_other_properties() {
|
||||
return [
|
||||
'formattedretentionperiod' => [
|
||||
'type' => PARAM_TEXT
|
||||
],
|
||||
'formattedlawfulbases' => [
|
||||
'type' => name_description_exporter::read_properties_definition(),
|
||||
'multiple' => true
|
||||
],
|
||||
'formattedsensitivedatareasons' => [
|
||||
'type' => name_description_exporter::read_properties_definition(),
|
||||
'multiple' => true,
|
||||
'optional' => true
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return other properties.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return array
|
||||
* @throws coding_exception
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function get_other_values(renderer_base $output) {
|
||||
$values = [];
|
||||
|
||||
$formattedbases = [];
|
||||
$lawfulbases = explode(',', $this->persistent->get('lawfulbases'));
|
||||
if (!empty($lawfulbases)) {
|
||||
foreach ($lawfulbases as $basis) {
|
||||
if (empty(trim($basis))) {
|
||||
continue;
|
||||
}
|
||||
$formattedbases[] = (object)[
|
||||
'name' => get_string($basis . '_name', 'tool_dataprivacy'),
|
||||
'description' => get_string($basis . '_description', 'tool_dataprivacy')
|
||||
];
|
||||
}
|
||||
}
|
||||
$values['formattedlawfulbases'] = $formattedbases;
|
||||
|
||||
$formattedsensitivereasons = [];
|
||||
$sensitivereasons = explode(',', $this->persistent->get('sensitivedatareasons'));
|
||||
if (!empty($sensitivereasons)) {
|
||||
foreach ($sensitivereasons as $reason) {
|
||||
if (empty(trim($reason))) {
|
||||
continue;
|
||||
}
|
||||
$formattedsensitivereasons[] = (object)[
|
||||
'name' => get_string($reason . '_name', 'tool_dataprivacy'),
|
||||
'description' => get_string($reason . '_description', 'tool_dataprivacy')
|
||||
];
|
||||
}
|
||||
}
|
||||
$values['formattedsensitivedatareasons'] = $formattedsensitivereasons;
|
||||
|
||||
$retentionperiod = $this->persistent->get('retentionperiod');
|
||||
if ($retentionperiod) {
|
||||
$interval = new DateInterval($retentionperiod);
|
||||
|
||||
// It is one or another.
|
||||
if ($interval->y) {
|
||||
$formattedtime = get_string('numyears', 'moodle', $interval->format('%y'));
|
||||
} else if ($interval->m) {
|
||||
$formattedtime = get_string('nummonths', 'moodle', $interval->format('%m'));
|
||||
} else if ($interval->d) {
|
||||
$formattedtime = get_string('numdays', 'moodle', $interval->format('%d'));
|
||||
} else {
|
||||
$formattedtime = get_string('retentionperiodzero', 'tool_dataprivacy');
|
||||
}
|
||||
} else {
|
||||
$formattedtime = get_string('retentionperiodnotdefined', 'tool_dataprivacy');
|
||||
}
|
||||
$values['formattedretentionperiod'] = $formattedtime;
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This file contains the form add/update a data category.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_dataprivacy\form;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use core\form\persistent;
|
||||
|
||||
/**
|
||||
* Data category form.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class category extends persistent {
|
||||
|
||||
/**
|
||||
* @var The persistent class.
|
||||
*/
|
||||
protected static $persistentclass = 'tool_dataprivacy\\category';
|
||||
|
||||
/**
|
||||
* Define the form - called by parent constructor
|
||||
*/
|
||||
public function definition() {
|
||||
$mform = $this->_form;
|
||||
|
||||
$mform->addElement('text', 'name', get_string('name'), 'maxlength="100"');
|
||||
$mform->setType('name', PARAM_TEXT);
|
||||
$mform->addRule('name', get_string('required'), 'required', null, 'server');
|
||||
$mform->addRule('name', get_string('maximumchars', '', 100), 'maxlength', 100, 'server');
|
||||
|
||||
$mform->addElement('editor', 'description', get_string('description'), null, ['autosave' => false]);
|
||||
$mform->setType('description', PARAM_CLEANHTML);
|
||||
|
||||
if (!empty($this->_customdata['showbuttons'])) {
|
||||
if (!$this->get_persistent()->get('id')) {
|
||||
$savetext = get_string('add');
|
||||
} else {
|
||||
$savetext = get_string('savechanges');
|
||||
}
|
||||
$this->add_action_buttons(true, $savetext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This file contains the form add/update context instance data.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_dataprivacy\form;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use tool_dataprivacy\api;
|
||||
use tool_dataprivacy\data_registry;
|
||||
|
||||
/**
|
||||
* Context instance data form.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class context_instance extends \core\form\persistent {
|
||||
|
||||
/**
|
||||
* @var The persistent class.
|
||||
*/
|
||||
protected static $persistentclass = 'tool_dataprivacy\\context_instance';
|
||||
|
||||
/**
|
||||
* Define the form - called by parent constructor
|
||||
*/
|
||||
public function definition() {
|
||||
$this->_form->setDisableShortforms();
|
||||
|
||||
$this->_form->addElement('header', 'contextname', $this->_customdata['contextname']);
|
||||
|
||||
$subjectscope = implode(', ', $this->_customdata['subjectscope']);
|
||||
if (empty($subjectscope)) {
|
||||
$subjectscope = get_string('noassignedroles', 'tool_dataprivacy');
|
||||
}
|
||||
$this->_form->addElement('static', 'subjectscope', get_string('subjectscope', 'tool_dataprivacy'), $subjectscope);
|
||||
$this->_form->addHelpButton('subjectscope', 'subjectscope', 'tool_dataprivacy');
|
||||
|
||||
$this->add_purpose_category($this->_customdata['context']->contextlevel);
|
||||
|
||||
$this->_form->addElement('hidden', 'contextid');
|
||||
$this->_form->setType('contextid', PARAM_INT);
|
||||
|
||||
parent::add_action_buttons(false, get_string('savechanges'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds purpose and category selectors.
|
||||
*
|
||||
* @param int $contextlevel Apply this context level defaults. False for no defaults.
|
||||
* @return null
|
||||
*/
|
||||
protected function add_purpose_category($contextlevel = false) {
|
||||
|
||||
$mform = $this->_form;
|
||||
|
||||
$addcategorytext = $this->get_add_element_content(get_string('addcategory', 'tool_dataprivacy'));
|
||||
$categoryselect = $mform->createElement('select', 'categoryid', null, $this->_customdata['categories']);
|
||||
$addcategory = $mform->createElement('button', 'addcategory', $addcategorytext, ['data-add-element' => 'category']);
|
||||
$mform->addElement('group', 'categorygroup', get_string('category', 'tool_dataprivacy'),
|
||||
[$categoryselect, $addcategory], null, false);
|
||||
$mform->addHelpButton('categorygroup', 'category', 'tool_dataprivacy');
|
||||
$mform->setType('categoryid', PARAM_INT);
|
||||
$mform->setDefault('categoryid', 0);
|
||||
|
||||
$addpurposetext = $this->get_add_element_content(get_string('addpurpose', 'tool_dataprivacy'));
|
||||
$purposeselect = $mform->createElement('select', 'purposeid', null, $this->_customdata['purposes']);
|
||||
$addpurpose = $mform->createElement('button', 'addpurpose', $addpurposetext, ['data-add-element' => 'purpose']);
|
||||
$mform->addElement('group', 'purposegroup', get_string('purpose', 'tool_dataprivacy'),
|
||||
[$purposeselect, $addpurpose], null, false);
|
||||
$mform->addHelpButton('purposegroup', 'purpose', 'tool_dataprivacy');
|
||||
$mform->setType('purposeid', PARAM_INT);
|
||||
$mform->setDefault('purposeid', 0);
|
||||
|
||||
if (!empty($this->_customdata['currentretentionperiod'])) {
|
||||
$mform->addElement('static', 'retention_current', get_string('retentionperiod', 'tool_dataprivacy'),
|
||||
$this->_customdata['currentretentionperiod']);
|
||||
$mform->addHelpButton('retention_current', 'retentionperiod', 'tool_dataprivacy');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 'add' label.
|
||||
*
|
||||
* It depends on the theme in use.
|
||||
*
|
||||
* @param string $label
|
||||
* @return \renderable|string
|
||||
*/
|
||||
private function get_add_element_content($label) {
|
||||
global $PAGE, $OUTPUT;
|
||||
|
||||
$bs4 = false;
|
||||
|
||||
$theme = $PAGE->theme;
|
||||
if ($theme->name === 'boost') {
|
||||
$bs4 = true;
|
||||
} else {
|
||||
foreach ($theme->parents as $basetheme) {
|
||||
if ($basetheme === 'boost') {
|
||||
$bs4 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bs4) {
|
||||
return $label;
|
||||
}
|
||||
return $OUTPUT->pix_icon('e/insert', $label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the customdata array for the provided context instance.
|
||||
*
|
||||
* @param \context $context
|
||||
* @return array
|
||||
*/
|
||||
public static function get_context_instance_customdata(\context $context) {
|
||||
|
||||
$persistent = \tool_dataprivacy\context_instance::get_record_by_contextid($context->id, false);
|
||||
if (!$persistent) {
|
||||
$persistent = new \tool_dataprivacy\context_instance();
|
||||
$persistent->set('contextid', $context->id);
|
||||
}
|
||||
|
||||
$purposeoptions = \tool_dataprivacy\output\data_registry_page::purpose_options(
|
||||
api::get_purposes()
|
||||
);
|
||||
$categoryoptions = \tool_dataprivacy\output\data_registry_page::category_options(
|
||||
api::get_categories()
|
||||
);
|
||||
|
||||
$customdata = [
|
||||
'context' => $context,
|
||||
'subjectscope' => data_registry::get_subject_scope($context),
|
||||
'contextname' => $context->get_context_name(),
|
||||
'persistent' => $persistent,
|
||||
'purposes' => $purposeoptions,
|
||||
'categories' => $categoryoptions,
|
||||
];
|
||||
|
||||
$effectivepurpose = api::get_effective_context_purpose($context);
|
||||
if ($effectivepurpose) {
|
||||
|
||||
$customdata['currentretentionperiod'] = self::get_retention_display_text($effectivepurpose, $context->contextlevel,
|
||||
$context);
|
||||
|
||||
$customdata['purposeretentionperiods'] = [];
|
||||
foreach ($purposeoptions as $optionvalue => $unused) {
|
||||
// Get the effective purpose if $optionvalue would be the selected value.
|
||||
$purpose = api::get_effective_context_purpose($context, $optionvalue);
|
||||
|
||||
$retentionperiod = self::get_retention_display_text(
|
||||
$purpose,
|
||||
$context->contextlevel,
|
||||
$context
|
||||
);
|
||||
$customdata['purposeretentionperiods'][$optionvalue] = $retentionperiod;
|
||||
}
|
||||
}
|
||||
|
||||
return $customdata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the purpose display text.
|
||||
*
|
||||
* @param \tool_dataprivacy\purpose $effectivepurpose
|
||||
* @param int $retentioncontextlevel
|
||||
* @param \context $context The context, just for displaying (filters) purposes.
|
||||
* @return string
|
||||
*/
|
||||
protected static function get_retention_display_text(\tool_dataprivacy\purpose $effectivepurpose, $retentioncontextlevel, \context $context) {
|
||||
global $PAGE;
|
||||
|
||||
$renderer = $PAGE->get_renderer('tool_dataprivacy');
|
||||
|
||||
$exporter = new \tool_dataprivacy\external\purpose_exporter($effectivepurpose, ['context' => $context]);
|
||||
$exportedpurpose = $exporter->export($renderer);
|
||||
|
||||
switch ($retentioncontextlevel) {
|
||||
case CONTEXT_COURSE:
|
||||
case CONTEXT_MODULE:
|
||||
case CONTEXT_BLOCK:
|
||||
$str = get_string('effectiveretentionperiodcourse', 'tool_dataprivacy',
|
||||
$exportedpurpose->formattedretentionperiod);
|
||||
break;
|
||||
case CONTEXT_USER:
|
||||
$str = get_string('effectiveretentionperioduser', 'tool_dataprivacy',
|
||||
$exportedpurpose->formattedretentionperiod);
|
||||
break;
|
||||
default:
|
||||
$str = $exportedpurpose->formattedretentionperiod;
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This file contains the form add/update context level data.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_dataprivacy\form;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use core\form\persistent;
|
||||
use tool_dataprivacy\api;
|
||||
use tool_dataprivacy\data_registry;
|
||||
|
||||
/**
|
||||
* Context level data form.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class contextlevel extends context_instance {
|
||||
|
||||
/**
|
||||
* @var The persistent class.
|
||||
*/
|
||||
protected static $persistentclass = 'tool_dataprivacy\\contextlevel';
|
||||
|
||||
/**
|
||||
* Define the form - called by parent constructor
|
||||
*/
|
||||
public function definition() {
|
||||
$this->_form->setDisableShortforms();
|
||||
|
||||
$this->_form->addElement('header', 'contextlevelname', $this->_customdata['contextlevelname']);
|
||||
|
||||
$this->add_purpose_category();
|
||||
|
||||
$this->_form->addElement('hidden', 'contextlevel');
|
||||
$this->_form->setType('contextlevel', PARAM_INT);
|
||||
|
||||
parent::add_action_buttons(false, get_string('savechanges'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the customdata array for the provided context level.
|
||||
*
|
||||
* @param int $contextlevel
|
||||
* @return array
|
||||
*/
|
||||
public static function get_contextlevel_customdata($contextlevel) {
|
||||
|
||||
$persistent = \tool_dataprivacy\contextlevel::get_record_by_contextlevel($contextlevel, false);
|
||||
if (!$persistent) {
|
||||
$persistent = new \tool_dataprivacy\contextlevel();
|
||||
$persistent->set('contextlevel', $contextlevel);
|
||||
}
|
||||
|
||||
$includeinherit = true;
|
||||
if ($contextlevel == CONTEXT_SYSTEM) {
|
||||
// Nothing to inherit from Site level.
|
||||
$includeinherit = false;
|
||||
}
|
||||
$includenotset = true;
|
||||
if ($contextlevel == CONTEXT_SYSTEM || $contextlevel == CONTEXT_USER) {
|
||||
// No 'not set' value for system and user because we do not have defaults for them.
|
||||
$includenotset = false;
|
||||
}
|
||||
$purposeoptions = \tool_dataprivacy\output\data_registry_page::purpose_options(
|
||||
api::get_purposes(), $includenotset, $includeinherit);
|
||||
$categoryoptions = \tool_dataprivacy\output\data_registry_page::category_options(
|
||||
api::get_categories(), $includenotset, $includeinherit);
|
||||
|
||||
$customdata = [
|
||||
'contextlevel' => $contextlevel,
|
||||
'contextlevelname' => get_string('contextlevelname' . $contextlevel, 'tool_dataprivacy'),
|
||||
'persistent' => $persistent,
|
||||
'purposes' => $purposeoptions,
|
||||
'categories' => $categoryoptions,
|
||||
];
|
||||
|
||||
$effectivepurpose = api::get_effective_contextlevel_purpose($contextlevel);
|
||||
if ($effectivepurpose) {
|
||||
|
||||
$customdata['currentretentionperiod'] = self::get_retention_display_text($effectivepurpose, $contextlevel,
|
||||
\context_system::instance());
|
||||
|
||||
$customdata['purposeretentionperiods'] = [];
|
||||
foreach ($purposeoptions as $optionvalue => $unused) {
|
||||
|
||||
// Get the effective purpose if $optionvalue would be the selected value.
|
||||
list($purposeid, $unused) = data_registry::get_effective_default_contextlevel_purpose_and_category($contextlevel,
|
||||
$optionvalue);
|
||||
$purpose = new \tool_dataprivacy\purpose($purposeid);
|
||||
|
||||
$retentionperiod = self::get_retention_display_text(
|
||||
$purpose,
|
||||
$contextlevel,
|
||||
\context_system::instance()
|
||||
);
|
||||
$customdata['purposeretentionperiods'][$optionvalue] = $retentionperiod;
|
||||
}
|
||||
}
|
||||
|
||||
return $customdata;
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This file contains the defaults form.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_dataprivacy\form;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use \tool_dataprivacy\output\data_registry_page;
|
||||
|
||||
require_once($CFG->libdir . '/formslib.php');
|
||||
require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
|
||||
|
||||
/**
|
||||
* Context levels defaults form.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class defaults extends \moodleform {
|
||||
|
||||
/**
|
||||
* Define the form.
|
||||
*/
|
||||
public function definition() {
|
||||
global $OUTPUT;
|
||||
|
||||
$mform = $this->_form;
|
||||
$mform->setDisableShortforms();
|
||||
|
||||
$notification = $OUTPUT->notification(get_string('defaultsinfo', 'tool_dataprivacy'),
|
||||
\core\output\notification::NOTIFY_INFO);
|
||||
$mform->addElement('html', $notification);
|
||||
|
||||
foreach ($this->_customdata['levels'] as $level => $classname) {
|
||||
|
||||
$mform->addElement('header', $classname . '-header',
|
||||
get_string('contextlevelname' . $level, 'tool_dataprivacy'));
|
||||
|
||||
list($purposevar, $categoryvar) = \tool_dataprivacy\data_registry::var_names_from_context($classname);
|
||||
|
||||
$includeinherit = true;
|
||||
if ($level == CONTEXT_SYSTEM) {
|
||||
$includeinherit = false;
|
||||
}
|
||||
|
||||
$categoryoptions = data_registry_page::category_options($this->_customdata['categories'], false, $includeinherit);
|
||||
$purposeoptions = data_registry_page::category_options($this->_customdata['purposes'], false, $includeinherit);
|
||||
|
||||
$mform->addElement('select', $categoryvar, get_string('category', 'tool_dataprivacy'), $categoryoptions);
|
||||
$mform->addHelpButton($categoryvar, 'categorydefault', 'tool_dataprivacy');
|
||||
$mform->setType($categoryvar, PARAM_INT);
|
||||
|
||||
$mform->addElement('select', $purposevar, get_string('purpose', 'tool_dataprivacy'), $purposeoptions);
|
||||
$mform->addHelpButton($purposevar, 'purposedefault', 'tool_dataprivacy');
|
||||
$mform->setType($purposevar, PARAM_INT);
|
||||
}
|
||||
|
||||
$this->add_action_buttons(true, get_string('savechanges'));
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This file contains the form add/update a data purpose.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_dataprivacy\form;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use core\form\persistent;
|
||||
|
||||
/**
|
||||
* Data purpose form.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class purpose extends persistent {
|
||||
|
||||
/**
|
||||
* @var string The persistent class.
|
||||
*/
|
||||
protected static $persistentclass = 'tool_dataprivacy\\purpose';
|
||||
|
||||
/**
|
||||
* Define the form - called by parent constructor
|
||||
*/
|
||||
public function definition() {
|
||||
$mform = $this->_form;
|
||||
|
||||
$mform->addElement('text', 'name', get_string('name'), 'maxlength="100"');
|
||||
$mform->setType('name', PARAM_TEXT);
|
||||
$mform->addRule('name', get_string('required'), 'required', null, 'server');
|
||||
$mform->addRule('name', get_string('maximumchars', '', 100), 'maxlength', 100, 'server');
|
||||
|
||||
$mform->addElement('editor', 'description', get_string('description'), null, ['autosave' => false]);
|
||||
$mform->setType('description', PARAM_CLEANHTML);
|
||||
|
||||
// Field for selecting lawful bases (from GDPR Article 6.1).
|
||||
$lawfulbases = [];
|
||||
foreach (\tool_dataprivacy\purpose::GDPR_ART_6_1_ITEMS as $article) {
|
||||
$key = 'gdpr_art_6_1_' . $article;
|
||||
$lawfulbases[$key] = get_string($key . '_name', 'tool_dataprivacy');
|
||||
}
|
||||
$options = array(
|
||||
'multiple' => true,
|
||||
);
|
||||
$mform->addElement('autocomplete', 'lawfulbases', get_string('lawfulbases', 'tool_dataprivacy'), $lawfulbases, $options);
|
||||
$mform->addRule('lawfulbases', get_string('required'), 'required', null, 'server');
|
||||
$mform->addHelpButton('lawfulbases', 'lawfulbases', 'tool_dataprivacy');
|
||||
|
||||
// Optional field for selecting reasons for collecting sensitive personal data (from GDPR Article 9.2).
|
||||
$sensitivereasons = [];
|
||||
foreach (\tool_dataprivacy\purpose::GDPR_ART_9_2_ITEMS as $article) {
|
||||
$key = 'gdpr_art_9_2_' . $article;
|
||||
$sensitivereasons[$key] = get_string($key . '_name', 'tool_dataprivacy');
|
||||
}
|
||||
$mform->addElement('autocomplete', 'sensitivedatareasons', get_string('sensitivedatareasons', 'tool_dataprivacy'),
|
||||
$sensitivereasons, $options);
|
||||
$mform->addHelpButton('sensitivedatareasons', 'sensitivedatareasons', 'tool_dataprivacy');
|
||||
|
||||
$number = $mform->createElement('text', 'retentionperiodnumber', null, ['size' => 8]);
|
||||
$unitoptions = [
|
||||
'Y' => get_string('years'),
|
||||
'M' => strtolower(get_string('months')),
|
||||
'D' => strtolower(get_string('days'))
|
||||
];
|
||||
$unit = $mform->createElement('select', 'retentionperiodunit', '', $unitoptions);
|
||||
$mform->addGroup(['number' => $number, 'unit' => $unit], 'retentionperiod',
|
||||
get_string('retentionperiod', 'tool_dataprivacy'), null, false);
|
||||
$mform->setType('retentionperiodnumber', PARAM_INT);
|
||||
|
||||
$this->_form->addElement('advcheckbox', 'protected', get_string('protected', 'tool_dataprivacy'),
|
||||
get_string('protectedlabel', 'tool_dataprivacy'));
|
||||
|
||||
if (!empty($this->_customdata['showbuttons'])) {
|
||||
if (!$this->get_persistent()->get('id')) {
|
||||
$savetext = get_string('add');
|
||||
} else {
|
||||
$savetext = get_string('savechanges');
|
||||
}
|
||||
$this->add_action_buttons(true, $savetext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts fields.
|
||||
*
|
||||
* @param \stdClass $data
|
||||
* @return \stdClass
|
||||
*/
|
||||
protected static function convert_fields(\stdClass $data) {
|
||||
$data = parent::convert_fields($data);
|
||||
|
||||
if (is_array($data->lawfulbases)) {
|
||||
$data->lawfulbases = implode(',', $data->lawfulbases);
|
||||
}
|
||||
if (!empty($data->sensitivedatareasons) && is_array($data->sensitivedatareasons)) {
|
||||
$data->sensitivedatareasons = implode(',', $data->sensitivedatareasons);
|
||||
}
|
||||
|
||||
// A single value.
|
||||
$data->retentionperiod = 'P' . $data->retentionperiodnumber . $data->retentionperiodunit;
|
||||
unset($data->retentionperiodnumber);
|
||||
unset($data->retentionperiodunit);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default data.
|
||||
*
|
||||
* @return \stdClass
|
||||
*/
|
||||
protected function get_default_data() {
|
||||
$data = parent::get_default_data();
|
||||
|
||||
$data->lawfulbases = explode(',', $data->lawfulbases);
|
||||
if (!empty($data->sensitivedatareasons)) {
|
||||
$data->sensitivedatareasons = explode(',', $data->sensitivedatareasons);
|
||||
}
|
||||
|
||||
// Convert the single properties into number and unit.
|
||||
$strlen = strlen($data->retentionperiod);
|
||||
$data->retentionperiodnumber = substr($data->retentionperiod, 1, $strlen - 2);
|
||||
$data->retentionperiodunit = substr($data->retentionperiod, $strlen - 1);
|
||||
unset($data->retentionperiod);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Collection of helper functions for the data privacy tool.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy\local;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use coding_exception;
|
||||
use moodle_exception;
|
||||
use tool_dataprivacy\api;
|
||||
|
||||
/**
|
||||
* Class containing helper functions for the data privacy tool.
|
||||
*
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class helper {
|
||||
|
||||
/**
|
||||
* Retrieves the human-readable text value of a data request type.
|
||||
*
|
||||
* @param int $requesttype The request type.
|
||||
* @return string
|
||||
* @throws coding_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public static function get_request_type_string($requesttype) {
|
||||
switch ($requesttype) {
|
||||
case api::DATAREQUEST_TYPE_EXPORT:
|
||||
return get_string('requesttypeexport', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_TYPE_DELETE:
|
||||
return get_string('requesttypedelete', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_TYPE_OTHERS:
|
||||
return get_string('requesttypeothers', 'tool_dataprivacy');
|
||||
default:
|
||||
throw new moodle_exception('errorinvalidrequesttype', 'tool_dataprivacy');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the human-readable shortened text value of a data request type.
|
||||
*
|
||||
* @param int $requesttype The request type.
|
||||
* @return string
|
||||
* @throws coding_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public static function get_shortened_request_type_string($requesttype) {
|
||||
switch ($requesttype) {
|
||||
case api::DATAREQUEST_TYPE_EXPORT:
|
||||
return get_string('requesttypeexportshort', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_TYPE_DELETE:
|
||||
return get_string('requesttypedeleteshort', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_TYPE_OTHERS:
|
||||
return get_string('requesttypeothersshort', 'tool_dataprivacy');
|
||||
default:
|
||||
throw new moodle_exception('errorinvalidrequesttype', 'tool_dataprivacy');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the human-readable value of a data request status.
|
||||
*
|
||||
* @param int $status The request status.
|
||||
* @return string
|
||||
* @throws coding_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public static function get_request_status_string($status) {
|
||||
switch ($status) {
|
||||
case api::DATAREQUEST_STATUS_PENDING:
|
||||
return get_string('statuspending', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_STATUS_PREPROCESSING:
|
||||
return get_string('statuspreprocessing', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_STATUS_AWAITING_APPROVAL:
|
||||
return get_string('statusawaitingapproval', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_STATUS_APPROVED:
|
||||
return get_string('statusapproved', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_STATUS_PROCESSING:
|
||||
return get_string('statusprocessing', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_STATUS_COMPLETE:
|
||||
return get_string('statuscomplete', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_STATUS_CANCELLED:
|
||||
return get_string('statuscancelled', 'tool_dataprivacy');
|
||||
case api::DATAREQUEST_STATUS_REJECTED:
|
||||
return get_string('statusrejected', 'tool_dataprivacy');
|
||||
default:
|
||||
throw new moodle_exception('errorinvalidrequeststatus', 'tool_dataprivacy');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the users that a user can make data request for.
|
||||
*
|
||||
* E.g. User having a parent role and has the 'tool/dataprivacy:makedatarequestsforchildren' capability.
|
||||
* @param int $userid The user's ID.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_children_of_user($userid) {
|
||||
global $DB;
|
||||
|
||||
// Get users that the user has role assignments to.
|
||||
$allusernames = get_all_user_name_fields(true, 'u');
|
||||
$sql = "SELECT u.id, $allusernames
|
||||
FROM {role_assignments} ra, {context} c, {user} u
|
||||
WHERE ra.userid = :userid
|
||||
AND ra.contextid = c.id
|
||||
AND c.instanceid = u.id
|
||||
AND c.contextlevel = :contextlevel";
|
||||
$params = [
|
||||
'userid' => $userid,
|
||||
'contextlevel' => CONTEXT_USER
|
||||
];
|
||||
|
||||
// The final list of users that we will return;
|
||||
$finalresults = [];
|
||||
|
||||
// Our prospective list of users.
|
||||
if ($candidates = $DB->get_records_sql($sql, $params)) {
|
||||
foreach ($candidates as $key => $child) {
|
||||
$childcontext = \context_user::instance($child->id);
|
||||
if (has_capability('tool/dataprivacy:makedatarequestsforchildren', $childcontext, $userid)) {
|
||||
$finalresults[$key] = $child;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $finalresults;
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class containing helper methods for processing data requests.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Adrian Greeve
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class containing helper methods for processing data requests.
|
||||
*
|
||||
* @copyright 2018 Adrian Greeve
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class metadata_registry {
|
||||
|
||||
/**
|
||||
* Returns plugin types / plugins and the user data that it stores in a format that can be sent to a template.
|
||||
*
|
||||
* @return array An array with all of the plugin types / plugins and the user data they store.
|
||||
*/
|
||||
public function get_registry_metadata() {
|
||||
$manager = new \core_privacy\manager();
|
||||
$pluginman = \core_plugin_manager::instance();
|
||||
$contributedplugins = $this->get_contrib_list();
|
||||
$metadata = $manager->get_metadata_for_components();
|
||||
$fullyrichtree = $this->get_full_component_list();
|
||||
foreach ($fullyrichtree as $branch => $leaves) {
|
||||
$plugintype = $leaves['plugin_type'];
|
||||
$plugins = array_map(function($component) use ($manager, $metadata, $contributedplugins, $plugintype, $pluginman) {
|
||||
// Use the plugin name for the plugins, ignore for core subsystems.
|
||||
$internaldata = ($plugintype == 'core') ? ['component' => $component] :
|
||||
['component' => $pluginman->plugin_name($component)];
|
||||
$internaldata['raw_component'] = $component;
|
||||
if ($manager->component_is_compliant($component)) {
|
||||
$internaldata['compliant'] = true;
|
||||
if (isset($metadata[$component])) {
|
||||
$collection = $metadata[$component]->get_collection();
|
||||
$internaldata = $this->format_metadata($collection, $component, $internaldata);
|
||||
} else if ($manager->is_empty_subsystem($component)) {
|
||||
// This is an unused subsystem.
|
||||
// Use the generic string.
|
||||
$internaldata['nullprovider'] = get_string('privacy:subsystem:empty', 'core_privacy');
|
||||
} else {
|
||||
// Call get_reason for null provider.
|
||||
$internaldata['nullprovider'] = get_string($manager->get_null_provider_reason($component), $component);
|
||||
}
|
||||
} else {
|
||||
$internaldata['compliant'] = false;
|
||||
}
|
||||
// Check to see if we are an external plugin.
|
||||
$componentshortname = explode('_', $component);
|
||||
$shortname = array_pop($componentshortname);
|
||||
if (isset($contributedplugins[$plugintype][$shortname])) {
|
||||
$internaldata['external'] = true;
|
||||
}
|
||||
return $internaldata;
|
||||
}, $leaves['plugins']);
|
||||
$fullyrichtree[$branch]['plugin_type_raw'] = $plugintype;
|
||||
// We're done using the plugin type. Convert it to a readable string.
|
||||
$fullyrichtree[$branch]['plugin_type'] = $pluginman->plugintype_name($plugintype);
|
||||
$fullyrichtree[$branch]['plugins'] = $plugins;
|
||||
}
|
||||
return $fullyrichtree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the metadata for use with a template.
|
||||
*
|
||||
* @param array $collection The collection associated with the component that we want to expand and format.
|
||||
* @param string $component The component that we are dealing in
|
||||
* @param array $internaldata The array to add the formatted metadata to.
|
||||
* @return array The internal data array with the formatted metadata.
|
||||
*/
|
||||
protected function format_metadata($collection, $component, $internaldata) {
|
||||
foreach ($collection as $collectioninfo) {
|
||||
$privacyfields = $collectioninfo->get_privacy_fields();
|
||||
$fields = '';
|
||||
if (!empty($privacyfields)) {
|
||||
$fields = array_map(function($key, $field) use ($component) {
|
||||
return [
|
||||
'field_name' => $key,
|
||||
'field_summary' => get_string($field, $component)
|
||||
];
|
||||
}, array_keys($privacyfields), $privacyfields);
|
||||
}
|
||||
// Can the metadata types be located somewhere else besides core?
|
||||
$items = explode('\\', get_class($collectioninfo));
|
||||
$type = array_pop($items);
|
||||
$typedata = [
|
||||
'name' => $collectioninfo->get_name(),
|
||||
'type' => $type,
|
||||
'fields' => $fields,
|
||||
'summary' => get_string($collectioninfo->get_summary(), $component)
|
||||
];
|
||||
if (strpos($type, 'subsystem_link') === 0 || strpos($type, 'plugintype_link') === 0) {
|
||||
$typedata['link'] = true;
|
||||
}
|
||||
$internaldata['metadata'][] = $typedata;
|
||||
}
|
||||
return $internaldata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full list of components.
|
||||
*
|
||||
* @return array An array of plugin types which contain plugin data.
|
||||
*/
|
||||
protected function get_full_component_list() {
|
||||
global $CFG;
|
||||
|
||||
$list = \core_component::get_component_list();
|
||||
$list['core']['core'] = "{$CFG->dirroot}/lib";
|
||||
$formattedlist = [];
|
||||
foreach ($list as $plugintype => $plugin) {
|
||||
$formattedlist[] = ['plugin_type' => $plugintype, 'plugins' => array_keys($plugin)];
|
||||
}
|
||||
|
||||
return $formattedlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of contributed plugins installed on the system.
|
||||
*
|
||||
* @return array A list of contributed plugins installed.
|
||||
*/
|
||||
protected function get_contrib_list() {
|
||||
return array_map(function($plugins) {
|
||||
return array_filter($plugins, function($plugindata) {
|
||||
return !$plugindata->is_standard();
|
||||
});
|
||||
}, \core_plugin_manager::instance()->get_plugins());
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Categories renderable.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy\output;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
use tool_dataprivacy\external\category_exporter;
|
||||
|
||||
/**
|
||||
* Class containing the categories page renderable.
|
||||
*
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class categories extends crud_element implements renderable, templatable {
|
||||
|
||||
/** @var array $categories All system categories. */
|
||||
protected $categories = [];
|
||||
|
||||
/**
|
||||
* Construct this renderable.
|
||||
*
|
||||
* @param \tool_dataprivacy\category[] $categories
|
||||
*/
|
||||
public function __construct($categories) {
|
||||
$this->categories = $categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this data so it can be used as the context for a mustache template.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
global $PAGE;
|
||||
|
||||
$context = \context_system::instance();
|
||||
|
||||
$PAGE->requires->js_call_amd('tool_dataprivacy/categoriesactions', 'init');
|
||||
$PAGE->requires->js_call_amd('tool_dataprivacy/add_category', 'getInstance', [$context->id]);
|
||||
|
||||
$data = new stdClass();
|
||||
|
||||
// Navigation links.
|
||||
$data->navigation = [];
|
||||
$navigationlinks = $this->get_navigation();
|
||||
foreach ($navigationlinks as $navlink) {
|
||||
$data->navigation[] = $navlink->export_for_template($output);
|
||||
}
|
||||
|
||||
$data->categories = [];
|
||||
foreach ($this->categories as $category) {
|
||||
$exporter = new category_exporter($category, ['context' => \context_system::instance()]);
|
||||
$exportedcategory = $exporter->export($output);
|
||||
|
||||
$actionmenu = $this->action_menu('category', $exportedcategory, $category);
|
||||
$exportedcategory->actions = $actionmenu->export_for_template($output);
|
||||
$data->categories[] = $exportedcategory;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Abstract renderer for independent renderable elements.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy\output;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
use tool_dataprivacy\external\purpose_exporter;
|
||||
use tool_dataprivacy\external\category_exporter;
|
||||
|
||||
/**
|
||||
* Abstract renderer for independent renderable elements.
|
||||
*
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class crud_element {
|
||||
|
||||
/**
|
||||
* Returns the top navigation buttons.
|
||||
*
|
||||
* @return \action_link[]
|
||||
*/
|
||||
protected final function get_navigation() {
|
||||
$back = new \action_link(
|
||||
new \moodle_url('/admin/tool/dataprivacy/dataregistry.php'),
|
||||
get_string('back'),
|
||||
null,
|
||||
['class' => 'btn btn-default']
|
||||
);
|
||||
return [$back];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an action menu for the provided element
|
||||
*
|
||||
* @param string $elementname 'purpose' or 'category'.
|
||||
* @param \stdClass $exported
|
||||
* @param \core\persistent $persistent
|
||||
* @return \action_menu
|
||||
*/
|
||||
protected final function action_menu($elementname, $exported, $persistent) {
|
||||
|
||||
// Just in case, we are doing funny stuff below.
|
||||
$elementname = clean_param($elementname, PARAM_ALPHA);
|
||||
|
||||
// Actions.
|
||||
$actionmenu = new \action_menu();
|
||||
$actionmenu->set_menu_trigger(get_string('actions'));
|
||||
$actionmenu->set_owner_selector($elementname . '-' . $exported->id . '-actions');
|
||||
$actionmenu->set_alignment(\action_menu::TL, \action_menu::BL);
|
||||
|
||||
$url = new \moodle_url('/admin/tool/dataprivacy/edit' . $elementname . '.php',
|
||||
['id' => $exported->id]);
|
||||
$link = new \action_menu_link_secondary($url, new \pix_icon('t/edit',
|
||||
get_string('edit')), get_string('edit'));
|
||||
$actionmenu->add($link);
|
||||
|
||||
if (!$persistent->is_used()) {
|
||||
$url = new \moodle_url('#');
|
||||
$attrs = ['data-id' => $exported->id, 'data-action' => 'delete' . $elementname, 'data-name' => $exported->name];
|
||||
$link = new \action_menu_link_secondary($url, new \pix_icon('t/delete',
|
||||
get_string('delete')), get_string('delete'), $attrs);
|
||||
$actionmenu->add($link);
|
||||
}
|
||||
|
||||
return $actionmenu;
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class containing data for a user's data requests.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy\output;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use coding_exception;
|
||||
use dml_exception;
|
||||
use moodle_exception;
|
||||
use moodle_url;
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use single_select;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
use tool_dataprivacy\data_request;
|
||||
use tool_dataprivacy\output\expired_contexts_table;
|
||||
|
||||
/**
|
||||
* Class containing data for a user's data requests.
|
||||
*
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class data_deletion_page implements renderable, templatable {
|
||||
|
||||
/** The default number of results to be shown per page. */
|
||||
const DEFAULT_PAGE_SIZE = 20;
|
||||
|
||||
/** @var data_request[] $requests List of data requests. */
|
||||
protected $filter = null;
|
||||
|
||||
/** @var data_request[] $requests List of data requests. */
|
||||
protected $expiredcontextstable = [];
|
||||
|
||||
/**
|
||||
* Construct this renderable.
|
||||
*
|
||||
* @param \tool_dataprivacy\data_request[] $filter
|
||||
* @param \tool_dataprivacy\expired_contexts_table $expiredcontextstable
|
||||
*/
|
||||
public function __construct($filter, expired_contexts_table $expiredcontextstable) {
|
||||
$this->filter = $filter;
|
||||
$this->expiredcontextstable = $expiredcontextstable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this data so it can be used as the context for a mustache template.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
* @throws coding_exception
|
||||
* @throws dml_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
$data = new stdClass();
|
||||
|
||||
$url = new moodle_url('/admin/tool/dataprivacy/datadeletion.php');
|
||||
$options = [
|
||||
CONTEXT_USER => get_string('user'),
|
||||
CONTEXT_COURSE => get_string('course'),
|
||||
CONTEXT_MODULE => get_string('activitiesandresources', 'tool_dataprivacy'),
|
||||
CONTEXT_BLOCK => get_string('blocks'),
|
||||
];
|
||||
$filterselector = new single_select($url, 'filter', $options, $this->filter, null);
|
||||
$data->filter = $filterselector->export_for_template($output);
|
||||
|
||||
ob_start();
|
||||
$this->expiredcontextstable->out(self::DEFAULT_PAGE_SIZE, true);
|
||||
$expiredcontexts = ob_get_contents();
|
||||
ob_end_clean();
|
||||
$data->expiredcontexts = $expiredcontexts;
|
||||
|
||||
$data->existingcontexts = $this->expiredcontextstable->rawdata ? true : false;
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contains the data registry compliance renderable.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Adrian Greeve
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy\output;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use templatable;
|
||||
|
||||
/**
|
||||
* Class containing the data registry compliance renderable
|
||||
*
|
||||
* @copyright 2018 Adrian Greeve
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class data_registry_compliance_page implements renderable, templatable {
|
||||
|
||||
/** @var array meta-data to be displayed about the system. */
|
||||
protected $metadata;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $metadata
|
||||
*/
|
||||
public function __construct($metadata) {
|
||||
$this->metadata = $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this data so it can be used as the context for a mustache template.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
return ['types' => $this->metadata];
|
||||
}
|
||||
}
|
||||
@@ -1,481 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Data registry renderable.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy\output;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
use tool_dataprivacy\data_registry;
|
||||
|
||||
require_once($CFG->libdir . '/coursecatlib.php');
|
||||
require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/dataprivacy/lib.php');
|
||||
require_once($CFG->libdir . '/blocklib.php');
|
||||
|
||||
/**
|
||||
* Class containing the data registry renderable
|
||||
*
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class data_registry_page implements renderable, templatable {
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $defaultcontextlevel;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $defaultcontextid;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param int $defaultcontextlevel
|
||||
* @param int $defaultcontextid
|
||||
* @return null
|
||||
*/
|
||||
public function __construct($defaultcontextlevel = false, $defaultcontextid = false) {
|
||||
$this->defaultcontextlevel = $defaultcontextlevel;
|
||||
$this->defaultcontextid = $defaultcontextid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this data so it can be used as the context for a mustache template.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
global $PAGE;
|
||||
|
||||
$params = [\context_system::instance()->id, $this->defaultcontextlevel, $this->defaultcontextid];
|
||||
$PAGE->requires->js_call_amd('tool_dataprivacy/data_registry', 'init', $params);
|
||||
|
||||
$data = new stdClass();
|
||||
$defaultsbutton = new \action_link(
|
||||
new \moodle_url('/admin/tool/dataprivacy/defaults.php'),
|
||||
get_string('setdefaults', 'tool_dataprivacy'),
|
||||
null,
|
||||
['class' => 'btn btn-default']
|
||||
);
|
||||
$data->defaultsbutton = $defaultsbutton->export_for_template($output);
|
||||
|
||||
$actionmenu = new \action_menu();
|
||||
$actionmenu->set_menu_trigger(get_string('edit'), 'btn btn-default');
|
||||
$actionmenu->set_owner_selector('dataregistry-actions');
|
||||
$actionmenu->set_alignment(\action_menu::TL, \action_menu::BL);
|
||||
|
||||
$url = new \moodle_url('/admin/tool/dataprivacy/categories.php');
|
||||
$categories = new \action_menu_link_secondary($url, null, get_string('categories', 'tool_dataprivacy'));
|
||||
$actionmenu->add($categories);
|
||||
|
||||
$url = new \moodle_url('/admin/tool/dataprivacy/purposes.php');
|
||||
$purposes = new \action_menu_link_secondary($url, null, get_string('purposes', 'tool_dataprivacy'));
|
||||
$actionmenu->add($purposes);
|
||||
|
||||
$data->actions = $actionmenu->export_for_template($output);
|
||||
|
||||
if (!data_registry::defaults_set()) {
|
||||
$data->info = (object)[
|
||||
'message' => get_string('dataregistryinfo', 'tool_dataprivacy'),
|
||||
'announce' => 1
|
||||
];
|
||||
$data->nosystemdefaults = (object)[
|
||||
'message' => get_string('nosystemdefaults', 'tool_dataprivacy'),
|
||||
'announce' => 1
|
||||
];
|
||||
}
|
||||
|
||||
$data->tree = $this->get_default_tree_structure();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tree default structure.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_default_tree_structure() {
|
||||
|
||||
$frontpage = \context_course::instance(SITEID);
|
||||
|
||||
$categorybranches = $this->get_all_category_branches();
|
||||
|
||||
$elements = [
|
||||
'text' => get_string('contextlevelname' . CONTEXT_SYSTEM, 'tool_dataprivacy'),
|
||||
'contextlevel' => CONTEXT_SYSTEM,
|
||||
'branches' => [
|
||||
[
|
||||
'text' => get_string('user'),
|
||||
'contextlevel' => CONTEXT_USER,
|
||||
], [
|
||||
'text' => get_string('categories'),
|
||||
'branches' => $categorybranches,
|
||||
'expandelement' => 'category',
|
||||
], [
|
||||
'text' => get_string('frontpagecourse', 'tool_dataprivacy'),
|
||||
'contextid' => $frontpage->id,
|
||||
'branches' => [
|
||||
[
|
||||
'text' => get_string('activitiesandresources', 'tool_dataprivacy'),
|
||||
'expandcontextid' => $frontpage->id,
|
||||
'expandelement' => 'module',
|
||||
'expanded' => 0,
|
||||
], [
|
||||
'text' => get_string('blocks'),
|
||||
'expandcontextid' => $frontpage->id,
|
||||
'expandelement' => 'block',
|
||||
'expanded' => 0,
|
||||
],
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// Returned as an array to follow a common array format.
|
||||
return [self::complete($elements, $this->defaultcontextlevel, $this->defaultcontextid)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hierarchy of system course categories.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_all_category_branches() {
|
||||
|
||||
$categories = data_registry::get_site_categories();
|
||||
|
||||
$categoriesbranch = [];
|
||||
while (count($categories) > 0) {
|
||||
foreach ($categories as $key => $category) {
|
||||
|
||||
$context = \context_coursecat::instance($category->id);
|
||||
$newnode = [
|
||||
'text' => shorten_text(format_string($category->name, true, ['context' => $context])),
|
||||
'categoryid' => $category->id,
|
||||
'contextid' => $context->id,
|
||||
];
|
||||
if ($category->coursecount > 0) {
|
||||
$newnode['branches'] = [
|
||||
[
|
||||
'text' => get_string('courses'),
|
||||
'expandcontextid' => $context->id,
|
||||
'expandelement' => 'course',
|
||||
'expanded' => 0,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
$added = false;
|
||||
if ($category->parent == 0) {
|
||||
// New categories root-level node.
|
||||
$categoriesbranch[] = $newnode;
|
||||
$added = true;
|
||||
|
||||
} else {
|
||||
// Add the new node under the appropriate parent.
|
||||
if ($this->add_to_parent_category_branch($category, $newnode, $categoriesbranch)) {
|
||||
$added = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($added) {
|
||||
unset($categories[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $categoriesbranch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the courses branch for the provided category.
|
||||
*
|
||||
* @param \context $catcontext
|
||||
* @return array
|
||||
*/
|
||||
public static function get_courses_branch(\context $catcontext) {
|
||||
|
||||
if ($catcontext->contextlevel !== CONTEXT_COURSECAT) {
|
||||
throw new \coding_exception('A course category context should be provided');
|
||||
}
|
||||
|
||||
$coursecat = \coursecat::get($catcontext->instanceid);
|
||||
$courses = $coursecat->get_courses();
|
||||
|
||||
$branches = [];
|
||||
|
||||
foreach ($courses as $course) {
|
||||
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
|
||||
if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$coursenode = [
|
||||
'text' => shorten_text(format_string($course->shortname, true, ['context' => $coursecontext])),
|
||||
'contextid' => $coursecontext->id,
|
||||
'branches' => [
|
||||
[
|
||||
'text' => get_string('activitiesandresources', 'tool_dataprivacy'),
|
||||
'expandcontextid' => $coursecontext->id,
|
||||
'expandelement' => 'module',
|
||||
'expanded' => 0,
|
||||
], [
|
||||
'text' => get_string('blocks'),
|
||||
'expandcontextid' => $coursecontext->id,
|
||||
'expandelement' => 'block',
|
||||
'expanded' => 0,
|
||||
],
|
||||
]
|
||||
];
|
||||
$branches[] = self::complete($coursenode);
|
||||
}
|
||||
|
||||
return $branches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the modules branch for the provided course.
|
||||
*
|
||||
* @param \context $coursecontext
|
||||
* @return array
|
||||
*/
|
||||
public static function get_modules_branch(\context $coursecontext) {
|
||||
|
||||
if ($coursecontext->contextlevel !== CONTEXT_COURSE) {
|
||||
throw new \coding_exception('A course context should be provided');
|
||||
}
|
||||
|
||||
$branches = [];
|
||||
|
||||
// Using the current user.
|
||||
$modinfo = get_fast_modinfo($coursecontext->instanceid);
|
||||
foreach ($modinfo->get_instances() as $moduletype => $instances) {
|
||||
foreach ($instances as $cm) {
|
||||
|
||||
if (!$cm->uservisible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$a = (object)[
|
||||
'instancename' => shorten_text($cm->get_formatted_name()),
|
||||
'modulename' => get_string('pluginname', 'mod_' . $moduletype),
|
||||
];
|
||||
|
||||
$text = get_string('moduleinstancename', 'tool_dataprivacy', $a);
|
||||
$branches[] = self::complete([
|
||||
'text' => $text,
|
||||
'contextid' => $cm->context->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $branches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the blocks branch for the provided course.
|
||||
*
|
||||
* @param \context $coursecontext
|
||||
* @return null
|
||||
*/
|
||||
public static function get_blocks_branch(\context $coursecontext) {
|
||||
global $DB;
|
||||
|
||||
if ($coursecontext->contextlevel !== CONTEXT_COURSE) {
|
||||
throw new \coding_exception('A course context should be provided');
|
||||
}
|
||||
|
||||
$branches = [];
|
||||
|
||||
$children = $coursecontext->get_child_contexts();
|
||||
foreach ($children as $childcontext) {
|
||||
|
||||
if ($childcontext->contextlevel !== CONTEXT_BLOCK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$blockinstance = block_instance_by_id($childcontext->instanceid);
|
||||
$displayname = shorten_text(format_string($blockinstance->get_title(), true, ['context' => $childcontext]));
|
||||
$branches[] = self::complete([
|
||||
'text' => $displayname,
|
||||
'contextid' => $childcontext->id,
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
return $branches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the provided category to the categories branch.
|
||||
*
|
||||
* @param stdClass $category
|
||||
* @param array $newnode
|
||||
* @param array $categoriesbranch
|
||||
* @return bool
|
||||
*/
|
||||
private function add_to_parent_category_branch($category, $newnode, &$categoriesbranch) {
|
||||
|
||||
foreach ($categoriesbranch as $key => $branch) {
|
||||
if (!empty($branch['categoryid']) && $branch['categoryid'] == $category->parent) {
|
||||
// It may be empty (if it does not contain courses and this is the first child cat).
|
||||
if (!isset($categoriesbranch[$key]['branches'])) {
|
||||
$categoriesbranch[$key]['branches'] = [];
|
||||
}
|
||||
$categoriesbranch[$key]['branches'][] = $newnode;
|
||||
return true;
|
||||
}
|
||||
if (!empty($branch['branches'])) {
|
||||
$parent = $this->add_to_parent_category_branch($category, $newnode, $categoriesbranch[$key]['branches']);
|
||||
if ($parent) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes tree nodes with default values.
|
||||
*
|
||||
* @param array $node
|
||||
* @param int|false $currentcontextlevel
|
||||
* @param int|false $currentcontextid
|
||||
* @return array
|
||||
*/
|
||||
private static function complete($node, $currentcontextlevel = false, $currentcontextid = false) {
|
||||
if (!isset($node['active'])) {
|
||||
if ($currentcontextlevel && !empty($node['contextlevel']) &&
|
||||
$currentcontextlevel == $node['contextlevel'] &&
|
||||
empty($currentcontextid)) {
|
||||
// This is the active context level, we also checked that there
|
||||
// is no default contextid set.
|
||||
$node['active'] = true;
|
||||
} else if ($currentcontextid && !empty($node['contextid']) &&
|
||||
$currentcontextid == $node['contextid']) {
|
||||
$node['active'] = true;
|
||||
} else {
|
||||
$node['active'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($node['branches'])) {
|
||||
$node['branches'] = [];
|
||||
} else {
|
||||
foreach ($node['branches'] as $key => $childnode) {
|
||||
$node['branches'][$key] = self::complete($childnode, $currentcontextlevel, $currentcontextid);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($node['expandelement'])) {
|
||||
$node['expandelement'] = null;
|
||||
}
|
||||
|
||||
if (!isset($node['expandcontextid'])) {
|
||||
$node['expandcontextid'] = null;
|
||||
}
|
||||
|
||||
if (!isset($node['contextid'])) {
|
||||
$node['contextid'] = null;
|
||||
}
|
||||
|
||||
if (!isset($node['contextlevel'])) {
|
||||
$node['contextlevel'] = null;
|
||||
}
|
||||
|
||||
if (!isset($node['expanded'])) {
|
||||
if (!empty($node['branches'])) {
|
||||
$node['expanded'] = 1;
|
||||
} else {
|
||||
$node['expanded'] = 0;
|
||||
}
|
||||
}
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* From a list of purpose persistents to a list of id => name purposes.
|
||||
*
|
||||
* @param \tool_dataprivacy\purpose $purposes
|
||||
* @param bool $includenotset
|
||||
* @param bool $includeinherit
|
||||
* @return string[]
|
||||
*/
|
||||
public static function purpose_options($purposes, $includenotset = true, $includeinherit = true) {
|
||||
$options = self::base_options($includenotset, $includeinherit);
|
||||
foreach ($purposes as $purpose) {
|
||||
$options[$purpose->get('id')] = $purpose->get('name');
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* From a list of category persistents to a list of id => name categories.
|
||||
*
|
||||
* @param \tool_dataprivacy\category $categories
|
||||
* @param bool $includenotset
|
||||
* @param bool $includeinherit
|
||||
* @return string[]
|
||||
*/
|
||||
public static function category_options($categories, $includenotset = true, $includeinherit = true) {
|
||||
$options = self::base_options($includenotset, $includeinherit);
|
||||
foreach ($categories as $category) {
|
||||
$options[$category->get('id')] = $category->get('name');
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base not set and inherit options.
|
||||
*
|
||||
* @param bool $includenotset
|
||||
* @param bool $includeinherit
|
||||
* @return array
|
||||
*/
|
||||
private static function base_options($includenotset = true, $includeinherit = true) {
|
||||
|
||||
$options = [];
|
||||
|
||||
if ($includenotset) {
|
||||
$options[\tool_dataprivacy\context_instance::NOTSET] = get_string('notset', 'tool_dataprivacy');
|
||||
}
|
||||
|
||||
if ($includeinherit) {
|
||||
$options[\tool_dataprivacy\context_instance::INHERIT] = get_string('inherit', 'tool_dataprivacy');
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class containing data for a user's data requests.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy\output;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use action_menu;
|
||||
use action_menu_link_secondary;
|
||||
use coding_exception;
|
||||
use context_system;
|
||||
use dml_exception;
|
||||
use moodle_exception;
|
||||
use moodle_url;
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
use tool_dataprivacy\api;
|
||||
use tool_dataprivacy\data_request;
|
||||
use tool_dataprivacy\external\data_request_exporter;
|
||||
|
||||
/**
|
||||
* Class containing data for a user's data requests.
|
||||
*
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class data_requests_page implements renderable, templatable {
|
||||
|
||||
/** @var data_request[] $requests List of data requests. */
|
||||
protected $requests = [];
|
||||
|
||||
/**
|
||||
* Construct this renderable.
|
||||
*
|
||||
* @param data_request[] $requests
|
||||
*/
|
||||
public function __construct($requests) {
|
||||
$this->requests = $requests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this data so it can be used as the context for a mustache template.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
* @throws coding_exception
|
||||
* @throws dml_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
$data = new stdClass();
|
||||
$data->newdatarequesturl = new moodle_url('/admin/tool/dataprivacy/createdatarequest.php');
|
||||
$data->newdatarequesturl->param('manage', true);
|
||||
|
||||
if (!is_https()) {
|
||||
$httpwarningmessage = get_string('httpwarning', 'tool_dataprivacy');
|
||||
$data->httpsite = array('message' => $httpwarningmessage, 'announce' => 1);
|
||||
}
|
||||
|
||||
$requests = [];
|
||||
foreach ($this->requests as $request) {
|
||||
$requestid = $request->get('id');
|
||||
$status = $request->get('status');
|
||||
$requestexporter = new data_request_exporter($request, ['context' => context_system::instance()]);
|
||||
$item = $requestexporter->export($output);
|
||||
|
||||
// Prepare actions.
|
||||
$actions = [];
|
||||
|
||||
// View action.
|
||||
$actionurl = new moodle_url('#');
|
||||
$actiondata = ['data-action' => 'view', 'data-requestid' => $requestid];
|
||||
$actiontext = get_string('viewrequest', 'tool_dataprivacy');
|
||||
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
|
||||
|
||||
if ($status == api::DATAREQUEST_STATUS_AWAITING_APPROVAL) {
|
||||
// Approve.
|
||||
$actiondata['data-action'] = 'approve';
|
||||
$actiontext = get_string('approverequest', 'tool_dataprivacy');
|
||||
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
|
||||
|
||||
// Deny.
|
||||
$actiondata['data-action'] = 'deny';
|
||||
$actiontext = get_string('denyrequest', 'tool_dataprivacy');
|
||||
$actions[] = new action_menu_link_secondary($actionurl, null, $actiontext, $actiondata);
|
||||
}
|
||||
|
||||
$actionsmenu = new action_menu($actions);
|
||||
$actionsmenu->set_menu_trigger(get_string('actions'));
|
||||
$actionsmenu->set_owner_selector('request-actions-' . $requestid);
|
||||
$actionsmenu->set_alignment(\action_menu::TL, \action_menu::BL);
|
||||
$item->actions = $actionsmenu->export_for_template($output);
|
||||
|
||||
$requests[] = $item;
|
||||
}
|
||||
$data->requests = $requests;
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Contains the class used for the displaying the expired contexts table.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta <jun@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_dataprivacy\output;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/tablelib.php');
|
||||
|
||||
use coding_exception;
|
||||
use context_helper;
|
||||
use dml_exception;
|
||||
use Exception;
|
||||
use html_writer;
|
||||
use pix_icon;
|
||||
use stdClass;
|
||||
use table_sql;
|
||||
use tool_dataprivacy\api;
|
||||
use tool_dataprivacy\expired_context;
|
||||
use tool_dataprivacy\external\purpose_exporter;
|
||||
use tool_dataprivacy\purpose;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
/**
|
||||
* The class for displaying the expired contexts table.
|
||||
*
|
||||
* @copyright 2018 Jun Pataleta <jun@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class expired_contexts_table extends table_sql {
|
||||
|
||||
/** @var int The context level acting as a filter for this table. */
|
||||
protected $contextlevel = null;
|
||||
|
||||
/**
|
||||
* @var bool $selectall Has the user selected all users on the page? True by default.
|
||||
*/
|
||||
protected $selectall = true;
|
||||
|
||||
/** @var purpose[] Array of purposes mapped to the contexts. */
|
||||
protected $purposes = [];
|
||||
|
||||
/**
|
||||
* expired_contexts_table constructor.
|
||||
*
|
||||
* @param int|null $contextlevel
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function __construct($contextlevel = null) {
|
||||
parent::__construct('expired-contexts-table');
|
||||
|
||||
$this->contextlevel = $contextlevel;
|
||||
|
||||
$columnheaders = [
|
||||
'name' => get_string('name'),
|
||||
'info' => get_string('info'),
|
||||
'purpose' => get_string('purpose', 'tool_dataprivacy'),
|
||||
'category' => get_string('category', 'tool_dataprivacy'),
|
||||
'retentionperiod' => get_string('retentionperiod', 'tool_dataprivacy'),
|
||||
'timecreated' => get_string('expiry', 'tool_dataprivacy'),
|
||||
];
|
||||
$checkboxattrs = [
|
||||
'title' => get_string('selectall'),
|
||||
'data-action' => 'selectall'
|
||||
];
|
||||
$columnheaders['select'] = html_writer::checkbox('selectall', 1, true, null, $checkboxattrs);
|
||||
|
||||
$this->define_columns(array_keys($columnheaders));
|
||||
$this->define_headers(array_values($columnheaders));
|
||||
$this->no_sorting('name');
|
||||
$this->no_sorting('select');
|
||||
$this->no_sorting('info');
|
||||
$this->no_sorting('purpose');
|
||||
$this->no_sorting('category');
|
||||
$this->no_sorting('retentionperiod');
|
||||
|
||||
// Make this table sorted by first name by default.
|
||||
$this->sortable(true, 'timecreated');
|
||||
}
|
||||
|
||||
/**
|
||||
* The context name column.
|
||||
*
|
||||
* @param stdClass $data The row data.
|
||||
* @return string
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function col_name($data) {
|
||||
global $OUTPUT;
|
||||
$context = context_helper::instance_by_id($data->contextid);
|
||||
$parent = $context->get_parent_context();
|
||||
$contextdata = (object)[
|
||||
'name' => $context->get_context_name(false, true),
|
||||
'parent' => $parent->get_context_name(false, true),
|
||||
];
|
||||
$fullcontexts = $context->get_parent_contexts(true);
|
||||
$contextsinpath = [];
|
||||
foreach ($fullcontexts as $contextinpath) {
|
||||
$contextsinpath[] = $contextinpath->get_context_name(false, true);
|
||||
}
|
||||
$infoicon = new pix_icon('i/info', implode(' / ', array_reverse($contextsinpath)));
|
||||
$infoiconhtml = $OUTPUT->render($infoicon);
|
||||
$name = html_writer::span(get_string('nameandparent', 'tool_dataprivacy', $contextdata), 'm-r-1');
|
||||
|
||||
return $name . $infoiconhtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* The context information column.
|
||||
*
|
||||
* @param stdClass $data The row data.
|
||||
* @return string
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function col_info($data) {
|
||||
global $OUTPUT;
|
||||
|
||||
$context = context_helper::instance_by_id($data->contextid);
|
||||
|
||||
$children = $context->get_child_contexts();
|
||||
if (empty($children)) {
|
||||
return get_string('none');
|
||||
} else {
|
||||
$childnames = [];
|
||||
foreach ($children as $child) {
|
||||
$childnames[] = $child->get_context_name(false, true);
|
||||
}
|
||||
$infoicon = new pix_icon('i/info', implode(', ', $childnames));
|
||||
$infoiconhtml = $OUTPUT->render($infoicon);
|
||||
$name = html_writer::span(get_string('nchildren', 'tool_dataprivacy', count($children)), 'm-r-1');
|
||||
|
||||
return $name . $infoiconhtml;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The category name column.
|
||||
*
|
||||
* @param stdClass $data The row data.
|
||||
* @return mixed
|
||||
* @throws coding_exception
|
||||
* @throws dml_exception
|
||||
*/
|
||||
public function col_category($data) {
|
||||
$context = context_helper::instance_by_id($data->contextid);
|
||||
$category = api::get_effective_context_category($context);
|
||||
|
||||
return s($category->get('name'));
|
||||
}
|
||||
|
||||
/**
|
||||
* The purpose column.
|
||||
*
|
||||
* @param stdClass $data The row data.
|
||||
* @return string
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function col_purpose($data) {
|
||||
$purpose = $this->purposes[$data->contextid];
|
||||
|
||||
return s($purpose->get('name'));
|
||||
}
|
||||
|
||||
/**
|
||||
* The retention period column.
|
||||
*
|
||||
* @param stdClass $data The row data.
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function col_retentionperiod($data) {
|
||||
global $PAGE;
|
||||
|
||||
$purpose = $this->purposes[$data->contextid];
|
||||
|
||||
$exporter = new purpose_exporter($purpose, ['context' => \context_system::instance()]);
|
||||
$exportedpurpose = $exporter->export($PAGE->get_renderer('core'));
|
||||
|
||||
return $exportedpurpose->formattedretentionperiod;
|
||||
}
|
||||
|
||||
/**
|
||||
* The timecreated a.k.a. the context expiry date column.
|
||||
*
|
||||
* @param stdClass $data The row data.
|
||||
* @return string
|
||||
*/
|
||||
public function col_timecreated($data) {
|
||||
return userdate($data->timecreated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the select column.
|
||||
*
|
||||
* @param stdClass $data The row data.
|
||||
* @return string
|
||||
*/
|
||||
public function col_select($data) {
|
||||
$id = $data->id;
|
||||
return html_writer::checkbox('expiredcontext_' . $id, $id, $this->selectall, '', ['class' => 'selectcontext']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the database for results to display in the table.
|
||||
*
|
||||
* @param int $pagesize size of page for paginated displayed table.
|
||||
* @param bool $useinitialsbar do you want to use the initials bar.
|
||||
* @throws dml_exception
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function query_db($pagesize, $useinitialsbar = true) {
|
||||
// Only count expired contexts that are awaiting confirmation.
|
||||
$total = expired_context::get_record_count_by_contextlevel($this->contextlevel, expired_context::STATUS_EXPIRED);
|
||||
$this->pagesize($pagesize, $total);
|
||||
|
||||
$sort = $this->get_sql_sort();
|
||||
if (empty($sort)) {
|
||||
$sort = 'timecreated';
|
||||
}
|
||||
|
||||
// Only load expired contexts that are awaiting confirmation.
|
||||
$expiredcontexts = expired_context::get_records_by_contextlevel($this->contextlevel, expired_context::STATUS_EXPIRED,
|
||||
$sort, $this->get_page_start(), $this->get_page_size());
|
||||
$this->rawdata = [];
|
||||
foreach ($expiredcontexts as $persistent) {
|
||||
$data = $persistent->to_record();
|
||||
|
||||
$context = context_helper::instance_by_id($data->contextid);
|
||||
|
||||
$purpose = api::get_effective_context_purpose($context);
|
||||
$this->purposes[$data->contextid] = $purpose;
|
||||
$this->rawdata[] = $data;
|
||||
}
|
||||
|
||||
// Set initial bars.
|
||||
if ($useinitialsbar) {
|
||||
$this->initialbars($total > $pagesize);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override default implementation to display a more meaningful information to the user.
|
||||
*/
|
||||
public function print_nothing_to_display() {
|
||||
global $OUTPUT;
|
||||
echo $this->render_reset_button();
|
||||
$this->print_initials_bar();
|
||||
echo $OUTPUT->notification(get_string('noexpiredcontexts', 'tool_dataprivacy'), 'warning');
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the table's show_hide_link method to prevent the show/hide link for the select column from rendering.
|
||||
*
|
||||
* @param string $column the column name, index into various names.
|
||||
* @param int $index numerical index of the column.
|
||||
* @return string HTML fragment.
|
||||
*/
|
||||
protected function show_hide_link($column, $index) {
|
||||
if ($index < 6) {
|
||||
return parent::show_hide_link($column, $index);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user