Compare commits
1432 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c37329dc10 | |||
| c3dbe66477 | |||
| 3bfd32be9b | |||
| 85a42937b9 | |||
| c3db28fe90 | |||
| 2f1c8a398b | |||
| e7c034a8d8 | |||
| 5a6185300a | |||
| 9ab746af27 | |||
| a21f18e947 | |||
| 711d42778a | |||
| d2a497ee03 | |||
| 3f8dfe2ab7 | |||
| 09cbca8566 | |||
| 1c2efd6f1b | |||
| 751bdfd50d | |||
| 19c1e9845b | |||
| 64a736ee72 | |||
| 1f319dda3d | |||
| 5790e87939 | |||
| 40260a258f | |||
| 86f3c074e9 | |||
| cfac8bb5ab | |||
| 772afa0f43 | |||
| 111ec01643 | |||
| cf21130cc2 | |||
| a454fd5f57 | |||
| 300e220dbc | |||
| 5432075187 | |||
| a51fa84f50 | |||
| 052d30081e | |||
| ab883ad6b0 | |||
| 6069d11046 | |||
| 1875af2207 | |||
| 65b8d2ab7d | |||
| b39379cb98 | |||
| c840b579cb | |||
| 784c21b116 | |||
| f423dd8ccf | |||
| f5d57e74bf | |||
| e67669e638 | |||
| aa881b7717 | |||
| 19a94ef923 | |||
| 866d7a064a | |||
| 1d5b6e94ee | |||
| 014628c4c4 | |||
| 61b0e103b7 | |||
| 2a0c44b56d | |||
| ab79c7bf95 | |||
| 4415985085 | |||
| 83c83473f8 | |||
| 680b9186c7 | |||
| 3717d20a33 | |||
| e29b668d7b | |||
| 8987e225da | |||
| b96151e864 | |||
| 1c6f6505b9 | |||
| 884aa7b9f7 | |||
| 082f2f7dd8 | |||
| 9bec7f417f | |||
| b3865f159b | |||
| f03a28c520 | |||
| 68d882a927 | |||
| 41b628707c | |||
| 2d287b27e3 | |||
| b76d708315 | |||
| 1f2972764b | |||
| 2161e3294d | |||
| 898584944b | |||
| 642ad98e28 | |||
| 6892904225 | |||
| 5f7519ba19 | |||
| 2c904a6c87 | |||
| 91b38b5c30 | |||
| e60b5bd04d | |||
| d54b8bb7ee | |||
| a300c3479d | |||
| 38174e03de | |||
| 0d21b5a2dc | |||
| a36a6e4491 | |||
| 27621e4bfa | |||
| 939ba8562b | |||
| d94a35a5a4 | |||
| 1f44dfad42 | |||
| 62ba30c11f | |||
| 663c41bf76 | |||
| ded79d63d8 | |||
| d6dcfee9f5 | |||
| bb478e8ddd | |||
| be6fc5b122 | |||
| cad1e4f427 | |||
| 858cb072b1 | |||
| 5ededd52c0 | |||
| cf31c73791 | |||
| 265894ee59 | |||
| f1a57a3181 | |||
| ce48eff24e | |||
| 008b92bd0a | |||
| fe8ca8b78d | |||
| e5fe5da40c | |||
| d8d35d7c1f | |||
| 680160dca4 | |||
| 78315808b1 | |||
| b67c98ca83 | |||
| d8eb85604f | |||
| 027b757f20 | |||
| a21351f43f | |||
| 573fe725a4 | |||
| 32bfc786df | |||
| 3504a2a99f | |||
| eca3395a70 | |||
| d5a8d310e0 | |||
| af89e5a896 | |||
| 31aa968f43 | |||
| 560ddebb05 | |||
| 45654c4813 | |||
| ff3187ba50 | |||
| f5d105b4e0 | |||
| 6f2c8d1ca6 | |||
| 10c6f9c88f | |||
| 33e3839fdc | |||
| f60980615c | |||
| 153f6546bb | |||
| 6acb8efc76 | |||
| 73e51962df | |||
| e56de91383 | |||
| 68aabce476 | |||
| 8d53360c70 | |||
| 833d4aa42c | |||
| 6e043dd5ec | |||
| f1c8d8a374 | |||
| 8e31b3241a | |||
| 86b5d678bc | |||
| 1b9f20fdf9 | |||
| 5204184a9b | |||
| a2650c9a7d | |||
| 83832b98a3 | |||
| 149552615c | |||
| 33ef4d941d | |||
| 5aa514982d | |||
| 2ed6a293df | |||
| 7c84c195e9 | |||
| 5db5c6dcc3 | |||
| 8fb6a1a4f3 | |||
| 08c753034f | |||
| c54ed68179 | |||
| 485c8e1f77 | |||
| 62994208a1 | |||
| 74b5f26b3b | |||
| 6bdc80eaf5 | |||
| 56ff85a36b | |||
| c2a6b2b6ec | |||
| 96b0818716 | |||
| 51f0dbbcda | |||
| 1465548ba5 | |||
| 06c8d3bf55 | |||
| fd982f3030 | |||
| 876cdf05c4 | |||
| b98339b09a | |||
| 4b6f3f6cc1 | |||
| 8862838f25 | |||
| 5255e8fea9 | |||
| decd3731df | |||
| 60f6712ce1 | |||
| 8d85f96696 | |||
| 23a203618c | |||
| c0c33ab059 | |||
| 691e92efa7 | |||
| 1b70269f4e | |||
| 8197937d22 | |||
| d60618cbea | |||
| 6a5d9d0705 | |||
| ac897aedea | |||
| 6582329fdf | |||
| e8279ab994 | |||
| 85d42a024b | |||
| 1ea41504b4 | |||
| 8195484581 | |||
| 04f3435b12 | |||
| 00ced8dde3 | |||
| d33d90031e | |||
| b26b312e46 | |||
| f90a0107d1 | |||
| f6d8b2f753 | |||
| 533ef686e4 | |||
| 3fe971f407 | |||
| d334974409 | |||
| e58797fb4c | |||
| ed333da1a8 | |||
| ef9428e9cb | |||
| 112aa66679 | |||
| 974a90e2e7 | |||
| 674544a52a | |||
| b30fadbae0 | |||
| ccc3f70717 | |||
| 2c5e158be1 | |||
| f9180359e6 | |||
| d567be0d04 | |||
| 98900443b7 | |||
| 6d86e2d223 | |||
| ab6abffb30 | |||
| 018135947a | |||
| 374387c563 | |||
| 6ed62aec00 | |||
| 776503b29a | |||
| 92928baabe | |||
| 1560349956 | |||
| f39b2d7da8 | |||
| 739e7f5c79 | |||
| 3747401992 | |||
| e34f261d5e | |||
| 7bf7b7e517 | |||
| 791113dd66 | |||
| 56141c546f | |||
| c52dac659c | |||
| 41960dcbe8 | |||
| 0a65299f7f | |||
| f6f1a5f624 | |||
| 96437cfa3e | |||
| 94f668496c | |||
| c31ed14f78 | |||
| f6636c4ef6 | |||
| 348bdcba70 | |||
| 7db992398f | |||
| 6be954134b | |||
| dafd1528c1 | |||
| 6951d69b81 | |||
| 0e9baa7675 | |||
| a3aaf32b0a | |||
| a2137340ae | |||
| fb7934b2b8 | |||
| 2c030c2ccc | |||
| 8de1eaa03b | |||
| b2481afc98 | |||
| c0e4458f28 | |||
| f3bd9db07c | |||
| 83038cdb43 | |||
| d1ec251117 | |||
| a54eadb25a | |||
| 5bd702aace | |||
| 9d1bc62c35 | |||
| de464f7105 | |||
| ab4ecb487d | |||
| 3bd2c9cc2d | |||
| 255bc952ba | |||
| 7eb75ea17c | |||
| 9429e8c3ea | |||
| 0c392c1a85 | |||
| 9708ed8762 | |||
| 7a797d3fe5 | |||
| 7658ef16d5 | |||
| c37224a097 | |||
| 5439bb15fe | |||
| ae396e60f1 | |||
| 8e72596812 | |||
| 0fa43bbd16 | |||
| e3a7ffba7d | |||
| 0f3b121f1a | |||
| a5a7741d7c | |||
| b141b2d185 | |||
| 2e0e356228 | |||
| a3a8c5b244 | |||
| c092051695 | |||
| 8f9e7916e7 | |||
| 973405d146 | |||
| e676202902 | |||
| 7f8fbfe6c7 | |||
| c606e82d4b | |||
| f0bec9ecdf | |||
| fd2bb53bf2 | |||
| 2465c76ac1 | |||
| e04bb65283 | |||
| af90c1bc4b | |||
| 4458f71ab0 | |||
| aa208d1bc2 | |||
| 30ca2558dc | |||
| c1fef4c9c3 | |||
| 524cd5de9c | |||
| 908ba9fe26 | |||
| dce8813021 | |||
| 9c2f2a1ac0 | |||
| f83de6d1ba | |||
| c47cccc329 | |||
| 219ede6ecf | |||
| 9b7c011e3d | |||
| f88e03e50a | |||
| 369181e33b | |||
| 89266764c0 | |||
| 2a869c727e | |||
| 7a5aee27ff | |||
| 9515077330 | |||
| 0f84929153 | |||
| be19685f6b | |||
| d3ae85a539 | |||
| 9e25c063b7 | |||
| 21e93fdbe3 | |||
| 2fefe3ca68 | |||
| 6b6226160d | |||
| 97c77877f7 | |||
| 04fe8c2a63 | |||
| 80936f77ef | |||
| 9c172adefe | |||
| d7882b3815 | |||
| e2ba4fd896 | |||
| 7981a0b125 | |||
| 856b970904 | |||
| af9f57975c | |||
| 8322e9a1ee | |||
| 6817208044 | |||
| 5eae39feb0 | |||
| e77114bf70 | |||
| 3da5a3c348 | |||
| a6c263e33d | |||
| 10662a78b9 | |||
| dfd93e3617 | |||
| 6554a3d31e | |||
| 767e94894a | |||
| c875879050 | |||
| 18920002b9 | |||
| 08c3fc7493 | |||
| eda9ba5e21 | |||
| 7ceb3f8568 | |||
| 5a4cbdb34f | |||
| 3ff77e753b | |||
| 669fba527d | |||
| 31842699a0 | |||
| eb8a915468 | |||
| c8c5d741ab | |||
| 94f42309c5 | |||
| 5fd78d27ae | |||
| 77518888c8 | |||
| 9b3ce309f7 | |||
| c13aaef075 | |||
| 19afe6488f | |||
| f819f0f992 | |||
| 3c38e56b83 | |||
| a3b8f6a288 | |||
| 266d7f04ff | |||
| 3f6f7b1804 | |||
| 01bde600e3 | |||
| 3a2e28454d | |||
| e178d5ec21 | |||
| a5543d0731 | |||
| 820148bd89 | |||
| 39558fa5dd | |||
| 99c76ad3ce | |||
| 3372b77957 | |||
| eddf0a076f | |||
| 101f6c43e1 | |||
| 3da2da51b8 | |||
| df122c79cd | |||
| e6c53933a5 | |||
| 34dcee8f34 | |||
| e3c8b00866 | |||
| 5c3ee69c74 | |||
| bcd7e54804 | |||
| 8d141bb1ef | |||
| d1778bc5a0 | |||
| 156233b745 | |||
| 88f53cf84e | |||
| 111f55d95e | |||
| ddc0b2c805 | |||
| 9acdfbcdda | |||
| 421628ec0b | |||
| 61cf3e9178 | |||
| cefc325958 | |||
| 2886a5e64d | |||
| 8da3291abb | |||
| b618310364 | |||
| 4b51386d9a | |||
| 08f2b6063f | |||
| b22bc26c51 | |||
| 044656f96a | |||
| 0612aa3418 | |||
| c73a9d5cbe | |||
| 07049468e6 | |||
| 0a8a57943c | |||
| 9c9d3ab247 | |||
| 1b03a7f54d | |||
| a6a8ecc369 | |||
| 84b5f665ee | |||
| 5b2b0e66c1 | |||
| 03d182e726 | |||
| 79380f1408 | |||
| f1034de3af | |||
| 0756be091c | |||
| 3ec3295ae1 | |||
| 9543ced2b4 | |||
| 61140b097b | |||
| 40e33083b7 | |||
| e916aa4ae3 | |||
| 0202ab0473 | |||
| bce56f5031 | |||
| e98ad2ad05 | |||
| 74a3c0a49a | |||
| 3618a1a90d | |||
| 1b6af94da4 | |||
| 7597722505 | |||
| f9f59314e9 | |||
| c66ec12365 | |||
| 4099ae4896 | |||
| 23e11831bf | |||
| acd75c858e | |||
| 78404d4134 | |||
| 9abf9488e1 | |||
| 5881e25d35 | |||
| e8861b846e | |||
| f37406011f | |||
| 8ff7363219 | |||
| 4f7b3cb360 | |||
| 196d9c90f3 | |||
| fbafd55731 | |||
| 103c72a03a | |||
| 4352cd98bf | |||
| ebe14385a9 | |||
| 5648cec536 | |||
| f80f15a178 | |||
| 6a763870e0 | |||
| 70ebf686fd | |||
| 0153ce4fad | |||
| 4d1c53e490 | |||
| 7e968c33f8 | |||
| cb1ec4e3c7 | |||
| 0318b526f9 | |||
| 2cff7ff95f | |||
| 4bd2ccd63b | |||
| 40b9237b49 | |||
| 6d780cde01 | |||
| d6b5be41d4 | |||
| 0ef3ff5b68 | |||
| ff6db243c0 | |||
| 515f4ea924 | |||
| 17d02fc2a8 | |||
| c9a9c02374 | |||
| 01ad8ce22f | |||
| afa0d54934 | |||
| 53f5e8a321 | |||
| 886191944f | |||
| a7abfcef0e | |||
| 2aa7deb2f0 | |||
| 6d67378c41 | |||
| ed7e2d03f0 | |||
| 835a653755 | |||
| 149f1ecabd | |||
| eb16feff78 | |||
| 4c65cc4464 | |||
| 249a14082e | |||
| 1c38c18ca1 | |||
| 7c44363a80 | |||
| 2cc51e4887 | |||
| baffbd6d6f | |||
| f0d0414a9e | |||
| 774835181e | |||
| d1caad9938 | |||
| c08b4f9466 | |||
| 5d22d5087a | |||
| 835f7f20da | |||
| 69943f974b | |||
| 37427de44e | |||
| 3d9d924be6 | |||
| be2d84d913 | |||
| ee5530d552 | |||
| f303211548 | |||
| ca427f5389 | |||
| b40e466d20 | |||
| 5bfaff81e7 | |||
| 80934c2385 | |||
| 8460b91e31 | |||
| 1e65154616 | |||
| 0d85f27c6f | |||
| 8391635fd9 | |||
| acf68b58cd | |||
| e29ec14535 | |||
| 486b512bc4 | |||
| 46895a7b3d | |||
| 520cc4e661 | |||
| eaecfd8d79 | |||
| 7dbc822131 | |||
| 86f3c6b22f | |||
| 079b45c562 | |||
| 81f4e778e8 | |||
| bc1e9f0914 | |||
| 28d3ae01a6 | |||
| d5a7132a6a | |||
| 0972a5bb7c | |||
| 04a9173ac4 | |||
| 6b003a5bc1 | |||
| 8849300bd8 | |||
| 9db1054919 | |||
| 470a789ac2 | |||
| a760d1a278 | |||
| b4665dba28 | |||
| 7fceae553c | |||
| 8ab65b68b0 | |||
| e637c48678 | |||
| a20987ace8 | |||
| 4444333a28 | |||
| 001b73720d | |||
| fdd07c306a | |||
| 05807656a2 | |||
| 27fa56c9f8 | |||
| eb960618a1 | |||
| 87aaec6c82 | |||
| 08339c2799 | |||
| 2b33a8b518 | |||
| ff36037e13 | |||
| d38c995dcd | |||
| f26b3692c5 | |||
| 9701aa1a6f | |||
| 8a936e7371 | |||
| 48ab35f25d | |||
| e69580756c | |||
| 3c75559a6a | |||
| 111f4da8e4 | |||
| d03959c524 | |||
| 902f812c72 | |||
| 9ede824794 | |||
| 21f51cf2a6 | |||
| 1dacf6193f | |||
| 06e1ec0cf4 | |||
| 2be0984d4f | |||
| 9826b43f94 | |||
| 660d630ad5 | |||
| f203772256 | |||
| 6675f08970 | |||
| b039e36e7e | |||
| 11379b3995 | |||
| 184c8ddb25 | |||
| b628cab84f | |||
| bc67687abf | |||
| f9bbcab782 | |||
| fae6020b32 | |||
| e0de954728 | |||
| 59fc010ac9 | |||
| 54f5ded420 | |||
| e93d70e1c9 | |||
| 2d6bc5cd57 | |||
| 501dec0271 | |||
| dd0c7b7329 | |||
| b7f7b588a3 | |||
| c592b7529c | |||
| 47bad57184 | |||
| 9a17f41506 | |||
| e435e42e29 | |||
| 63a4554474 | |||
| 368436baac | |||
| d3a7b1ff87 | |||
| 616559742e | |||
| 0b1c63416f | |||
| 7bd8cd00de | |||
| 37acf2d4be | |||
| c82e60d0f3 | |||
| 062c83aae2 | |||
| ac3bb88f6f | |||
| 5f02dbb5fd | |||
| 349d0b6772 | |||
| a55d5c8292 | |||
| a85d8cffc1 | |||
| 4caf14d0dc | |||
| 5b9837e832 | |||
| ed7c8cf849 | |||
| 6a76fdfa40 | |||
| 36f4e4c861 | |||
| 10093c3635 | |||
| c9ef8771ce | |||
| 5f2fe216f0 | |||
| bf91ad7c8c | |||
| be960b40a0 | |||
| 48037ff7b0 | |||
| a5ca61930d | |||
| fa334d3119 | |||
| b71fc4ce4b | |||
| 113e66d70b | |||
| 297661d754 | |||
| e8112121d5 | |||
| 6480c4091e | |||
| b81b826c4a | |||
| af1c644712 | |||
| 3a84788acf | |||
| 78ecf75ead | |||
| 721fd1795f | |||
| c92feb872d | |||
| b7844952d7 | |||
| 34d73b6faa | |||
| cb4f59d780 | |||
| 521751de69 | |||
| c813b07254 | |||
| 399252fd85 | |||
| bdbe55c831 | |||
| 7ff43c2334 | |||
| 35b09d5623 | |||
| 9073a4e708 | |||
| f3a972d97a | |||
| 8b58e0535c | |||
| dced5c43c8 | |||
| 7ce30dda42 | |||
| 5c4ff2db85 | |||
| 31d0fb4349 | |||
| a2155a12e4 | |||
| 774108ca8a | |||
| c9ca50d029 | |||
| 5298471d0f | |||
| 22f6afdcd5 | |||
| 0a617a7e20 | |||
| 73f8e3dc26 | |||
| 709350d111 | |||
| 8186a5994d | |||
| 70698f7964 | |||
| ecebec32bc | |||
| 5b0ae8c97d | |||
| ce52426d52 | |||
| a00de36d15 | |||
| 0f4c592b5c | |||
| 07f6d8962c | |||
| 132ea1c052 | |||
| 9f2f2294d8 | |||
| c873339cc1 | |||
| a5dddf9bff | |||
| 9152286263 | |||
| 3e3a083ed1 | |||
| a879012464 | |||
| 18b98348a9 | |||
| caeb922dea | |||
| f79f0b5975 | |||
| 6b8921a67b | |||
| 01dd8aff46 | |||
| f4c5e78107 | |||
| 019a88a178 | |||
| ef66a33a94 | |||
| 6411b74074 | |||
| 3b8ee4c3a0 | |||
| a3a7a072e1 | |||
| db1a3e6623 | |||
| 739507da50 | |||
| ace39db60f | |||
| 4dd2d6a7e0 | |||
| 1414c0c83a | |||
| 9fd2c9c529 | |||
| d405b3b89a | |||
| 65f6e6ad89 | |||
| 948d3f566f | |||
| 8cd3227cd4 | |||
| a5947dc4cc | |||
| b767d92456 | |||
| 208a7c059f | |||
| bbaea992ab | |||
| 6b318d7f2b | |||
| 3656528c45 | |||
| 80ba17b0a5 | |||
| fe9954ef1f | |||
| b94fe0ece2 | |||
| c585e4fc60 | |||
| e217c239fd | |||
| bdb5843290 | |||
| 2eb7414d52 | |||
| 225408d744 | |||
| 07fa93a0ec | |||
| 86feef9a01 | |||
| d885504581 | |||
| 1e9ba15782 | |||
| e07927711e | |||
| 1ff3c3a64c | |||
| 933afbd395 | |||
| 0fc22245e1 | |||
| 81ea58cdf5 | |||
| 0682576b8e | |||
| 4a12d7a081 | |||
| 517baef479 | |||
| b88a6ac832 | |||
| 4ccebf873e | |||
| bc77389997 | |||
| fbf8d40f98 | |||
| 0c8d774cba | |||
| 51b7c14545 | |||
| 396fe24044 | |||
| 7b9251eb7f | |||
| 5f9e43efaa | |||
| 1568fc669c | |||
| 3a3abbbbd8 | |||
| ca50317233 | |||
| b8bea9d1b7 | |||
| a4535a6511 | |||
| f5579a67d8 | |||
| ff0e438780 | |||
| f9d9e29861 | |||
| c4b7e6de55 | |||
| 6275657518 | |||
| e1ed5f7ae4 | |||
| 5204900673 | |||
| a2a91c1fcf | |||
| 5ae13d00c0 | |||
| 2c746ce80a | |||
| a79e82588d | |||
| 0405156624 | |||
| a8f19ad776 | |||
| 9de64f724e | |||
| cb411703b7 | |||
| 6477811c56 | |||
| 0103da4a97 | |||
| 1475e116df | |||
| e0228935e4 | |||
| b43a5dd313 | |||
| 9c68283112 | |||
| 8cb537ec1b | |||
| 2e980b6b3a | |||
| 1c93fc9444 | |||
| 5155bf10bc | |||
| 1a83ee366d | |||
| cd0173d077 | |||
| 9280d2ca0b | |||
| ee49ade9c9 | |||
| 1eaf0be6ed | |||
| 7c46fbea90 | |||
| 5d307557cb | |||
| 24d2357da7 | |||
| 00cb9fe1e1 | |||
| fd772ad29a | |||
| 84f821c5c8 | |||
| dd5039e55d | |||
| 6e5876ca57 | |||
| 8392c88414 | |||
| fddf02e152 | |||
| 94714224d9 | |||
| e17623068c | |||
| 0ccecc5f49 | |||
| 3ad05e0471 | |||
| bc3e7588b9 | |||
| 61ab947bd1 | |||
| 6757aa403e | |||
| f9c68b4a4d | |||
| ff9019a16b | |||
| 3857876a58 | |||
| 3eef55dda4 | |||
| 8531f802ec | |||
| 77e0eaf2a6 | |||
| d2979fb33f | |||
| ed3ab1da74 | |||
| 9f5aecebfe | |||
| 192e9b2e1e | |||
| 737ab6eff8 | |||
| 652ce0a4dd | |||
| 63de5af968 | |||
| 6bc3ca7a4c | |||
| 5bb45aaa3c | |||
| 34931397bc | |||
| 747373c3f6 | |||
| 31b0b02025 | |||
| 168d335990 | |||
| 932689aa5e | |||
| 09913a019b | |||
| dee60ecadb | |||
| 4017db1b69 | |||
| a2881f0511 | |||
| da6c666441 | |||
| 04de230de4 | |||
| 89ed12aa50 | |||
| 767a4a4da5 | |||
| 0014edcb6e | |||
| 6a73402fdf | |||
| 67553b916f | |||
| dc57cba6be | |||
| 08cbe9b56c | |||
| fd07089a41 | |||
| 77e236ca8f | |||
| f50f0978da | |||
| 30eced81da | |||
| ea82e35775 | |||
| 14fc7f597a | |||
| 16c3e11c0d | |||
| de3f8e2653 | |||
| cc2454a92b | |||
| 01d52250a4 | |||
| 2aa76eae1d | |||
| 57d9e97c00 | |||
| e79517f1ee | |||
| a000775fed | |||
| 7bd0c3beba | |||
| 583281c7e6 | |||
| ed070b8d46 | |||
| 86b5c08a80 | |||
| 6eb7886c9c | |||
| 80ca92cfc7 | |||
| 958b46ad18 | |||
| 742af6b337 | |||
| 11e04e1bd4 | |||
| 62a0907588 | |||
| bbd4e0c745 | |||
| e3bcf576b1 | |||
| a98506c8e4 | |||
| 4853023e80 | |||
| 61ee905511 | |||
| 04cfb8194a | |||
| 623ee07cfe | |||
| a83ceb2be8 | |||
| 1b6f7526df | |||
| 707e1c5527 | |||
| cbca0b7c89 | |||
| b2ee1cf532 | |||
| 5e8a151aff | |||
| fcedf5a69b | |||
| abf6c76dbf | |||
| 1b842c1eed | |||
| 036ab38556 | |||
| 2baf76b657 | |||
| 01ac2dfe67 | |||
| 8dd32ca48b | |||
| 1b0e5bbcd3 | |||
| c1034f8595 | |||
| 06a28cd3e7 | |||
| c7640874bf | |||
| 458c700143 | |||
| 6a4148c734 | |||
| e9dbec72f8 | |||
| b555c2aee0 | |||
| 147174ffbb | |||
| 6a643e96d6 | |||
| 0742adc1f8 | |||
| 33e486cad6 | |||
| 2c89620ad0 | |||
| 96e983e488 | |||
| c2abd2c0f0 | |||
| 8256acd42e | |||
| 4b057b9d1a | |||
| 085f325851 | |||
| 4d26291ddc | |||
| 6dcb3a1692 | |||
| b2970055de | |||
| 0ac58fc0d7 | |||
| 558fc27a1d | |||
| 7c6310c1c0 | |||
| 5d70a00dd8 | |||
| e78265a235 | |||
| 99c4c947fe | |||
| 57f1a4adc7 | |||
| 7c08cacca7 | |||
| 23c1612fe1 | |||
| 7da1a29e3b | |||
| 69b780e97a | |||
| 9f271d765d | |||
| 8f8d4649b6 | |||
| 543afc2850 | |||
| be17ced747 | |||
| 783eecec41 | |||
| 71092f49c9 | |||
| 1892dd565f | |||
| a269833f80 | |||
| 49f57eb19d | |||
| 6f420dc5d0 | |||
| a27e44f244 | |||
| 577410806c | |||
| c6846832d5 | |||
| 5bfbe050ee | |||
| 9237d70fc8 | |||
| ff565b1f9c | |||
| f9bd55fae3 | |||
| a6b5e1366e | |||
| f4733abf2a | |||
| 862439c883 | |||
| e9786dc883 | |||
| 7c9b564f3f | |||
| 041072f63c | |||
| bc4e9f628c | |||
| 85b56ae4f7 | |||
| 48dfcbe27b | |||
| 2f0b720d6c | |||
| 26a4ad65dc | |||
| 8269544f2f | |||
| 775b6d0d50 | |||
| 25627e446f | |||
| eb82d420f0 | |||
| bc360e9441 | |||
| e786a591d5 | |||
| d9321f3342 | |||
| b2865ca1e4 | |||
| c1ae0d9bc4 | |||
| 7dc3c9b24e | |||
| 170abe6a2b | |||
| a1e64b07b5 | |||
| 9973df93f4 | |||
| a68417183c | |||
| 9e21adb5f7 | |||
| 65c23f097c | |||
| 92be49df49 | |||
| 7eab2af613 | |||
| 5fb193152e | |||
| 44080d810b | |||
| c525fe3095 | |||
| 86fe3ca41b | |||
| 20884e18e5 | |||
| b0b49c938b | |||
| 2e6acd5f60 | |||
| b0e4353ddd | |||
| 16edec0104 | |||
| 07c9c193b3 | |||
| 0817548194 | |||
| 3585740ced | |||
| 1cf00cde92 | |||
| 4ee7ad2377 | |||
| 9cdf3e4316 | |||
| 60bf7bf6c2 | |||
| d378b3a50e | |||
| 82c9aa61a8 | |||
| cb8197f594 | |||
| 9873a175a5 | |||
| 89033d2d07 | |||
| 110e818da7 | |||
| 5d06da4e41 | |||
| 78ebc5d9e7 | |||
| d8952802e7 | |||
| 7a49ac3d79 | |||
| 1983c83957 | |||
| 1b1b6313c0 | |||
| d2bc47577b | |||
| 674a9c859d | |||
| 56335cc001 | |||
| 2e86cca9ca | |||
| 0913f3314f | |||
| cf7979c622 | |||
| cda6d082de | |||
| eb12fec156 | |||
| d2c04300f3 | |||
| ceb21de5e3 | |||
| 6aef716ec4 | |||
| fc16e61230 | |||
| 0534fbc2aa | |||
| 5b9f872429 | |||
| dd67898152 | |||
| 2377dbd9d8 | |||
| 3810c3ad36 | |||
| ef9c44ad7e | |||
| 3eac779614 | |||
| 5223ceed6b | |||
| f27cf98517 | |||
| 7fe0a3b051 | |||
| f03765aae7 | |||
| 9fa3922c77 | |||
| 4a9c1685bd | |||
| fbb3918ebe | |||
| d17dc20947 | |||
| a63717d365 | |||
| 0867abed26 | |||
| be94915636 | |||
| 5de4b14160 | |||
| 3e316b8713 | |||
| 267e1af112 | |||
| 37db3b8561 | |||
| 5a146aa633 | |||
| 934220228a | |||
| 3c9a5ae1cc | |||
| 8d7d722fb0 | |||
| 2f0be313b3 | |||
| 68e6299010 | |||
| e3a18e4458 | |||
| 21d27e3f96 | |||
| a74a3f8ab8 | |||
| e95f91c90a | |||
| 7a793db488 | |||
| 645b80ea72 | |||
| 5c07c79657 | |||
| 5cb155ad9d | |||
| a8b5704fb4 | |||
| 707d3a3aa3 | |||
| 9f11f13ad8 | |||
| b091c290bb | |||
| c101a623d8 | |||
| 5815d6f01c | |||
| 2e17137c8b | |||
| 28b6ae97e9 | |||
| d527a497ea | |||
| 64488fd069 | |||
| 76148061d7 | |||
| 9214166d89 | |||
| 253fcd9ce2 | |||
| 218cbbed31 | |||
| b3dfb6eafa | |||
| 8fda9b0267 | |||
| 5fafbd8a3c | |||
| 9a550cac49 | |||
| ffd593db80 | |||
| 14618260c8 | |||
| 7f1fcbda75 | |||
| 5fc058c0b0 | |||
| 2faddcdef5 | |||
| a49458943c | |||
| aaf815541b | |||
| eb618c1c71 | |||
| 4948a43536 | |||
| b4a4b7ce70 | |||
| 4f21e3a33f | |||
| d6e63cb602 | |||
| 504e36da8e | |||
| 9c796e028d | |||
| 3a7f56c779 | |||
| c982d78c20 | |||
| c4f22db315 | |||
| f926f376f8 | |||
| 6972f8403f | |||
| 457fcfe3d0 | |||
| bf085b7fc5 | |||
| 2eb83b7678 | |||
| a3e647dc98 | |||
| a5bac67326 | |||
| 3117287072 | |||
| f6121c0bd6 | |||
| fd1ef60779 | |||
| ff1f239381 | |||
| 6c855610bf | |||
| 8b1eb7742e | |||
| 08e1bbd86b | |||
| b899bab0bc | |||
| 48352701a1 | |||
| f23541f3fb | |||
| ab83a52583 | |||
| 7f3c09ae49 | |||
| 2e8f163f64 | |||
| 41275ea779 | |||
| e129c4733b | |||
| 8904b10b44 | |||
| 5e092e2540 | |||
| 235d4be426 | |||
| 0ce2cf80ee | |||
| 7d5efd643c | |||
| 8566cace36 | |||
| f71d4d4a97 | |||
| ccd495d806 | |||
| 5613a37ab2 | |||
| 8e2eb1f335 | |||
| 89660817f7 | |||
| ea61136232 | |||
| 9a36044425 | |||
| 6dc3fc7508 | |||
| 8ba2f949d0 | |||
| e53bde7de2 | |||
| aee5a8666a | |||
| a163049a64 | |||
| 055010a34a | |||
| abe4724b32 | |||
| 163a9d21e5 | |||
| 234d16cfcb | |||
| e598dc49a7 | |||
| 1702e66574 | |||
| 9d8c7eaaa8 | |||
| a77941b809 | |||
| 7fe0d459d6 | |||
| bfe824dd3d | |||
| ad0f68802a | |||
| e3db31ac67 | |||
| 00ab44ee52 | |||
| bb89fdcf22 | |||
| b813a2778b | |||
| 7e9362481d | |||
| 407a8d675e | |||
| 38055cdb22 | |||
| b3c71b12c7 | |||
| 5ac4c7f2ba | |||
| 0c8d6093dc | |||
| 0ab8ddc56b | |||
| 2cda43b3bd | |||
| efc06d64e7 | |||
| a71eb555c2 | |||
| 7517f9ea50 | |||
| 73f8281a01 | |||
| 158586680d | |||
| c81ead0adb | |||
| 1ac64fbe83 | |||
| 3111b937ef | |||
| 58c586c3c4 | |||
| 9b2b1b5b12 | |||
| e9925ef601 | |||
| d8aba73bb4 | |||
| 4174bcc99c | |||
| f89874634c | |||
| 66a13f93e4 | |||
| 859e9b3394 | |||
| 9aae08cc38 | |||
| 5608acd0d7 | |||
| 0b13c999e2 | |||
| fa62cb4137 | |||
| db75ead6fe | |||
| 5ae345bdeb | |||
| ac8bf02214 | |||
| a1a04839eb | |||
| d8c58a53d3 | |||
| 17384a4a8e | |||
| 0a8e683595 | |||
| 63da6656b9 | |||
| 53556cf240 | |||
| a11dae7625 | |||
| 274d3ca6d5 | |||
| 492ddb2bf8 | |||
| 5bb4d629fc | |||
| 9cb82a1530 | |||
| a5e434b97e | |||
| cf9910912f | |||
| b2961aa526 | |||
| 48a5afe796 | |||
| 83539ba7aa | |||
| 01484f2289 | |||
| a167de121b | |||
| f058e911cd | |||
| 06cded2b76 | |||
| df5debbfc4 | |||
| 1c1c751560 | |||
| e6645efea6 | |||
| eedd1437c8 | |||
| b1c1aa89ed | |||
| 8ede8c859e | |||
| e288c6314f | |||
| 4865f138c7 | |||
| 9db5265f18 | |||
| 5b7ee7d6b3 | |||
| e1a489362c | |||
| 2da5023cd2 | |||
| b025f80743 | |||
| b65a529086 | |||
| 68673fb78d | |||
| 0de71f9432 | |||
| 82815a491e | |||
| 5abcd64852 | |||
| da908a84c3 | |||
| fde9d39538 | |||
| 97cf15c9ad | |||
| 6837f2f698 | |||
| 83b01d7505 | |||
| e9c315ff85 | |||
| fb4da0de30 | |||
| 3cf3e20b77 | |||
| 2d6a9a6ef3 | |||
| 2a208a32cb | |||
| ba6233ae2c | |||
| ad2ed52966 | |||
| 84bc03313b | |||
| 043279b4d9 | |||
| 4a108f79a8 | |||
| 0c496132cf | |||
| d251a46588 | |||
| 20c6e3628c | |||
| 6df9abc397 | |||
| 330fa046d5 | |||
| c16fd22bbc | |||
| b8cf9f06dc | |||
| 879c847aed | |||
| 7c9b7feb10 | |||
| 272467514a | |||
| bbcf850b68 | |||
| 7bdde1a0ee | |||
| 5912eca97e | |||
| 5dfceb9f52 | |||
| 28b96ce145 | |||
| ef7dc1ae34 | |||
| 4f04ad2d19 | |||
| 12b47586bb | |||
| 67240e4539 | |||
| a324b4981a | |||
| 63401025a9 | |||
| e32a370df2 | |||
| c0f75375fe | |||
| 5dc4095720 | |||
| 50114457be | |||
| 7857d77a30 | |||
| 6a8c7fd988 | |||
| 1a7ded0892 | |||
| 504bde6118 | |||
| a38462a3aa | |||
| 1c6c7ac06d | |||
| c50907bdfd | |||
| 65be0f98d8 | |||
| fa13c8a8cb | |||
| 1b091c789c | |||
| ff0edf2d12 | |||
| ac1af59d9b | |||
| d51a8d2e40 | |||
| a1e72fc747 | |||
| 591fbbe972 | |||
| 41e6821e5d | |||
| 15646264c4 | |||
| 505de3307c | |||
| e326124d9e | |||
| e6f2e62275 | |||
| c823d1c9aa | |||
| d3b89d98f5 | |||
| 4c29192189 | |||
| 33947383f7 | |||
| c2155bef19 | |||
| 0829f91214 | |||
| 7ec58e84b0 | |||
| ae02647dec | |||
| cb04f502d5 | |||
| 13c3219692 | |||
| 557b880f0b | |||
| 803a9fdc96 | |||
| 659b1ee304 | |||
| 22a68d6fac | |||
| 3eb97ca783 | |||
| 2530725163 | |||
| 9308160bce | |||
| 2667898317 | |||
| 9525aa62a5 | |||
| a6cf5b5582 | |||
| 02d16e76c3 | |||
| 1926647e85 | |||
| f510295159 | |||
| fc86038b54 | |||
| e4fd0ad2e3 | |||
| 64b3116245 | |||
| 39dc526cd4 | |||
| f46f4ca56d | |||
| fd4f1fd78a | |||
| cde629d3c6 | |||
| c2ebe33f77 | |||
| e807adb913 | |||
| af37de609e | |||
| 01880d2a9e | |||
| adb29782e8 | |||
| 2be626caaa | |||
| 5ecd41650f | |||
| 7f4bc03789 | |||
| d77de7c873 | |||
| 409a0ecf2a | |||
| 9fcec4530d | |||
| 2489235195 | |||
| e16a56ccc5 | |||
| 9e3b6c626c | |||
| 00f88c287d | |||
| 23d7a70ef3 | |||
| 652948bd4b | |||
| 70a713c3d8 | |||
| e4b5f17a93 | |||
| 70611182ec | |||
| 660f2f2b70 | |||
| 847250b4d5 | |||
| ccd3ab300d | |||
| 304b80c734 | |||
| 74798df3e9 | |||
| 6503626135 | |||
| b62d6b901a | |||
| 511a150aa0 | |||
| 4fca4d4e87 | |||
| 1c216b13e4 | |||
| d7a7ef5c44 | |||
| c426a1e2f7 | |||
| 761e8a442f | |||
| e15be87a98 | |||
| d716aa1587 | |||
| 9a2441ca48 | |||
| 036b10b783 | |||
| 2444915544 | |||
| a6870770c3 | |||
| 6acae651b3 | |||
| 2d9efe5283 | |||
| 068935011b | |||
| 4e00702953 | |||
| 020fd88e2e | |||
| fb20acf02d | |||
| b2b940caae | |||
| ab578c4b48 | |||
| b571d8b51f | |||
| 2f81f23e3c | |||
| d02e847c3f | |||
| aab559c464 | |||
| 457d29926f | |||
| 7660a50369 | |||
| 35b5315034 | |||
| 74cc4d6acb | |||
| 98bd6c5273 | |||
| aaa3a28e03 | |||
| 1ab916014f | |||
| 6a02fd458f | |||
| 9f35372c0c | |||
| 042077dd47 | |||
| c31343bf78 | |||
| 3baef4ab41 | |||
| 25666e5ff5 | |||
| ba68a016de | |||
| 53b2f1cf4a | |||
| ec85b1eb56 | |||
| e96e4d5a5b | |||
| b01791c076 | |||
| f93839e2ca | |||
| c45ca633b7 | |||
| fe94c07971 | |||
| 63d3298c0b | |||
| 318697f600 | |||
| c00848dbf2 | |||
| 5fd29e79dc | |||
| 8794e3392d | |||
| 3cbea698c3 | |||
| 384947bab2 | |||
| 5fea116586 | |||
| 9ce791b51e | |||
| 9fd5ed6217 | |||
| 402d910a60 | |||
| 78c66df3e5 | |||
| 7391e4a459 | |||
| d1e03e531d | |||
| 3eba4443a1 | |||
| a34f98e166 | |||
| df6bdbcd46 | |||
| c709d42c06 | |||
| c13b3ca26b | |||
| 88ed1a48fd | |||
| 1402bb28bc | |||
| ec1aa43448 | |||
| 3b70d720ec | |||
| 10d22c10fc | |||
| a65c67a7b0 | |||
| 2f7ef500a4 | |||
| 157981c24b | |||
| 5cb58b2db4 | |||
| 608cce8d16 | |||
| b74d94b85e | |||
| fdea53a86b | |||
| 4ff3d4e309 | |||
| b448376bea | |||
| d6d0e0f4be | |||
| 9a32a6d629 | |||
| 8d41ccfbe1 | |||
| ac1f4d9633 | |||
| 4826b9d454 | |||
| 07cb9ff078 | |||
| 3f79afbb78 | |||
| 540f98ac7e | |||
| d6e92ad815 | |||
| cc34be6fae | |||
| 1f05e1459a | |||
| 287089b1a2 | |||
| 2b460b0fc8 | |||
| cb9d984b86 | |||
| 11af3ac71d | |||
| 1f446a04c2 | |||
| 39237cf346 | |||
| 60770d10f9 | |||
| 108411af39 | |||
| 3104923f6b | |||
| 7edb9a48e0 | |||
| d5b71904b0 | |||
| 8ac72824e1 | |||
| 7b3832899c | |||
| 0c4f7b3b1b | |||
| 5dc5337f36 | |||
| 8b283a6cf5 | |||
| b0734fa5ee | |||
| 9e5c6b8d3d | |||
| 9962da5423 | |||
| bae7a3b909 | |||
| 6103daf4b0 | |||
| 702605b2c9 | |||
| f5e3d2fe72 | |||
| 4965564ef4 | |||
| 9f65a1fb64 | |||
| 907832b43b | |||
| a4c2795681 | |||
| 4d7d71fb37 | |||
| 813baed84d | |||
| 319e12ba83 | |||
| 33c67b74aa | |||
| d36fdb9657 | |||
| de874ead39 | |||
| cab3b03540 | |||
| 462785cb74 | |||
| 2d3c247d4d | |||
| 542bd70b90 | |||
| 29372f84cb | |||
| c099963697 | |||
| f6a3bbe132 | |||
| 96a77cf0b1 | |||
| cd54b73299 | |||
| 657cb6b451 | |||
| 04be8aa428 | |||
| 2f71de8e90 | |||
| 6b38456cda | |||
| 6f0e8dbaa5 | |||
| 275fbca097 | |||
| 5ef58abac3 | |||
| 8854864605 | |||
| cc37bd164f | |||
| e8605c6514 | |||
| 03c8a58b59 | |||
| ad95dcaccb | |||
| 25bb7e4a12 | |||
| 2ace718489 | |||
| 3eeca45c23 | |||
| 58394a6223 | |||
| 6c364342cf | |||
| 986e88bce3 | |||
| fec3b64a4f | |||
| 14120d413e | |||
| 5fe7cae927 | |||
| dbe472f58b | |||
| 8156736712 | |||
| 5e5283f9f3 | |||
| ce694bbb81 | |||
| ff6faa59eb | |||
| 0cca93b3b2 | |||
| 67def8d76b | |||
| de935c233b | |||
| 0a45b8eba3 | |||
| d10e946d7b | |||
| 479e718dc3 | |||
| 0bbd7b8291 | |||
| 68791e3d6e | |||
| 65c7d85d30 | |||
| a48f144dc1 | |||
| 1174beee83 | |||
| 2a626230ee | |||
| ce5fe979f8 | |||
| be05d8c3f0 | |||
| ad904c2827 | |||
| 087b392b04 | |||
| 5a6b7338c3 | |||
| d60d047a96 | |||
| 30161129f5 | |||
| edd820115e | |||
| c5430a26cf | |||
| 89401022c3 | |||
| f8cecf2031 | |||
| 697d94550c | |||
| 487b237684 | |||
| 7444926f37 | |||
| d1c7b42f57 | |||
| 6e270daa1d | |||
| 6b4810bdf9 | |||
| b9ab4a82e5 | |||
| e185931893 | |||
| 0a8086fb97 | |||
| 8b31cbead8 | |||
| feb69eb8a5 | |||
| ccdb33e9a8 | |||
| df4dd003f7 | |||
| 46b8ab729a | |||
| aa7f151edd | |||
| c76ac89f80 |
@@ -3,6 +3,7 @@
|
||||
*/**/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/
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
**/yui/build/** -diff
|
||||
**/amd/build/** -diff
|
||||
lib/dml/oci_native_moodle_package.sql text eol=lf
|
||||
theme/bootstrapbase/style/editor.css -diff
|
||||
theme/bootstrapbase/style/moodle.css -diff
|
||||
|
||||
@@ -4,6 +4,7 @@ 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/
|
||||
|
||||
+2
-2
@@ -55,7 +55,7 @@ matrix:
|
||||
include:
|
||||
# Run grunt/npm install on highest version ('node' is an alias for the latest node.js version.)
|
||||
- php: 7.1
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='8.9'
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='lts/carbon'
|
||||
|
||||
exclude:
|
||||
# MySQL - it's just too slow.
|
||||
@@ -249,7 +249,7 @@ script:
|
||||
- >
|
||||
if [ "$TASK" = 'PHPUNIT' ];
|
||||
then
|
||||
vendor/bin/phpunit;
|
||||
vendor/bin/phpunit --fail-on-risky --disallow-test-output --verbose;
|
||||
fi
|
||||
|
||||
- >
|
||||
|
||||
@@ -165,6 +165,12 @@ function print_auth_lock_options($auth, $user_fields, $helptext, $retrieveopts,
|
||||
// If custom field then pick name from database.
|
||||
$fieldshortname = str_replace('profile_field_', '', $fieldname);
|
||||
$fieldname = $customfieldname[$fieldshortname]->name;
|
||||
if (core_text::strlen($fieldshortname) > 67) {
|
||||
// If custom profile field name is longer than 67 characters we will not be able to store the setting
|
||||
// such as 'field_updateremote_profile_field_NOTSOSHORTSHORTNAME' in the database because the character
|
||||
// limit for the setting name is 100.
|
||||
continue;
|
||||
}
|
||||
} elseif ($fieldname == 'url') {
|
||||
$fieldname = get_string('webpage');
|
||||
} else {
|
||||
|
||||
+13
-9
@@ -54,14 +54,16 @@ $statusmsg = '';
|
||||
$errormsg = '';
|
||||
|
||||
if ($data = data_submitted() and confirm_sesskey()) {
|
||||
if (admin_write_settings($data)) {
|
||||
$statusmsg = get_string('changessaved');
|
||||
}
|
||||
|
||||
$count = admin_write_settings($data);
|
||||
if (empty($adminroot->errors)) {
|
||||
switch ($return) {
|
||||
case 'site': redirect("$CFG->wwwroot/");
|
||||
case 'admin': redirect("$CFG->wwwroot/$CFG->admin/");
|
||||
// No errors. Did we change any setting? If so, then indicate success.
|
||||
if ($count) {
|
||||
$statusmsg = get_string('changessaved');
|
||||
} else {
|
||||
switch ($return) {
|
||||
case 'site': redirect("$CFG->wwwroot/");
|
||||
case 'admin': redirect("$CFG->wwwroot/$CFG->admin/");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$errormsg = get_string('errorwithsettings', 'admin');
|
||||
@@ -73,7 +75,7 @@ if ($data = data_submitted() and confirm_sesskey()) {
|
||||
if ($PAGE->user_allowed_editing() && $adminediting != -1) {
|
||||
$USER->editing = $adminediting;
|
||||
}
|
||||
|
||||
$buttons = null;
|
||||
if ($PAGE->user_allowed_editing()) {
|
||||
$url = clone($PAGE->url);
|
||||
if ($PAGE->user_is_editing()) {
|
||||
@@ -127,7 +129,9 @@ if ($savebutton) {
|
||||
$visiblepathtosection = array_reverse($settingspage->visiblepath);
|
||||
$PAGE->set_title("$SITE->shortname: " . implode(": ",$visiblepathtosection));
|
||||
$PAGE->set_heading($SITE->fullname);
|
||||
$PAGE->set_button($buttons);
|
||||
if ($buttons) {
|
||||
$PAGE->set_button($buttons);
|
||||
}
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?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/>.
|
||||
/**
|
||||
* Privacy Subsystem implementation for core_admin.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core_admin\privacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
/**
|
||||
* Privacy Subsystem for core_admin implementing null_provider.
|
||||
*
|
||||
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements \core_privacy\local\metadata\null_provider {
|
||||
/**
|
||||
* Get the language string identifier with the component's language
|
||||
* file to explain why this plugin stores no data.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_reason() {
|
||||
return 'privacy:metadata';
|
||||
}
|
||||
}
|
||||
@@ -2259,4 +2259,361 @@
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.5" requires="3.1">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="5.5.31" />
|
||||
<VENDOR name="mysql" version="5.5.31" />
|
||||
<VENDOR name="postgres" version="9.3" />
|
||||
<VENDOR name="mssql" version="10.0" />
|
||||
<VENDOR name="oracle" version="10.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.0.0" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="mbstringrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.6" requires="3.1">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="5.5.31" />
|
||||
<VENDOR name="mysql" version="5.6" />
|
||||
<VENDOR name="postgres" version="9.4" />
|
||||
<VENDOR name="mssql" version="10.0" />
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.0.0" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="mbstringrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
@@ -29,9 +29,10 @@ require_once($CFG->libdir.'/adminlib.php');
|
||||
$confirm = optional_param('confirm', 0, PARAM_BOOL);
|
||||
$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
|
||||
|
||||
admin_externalpage_setup('purgecaches');
|
||||
|
||||
// If we have got here as a confirmed aciton, do it.
|
||||
if ($confirm && isloggedin() && confirm_sesskey()) {
|
||||
require_capability('moodle/site:config', context_system::instance());
|
||||
if ($confirm && confirm_sesskey()) {
|
||||
|
||||
// Valid request. Purge, and redirect the user back to where they came from.
|
||||
purge_all_caches();
|
||||
@@ -45,8 +46,6 @@ if ($confirm && isloggedin() && confirm_sesskey()) {
|
||||
}
|
||||
|
||||
// Otherwise, show a button to actually purge the caches.
|
||||
admin_externalpage_setup('purgecaches');
|
||||
|
||||
$actionurl = new moodle_url('/admin/purgecaches.php', array('sesskey'=>sesskey(), 'confirm'=>1));
|
||||
if ($returnurl) {
|
||||
$actionurl->param('returnurl', $returnurl);
|
||||
|
||||
@@ -37,7 +37,7 @@ require_once($CFG->dirroot . "/webservice/xmlrpc/lib.php");
|
||||
|
||||
admin_externalpage_setup('registrationhubs');
|
||||
|
||||
$renderer = $PAGE->get_renderer('core', 'register');
|
||||
$renderer = $PAGE->get_renderer('core', 'admin');
|
||||
|
||||
$unregistration = optional_param('unregistration', 0, PARAM_INT);
|
||||
$cleanregdata = optional_param('cleanregdata', 0, PARAM_BOOL);
|
||||
@@ -181,8 +181,7 @@ if (empty($cancel) and $unregistration and !$confirm) {
|
||||
echo $OUTPUT->header();
|
||||
|
||||
//check if the site is registered on Moodle.org and display a message about registering on MOOCH
|
||||
$adminrenderer = $PAGE->get_renderer('core', 'admin');
|
||||
echo $adminrenderer->warn_if_not_registered();
|
||||
echo $renderer->warn_if_not_registered();
|
||||
|
||||
//do not check sesskey if confirm = false because this script is linked into email message
|
||||
if (!empty($errormessage)) {
|
||||
|
||||
@@ -195,7 +195,7 @@ if ($huburl == HUB_MOODLEORGHUBURL) {
|
||||
echo $OUTPUT->notification($registrationmessage, $notificationtype);
|
||||
|
||||
echo $OUTPUT->heading(get_string('registerwithmoodleorg', 'admin'));
|
||||
$renderer = $PAGE->get_renderer('core', 'register');
|
||||
$renderer = $PAGE->get_renderer('core', 'admin');
|
||||
echo $renderer->moodleorg_registration_message();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
<?php
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// This file is part of Moodle - http://moodle.org/ //
|
||||
// Moodle - Modular Object-Oriented Dynamic Learning Environment //
|
||||
// //
|
||||
// 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/>. //
|
||||
// //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Registration renderer.
|
||||
* @package moodle
|
||||
* @subpackage registration
|
||||
* @copyright 2010 Moodle Pty Ltd (http://moodle.com)
|
||||
* @author Jerome Mouneyrac
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_register_renderer extends plugin_renderer_base {
|
||||
|
||||
/**
|
||||
* Display message about the benefits of registering on Moodle.org
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function moodleorg_registration_message() {
|
||||
$moodleorgstatslink = html_writer::link('http://moodle.net/stats',
|
||||
get_string('statsmoodleorg', 'admin'),
|
||||
array('target' => '_blank'));
|
||||
|
||||
$moodleorgregmsg = get_string('registermoodleorg', 'admin');
|
||||
$items = array(get_string('registermoodleorgli1', 'admin'),
|
||||
get_string('registermoodleorgli2', 'admin', $moodleorgstatslink));
|
||||
$moodleorgregmsg .= html_writer::alist($items);
|
||||
return $moodleorgregmsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a box message confirming a site registration (add or update)
|
||||
* @param string $confirmationmessage
|
||||
* @return string
|
||||
*/
|
||||
public function registration_confirmation($confirmationmessage) {
|
||||
$linktositelist = html_writer::tag('a', get_string('sitelist', 'hub'),
|
||||
array('href' => new moodle_url('/local/hub/index.php')));
|
||||
$message = $confirmationmessage . html_writer::empty_tag('br') . $linktositelist;
|
||||
return $this->output->box($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the listing of registered on hub
|
||||
*/
|
||||
public function registeredonhublisting($hubs) {
|
||||
global $CFG;
|
||||
$table = new html_table();
|
||||
$table->head = array(get_string('hub', 'hub'), get_string('operation', 'hub'));
|
||||
$table->size = array('80%', '20%');
|
||||
|
||||
foreach ($hubs as $hub) {
|
||||
if ($hub->huburl == HUB_MOODLEORGHUBURL) {
|
||||
$hub->hubname = get_string('registeredmoodleorg', 'hub', $hub->hubname);
|
||||
}
|
||||
$hublink = html_writer::tag('a', $hub->hubname, array('href' => $hub->huburl));
|
||||
$hublinkcell = html_writer::tag('div', $hublink, array('class' => 'registeredhubrow'));
|
||||
|
||||
$unregisterhuburl = new moodle_url("/" . $CFG->admin . "/registration/index.php",
|
||||
array('sesskey' => sesskey(), 'huburl' => $hub->huburl,
|
||||
'unregistration' => 1));
|
||||
$unregisterbutton = new single_button($unregisterhuburl,
|
||||
get_string('unregister', 'hub'));
|
||||
$unregisterbutton->class = 'centeredbutton';
|
||||
$unregisterbuttonhtml = $this->output->render($unregisterbutton);
|
||||
|
||||
//add button cells
|
||||
$cells = array($hublinkcell, $unregisterbuttonhtml);
|
||||
$row = new html_table_row($cells);
|
||||
$table->data[] = $row;
|
||||
}
|
||||
|
||||
return html_writer::table($table);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2033,4 +2033,69 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
return $this->warning(get_string('legacylogginginuse'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display message about the benefits of registering on Moodle.org
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function moodleorg_registration_message() {
|
||||
$moodleorgstatslink = html_writer::link('http://moodle.net/stats',
|
||||
get_string('statsmoodleorg', 'admin'),
|
||||
array('target' => '_blank'));
|
||||
|
||||
$moodleorgregmsg = get_string('registermoodleorg', 'admin');
|
||||
$items = array(get_string('registermoodleorgli1', 'admin'),
|
||||
get_string('registermoodleorgli2', 'admin', $moodleorgstatslink));
|
||||
$moodleorgregmsg .= html_writer::alist($items);
|
||||
return $moodleorgregmsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a box message confirming a site registration (add or update)
|
||||
* @param string $confirmationmessage
|
||||
* @return string
|
||||
*/
|
||||
public function registration_confirmation($confirmationmessage) {
|
||||
$linktositelist = html_writer::tag('a', get_string('sitelist', 'hub'),
|
||||
array('href' => new moodle_url('/local/hub/index.php')));
|
||||
$message = $confirmationmessage . html_writer::empty_tag('br') . $linktositelist;
|
||||
return $this->output->box($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the listing of registered on hub
|
||||
*
|
||||
* @param stdClass[] $hubs
|
||||
* @return string
|
||||
*/
|
||||
public function registeredonhublisting($hubs) {
|
||||
global $CFG;
|
||||
$table = new html_table();
|
||||
$table->head = array(get_string('hub', 'hub'), get_string('operation', 'hub'));
|
||||
$table->size = array('80%', '20%');
|
||||
|
||||
foreach ($hubs as $hub) {
|
||||
if ($hub->huburl == HUB_MOODLEORGHUBURL) {
|
||||
$hub->hubname = get_string('registeredmoodleorg', 'hub', $hub->hubname);
|
||||
}
|
||||
$hublink = html_writer::tag('a', $hub->hubname, array('href' => $hub->huburl));
|
||||
$hublinkcell = html_writer::tag('div', $hublink, array('class' => 'registeredhubrow'));
|
||||
|
||||
$unregisterhuburl = new moodle_url("/" . $CFG->admin . "/registration/index.php",
|
||||
array('sesskey' => sesskey(), 'huburl' => $hub->huburl,
|
||||
'unregistration' => 1));
|
||||
$unregisterbutton = new single_button($unregisterhuburl,
|
||||
get_string('unregister', 'hub'));
|
||||
$unregisterbutton->class = 'centeredbutton';
|
||||
$unregisterbuttonhtml = $this->output->render($unregisterbutton);
|
||||
|
||||
// Add button cells.
|
||||
$cells = array($hublinkcell, $unregisterbuttonhtml);
|
||||
$row = new html_table_row($cells);
|
||||
$table->data[] = $row;
|
||||
}
|
||||
|
||||
return html_writer::table($table);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ if ($roleid) {
|
||||
$select = new single_select($PAGE->url, 'roleid', $nameswithcounts, $roleid, null);
|
||||
$select->label = get_string('assignanotherrole', 'core_role');
|
||||
echo $OUTPUT->render($select);
|
||||
echo '<p><a href="' . $PAGE->url . '">' . get_string('backtoallroles', 'core_role') . '</a></p>';
|
||||
echo '<p><a href="' . $url . '">' . get_string('backtoallroles', 'core_role') . '</a></p>';
|
||||
echo '</div>';
|
||||
|
||||
} else if (empty($assignableroles)) {
|
||||
|
||||
@@ -100,6 +100,8 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
$this->role->shortname = core_text::strtolower(clean_param($this->role->shortname, PARAM_ALPHANUMEXT));
|
||||
if (empty($this->role->shortname)) {
|
||||
$this->errors['shortname'] = get_string('errorbadroleshortname', 'core_role');
|
||||
} else if (core_text::strlen($this->role->shortname) > 100) { // Check if it exceeds the max of 100 characters.
|
||||
$this->errors['shortname'] = get_string('errorroleshortnametoolong', 'core_role');
|
||||
}
|
||||
}
|
||||
if ($DB->record_exists_select('role', 'shortname = ? and id <> ?', array($this->role->shortname, $this->roleid))) {
|
||||
@@ -476,7 +478,7 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
}
|
||||
|
||||
protected function get_shortname_field($id) {
|
||||
return '<input type="text" id="' . $id . '" name="' . $id . '" maxlength="254" value="' . s($this->role->shortname) . '"' .
|
||||
return '<input type="text" id="' . $id . '" name="' . $id . '" maxlength="100" value="' . s($this->role->shortname) . '"' .
|
||||
' class="form-control"/>';
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,379 @@
|
||||
<?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/>.
|
||||
/**
|
||||
* Privacy Subsystem implementation for core_role.
|
||||
*
|
||||
* @package core_role
|
||||
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core_role\privacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use \core_privacy\local\metadata\collection;
|
||||
use \core_privacy\local\request\contextlist;
|
||||
use \core_privacy\local\request\approved_contextlist;
|
||||
use \core_privacy\local\request\transform;
|
||||
use \core_privacy\local\request\writer;
|
||||
|
||||
/**
|
||||
* Privacy provider for core_role.
|
||||
*
|
||||
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements
|
||||
\core_privacy\local\metadata\provider,
|
||||
\core_privacy\local\request\subsystem\provider,
|
||||
\core_privacy\local\request\subsystem\plugin_provider,
|
||||
\core_privacy\local\request\user_preference_provider {
|
||||
|
||||
/**
|
||||
* Get information about the user data stored by this plugin.
|
||||
*
|
||||
* @param collection $collection An object for storing metadata.
|
||||
* @return collection The metadata.
|
||||
*/
|
||||
public static function get_metadata(collection $collection) {
|
||||
$rolecapabilities = [
|
||||
'roleid' => 'privacy:metadata:role_capabilities:roleid',
|
||||
'capability' => 'privacy:metadata:role_capabilities:capability',
|
||||
'permission' => 'privacy:metadata:role_capabilities:permission',
|
||||
'timemodified' => 'privacy:metadata:role_capabilities:timemodified',
|
||||
'modifierid' => 'privacy:metadata:role_capabilities:modifierid'
|
||||
];
|
||||
$roleassignments = [
|
||||
'roleid' => 'privacy:metadata:role_assignments:roleid',
|
||||
'userid' => 'privacy:metadata:role_assignments:userid',
|
||||
'timemodified' => 'privacy:metadata:role_assignments:timemodified',
|
||||
'modifierid' => 'privacy:metadata:role_assignments:modifierid',
|
||||
'component' => 'privacy:metadata:role_assignments:component',
|
||||
'itemid' => 'privacy:metadata:role_assignments:itemid'
|
||||
];
|
||||
$collection->add_database_table('role_capabilities', $rolecapabilities,
|
||||
'privacy:metadata:role_capabilities:tableexplanation');
|
||||
$collection->add_database_table('role_assignments', $roleassignments,
|
||||
'privacy:metadata:role_assignments:tableexplanation');
|
||||
|
||||
$collection->add_user_preference('definerole_showadvanced',
|
||||
'privacy:metadata:preference:showadvanced');
|
||||
|
||||
return $collection;
|
||||
}
|
||||
/**
|
||||
* Export all user preferences for the plugin.
|
||||
*
|
||||
* @param int $userid The userid of the user whose data is to be exported.
|
||||
*/
|
||||
public static function export_user_preferences($userid) {
|
||||
$showadvanced = get_user_preferences('definerole_showadvanced', null, $userid);
|
||||
if ($showadvanced !== null) {
|
||||
writer::export_user_preference('core_role',
|
||||
'definerole_showadvanced',
|
||||
transform::yesno($showadvanced),
|
||||
get_string('privacy:metadata:preference:showadvanced', 'core_role')
|
||||
);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Return all contexts for this userid.
|
||||
*
|
||||
* @param int $userid The user ID.
|
||||
* @return contextlist The list of context IDs.
|
||||
*/
|
||||
public static function get_contexts_for_userid($userid) {
|
||||
global $DB;
|
||||
|
||||
$contextlist = new contextlist();
|
||||
|
||||
// The role_capabilities table contains user data.
|
||||
$contexts = [
|
||||
CONTEXT_SYSTEM,
|
||||
CONTEXT_USER,
|
||||
CONTEXT_COURSECAT,
|
||||
CONTEXT_COURSE,
|
||||
CONTEXT_MODULE,
|
||||
CONTEXT_BLOCK
|
||||
];
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
|
||||
$sql = "SELECT ctx.id
|
||||
FROM {context} ctx
|
||||
JOIN {role_capabilities} rc
|
||||
ON rc.contextid = ctx.id
|
||||
AND ((ctx.contextlevel {$insql} AND rc.modifierid = :modifierid)
|
||||
OR (ctx.contextlevel = :contextlevel AND ctx.instanceid = :userid))";
|
||||
$params = [
|
||||
'modifierid' => $userid,
|
||||
'contextlevel' => CONTEXT_USER,
|
||||
'userid' => $userid
|
||||
];
|
||||
$params += $inparams;
|
||||
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
// The role_assignments table contains user data.
|
||||
$contexts = [
|
||||
CONTEXT_SYSTEM,
|
||||
CONTEXT_USER,
|
||||
CONTEXT_COURSECAT,
|
||||
CONTEXT_COURSE,
|
||||
CONTEXT_MODULE,
|
||||
CONTEXT_BLOCK
|
||||
];
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
|
||||
$params = [
|
||||
'userid' => $userid,
|
||||
'modifierid' => $userid
|
||||
];
|
||||
$params += $inparams;
|
||||
$sql = "SELECT ctx.id
|
||||
FROM {role_assignments} ra
|
||||
JOIN {context} ctx
|
||||
ON ctx.id = ra.contextid
|
||||
AND ctx.contextlevel {$insql}
|
||||
WHERE (ra.userid = :userid
|
||||
OR ra.modifierid = :modifierid)
|
||||
AND ra.component != 'tool_cohortroles'";
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
return $contextlist;
|
||||
}
|
||||
/**
|
||||
* Export all user data for the specified user, in the specified contexts.
|
||||
*
|
||||
* @param approved_contextlist $contextlist The list of approved contexts for a user.
|
||||
*/
|
||||
public static function export_user_data(approved_contextlist $contextlist) {
|
||||
global $DB;
|
||||
|
||||
if (empty($contextlist)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rolesnames = self::get_roles_name();
|
||||
$userid = $contextlist->get_user()->id;
|
||||
$ctxfields = \context_helper::get_preload_record_columns_sql('ctx');
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
|
||||
|
||||
// Role Assignments export data.
|
||||
$contexts = [
|
||||
CONTEXT_SYSTEM,
|
||||
CONTEXT_USER,
|
||||
CONTEXT_COURSECAT,
|
||||
CONTEXT_COURSE,
|
||||
CONTEXT_MODULE,
|
||||
CONTEXT_BLOCK
|
||||
];
|
||||
list($inctxsql, $ctxparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
|
||||
$sql = "SELECT ra.id, ra.contextid, ra.roleid, ra.userid, ra.timemodified, ra.modifierid, $ctxfields
|
||||
FROM {role_assignments} ra
|
||||
JOIN {context} ctx
|
||||
ON ctx.id = ra.contextid
|
||||
AND ctx.contextlevel {$inctxsql}
|
||||
AND (ra.userid = :userid OR ra.modifierid = :modifierid)
|
||||
AND ra.component != 'tool_cohortroles'
|
||||
JOIN {role} r
|
||||
ON r.id = ra.roleid
|
||||
WHERE ctx.id {$insql}";
|
||||
$params = ['userid' => $userid, 'modifierid' => $userid];
|
||||
$params += $inparams;
|
||||
$params += $ctxparams;
|
||||
$assignments = $DB->get_recordset_sql($sql, $params);
|
||||
foreach ($assignments as $assignment) {
|
||||
\context_helper::preload_from_record($assignment);
|
||||
$alldata[$assignment->contextid][$rolesnames[$assignment->roleid]][] = (object)[
|
||||
'timemodified' => transform::datetime($assignment->timemodified),
|
||||
'userid' => transform::user($assignment->userid),
|
||||
'modifierid' => transform::user($assignment->modifierid)
|
||||
];
|
||||
}
|
||||
$assignments->close();
|
||||
if (!empty($alldata)) {
|
||||
array_walk($alldata, function($roledata, $contextid) {
|
||||
$context = \context::instance_by_id($contextid);
|
||||
array_walk($roledata, function($data, $rolename) use ($context) {
|
||||
writer::with_context($context)->export_data(
|
||||
[get_string('privacy:metadata:role_assignments', 'core_role'), $rolename],
|
||||
(object)$data);
|
||||
});
|
||||
});
|
||||
unset($alldata);
|
||||
}
|
||||
|
||||
// Role Capabilities export data.
|
||||
$strpermissions = self::get_permissions_name();
|
||||
$contexts = [
|
||||
CONTEXT_SYSTEM,
|
||||
CONTEXT_USER,
|
||||
CONTEXT_COURSECAT,
|
||||
CONTEXT_COURSE,
|
||||
CONTEXT_MODULE,
|
||||
CONTEXT_BLOCK
|
||||
];
|
||||
list($inctxsql, $ctxparams) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED);
|
||||
$sql = "SELECT rc.id, rc.contextid, rc.capability, rc.permission, rc.timemodified, rc.roleid, $ctxfields
|
||||
FROM {context} ctx
|
||||
JOIN {role_capabilities} rc
|
||||
ON rc.contextid = ctx.id
|
||||
AND ((ctx.contextlevel {$inctxsql} AND rc.modifierid = :modifierid)
|
||||
OR (ctx.contextlevel = :contextlevel AND ctx.instanceid = :userid))
|
||||
WHERE ctx.id {$insql}";
|
||||
$params = [
|
||||
'modifierid' => $userid,
|
||||
'contextlevel' => CONTEXT_USER,
|
||||
'userid' => $userid
|
||||
];
|
||||
$params += $inparams;
|
||||
$params += $ctxparams;
|
||||
$capabilities = $DB->get_recordset_sql($sql, $params);
|
||||
foreach ($capabilities as $capability) {
|
||||
\context_helper::preload_from_record($capability);
|
||||
$alldata[$capability->contextid][$rolesnames[$capability->roleid]][] = (object)[
|
||||
'timemodified' => transform::datetime($capability->timemodified),
|
||||
'capability' => $capability->capability,
|
||||
'permission' => $strpermissions[$capability->permission]
|
||||
];
|
||||
}
|
||||
$capabilities->close();
|
||||
if (!empty($alldata)) {
|
||||
array_walk($alldata, function($capdata, $contextid) {
|
||||
$context = \context::instance_by_id($contextid);
|
||||
array_walk($capdata, function($data, $rolename) use ($context) {
|
||||
writer::with_context($context)->export_data(
|
||||
[get_string('privacy:metadata:role_capabilities', 'core_role'), $rolename],
|
||||
(object)$data);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Exports the data relating to tool_cohortroles component on role assignments by
|
||||
* Assign user roles to cohort feature.
|
||||
*
|
||||
* @param int $userid The user ID.
|
||||
*/
|
||||
public static function export_user_role_to_cohort($userid) {
|
||||
global $DB;
|
||||
|
||||
$rolesnames = self::get_roles_name();
|
||||
$sql = "SELECT ra.id, ra.contextid, ra.roleid, ra.userid, ra.timemodified, ra.modifierid, r.id as roleid
|
||||
FROM {role_assignments} ra
|
||||
JOIN {context} ctx
|
||||
ON ctx.id = ra.contextid
|
||||
AND ctx.contextlevel = :contextlevel
|
||||
AND ra.component = 'tool_cohortroles'
|
||||
JOIN {role} r
|
||||
ON r.id = ra.roleid
|
||||
WHERE ctx.instanceid = :instanceid
|
||||
OR ra.userid = :userid";
|
||||
$params = ['userid' => $userid, 'instanceid' => $userid, 'contextlevel' => CONTEXT_USER];
|
||||
$assignments = $DB->get_recordset_sql($sql, $params);
|
||||
foreach ($assignments as $assignment) {
|
||||
$alldata[$assignment->contextid][$rolesnames[$assignment->roleid]][] = (object)[
|
||||
'timemodified' => transform::datetime($assignment->timemodified),
|
||||
'userid' => transform::user($assignment->userid),
|
||||
'modifierid' => transform::user($assignment->modifierid)
|
||||
];
|
||||
}
|
||||
$assignments->close();
|
||||
if (!empty($alldata)) {
|
||||
array_walk($alldata, function($roledata, $contextid) {
|
||||
$context = \context::instance_by_id($contextid);
|
||||
array_walk($roledata, function($data, $rolename) use ($context) {
|
||||
writer::with_context($context)->export_related_data(
|
||||
[get_string('privacy:metadata:role_cohortroles', 'core_role'), $rolename], 'cohortroles',
|
||||
(object)$data);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Delete all user data for this context.
|
||||
*
|
||||
* @param \context $context The context to delete data for.
|
||||
*/
|
||||
public static function delete_data_for_all_users_in_context(\context $context) {
|
||||
global $DB;
|
||||
|
||||
// Don't remove data from role_capabilities.
|
||||
// Because this data affects the whole Moodle, there are override capabilities.
|
||||
// Don't belong to the modifier user.
|
||||
|
||||
// Remove data from role_assignments.
|
||||
$DB->delete_records('role_assignments', ['contextid' => $context->id]);
|
||||
}
|
||||
/**
|
||||
* Delete all user data for this user only.
|
||||
*
|
||||
* @param approved_contextlist $contextlist The list of approved contexts for a user.
|
||||
*/
|
||||
public static function delete_data_for_user(approved_contextlist $contextlist) {
|
||||
global $DB;
|
||||
|
||||
// Don't remove data from role_capabilities.
|
||||
// Because this data affects the whole Moodle, there are override capabilities.
|
||||
// Don't belong to the modifier user.
|
||||
|
||||
// Remove data from role_assignments.
|
||||
if (empty($contextlist->count())) {
|
||||
return;
|
||||
}
|
||||
$userid = $contextlist->get_user()->id;
|
||||
foreach ($contextlist->get_contexts() as $context) {
|
||||
// Only delete the roles assignments where the user is assigned in all contexts.
|
||||
$DB->delete_records('role_assignments', ['userid' => $userid, 'contextid' => $context->id]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Delete user entries in role_assignments related to the feature
|
||||
* Assign user roles to cohort feature.
|
||||
*
|
||||
* @param int $userid The user ID.
|
||||
*/
|
||||
public static function delete_user_role_to_cohort($userid) {
|
||||
global $DB;
|
||||
|
||||
// Delete entries where userid is a mentor by tool_cohortroles.
|
||||
$DB->delete_records('role_assignments', ['userid' => $userid, 'component' => 'tool_cohortroles']);
|
||||
}
|
||||
/**
|
||||
* Get all the localised roles name in a simple array.
|
||||
*
|
||||
* @return array Array of name of the roles by roleid.
|
||||
*/
|
||||
protected static function get_roles_name() {
|
||||
$roles = role_fix_names(get_all_roles(), \context_system::instance(), ROLENAME_ORIGINAL);
|
||||
$rolesnames = array();
|
||||
foreach ($roles as $role) {
|
||||
$rolesnames[$role->id] = $role->localname;
|
||||
}
|
||||
return $rolesnames;
|
||||
}
|
||||
/**
|
||||
* Get all the permissions name in a simple array.
|
||||
*
|
||||
* @return array Array of permissions name.
|
||||
*/
|
||||
protected static function get_permissions_name() {
|
||||
$strpermissions = array(
|
||||
CAP_INHERIT => get_string('inherit', 'role'),
|
||||
CAP_ALLOW => get_string('allow', 'role'),
|
||||
CAP_PREVENT => get_string('prevent', 'role'),
|
||||
CAP_PROHIBIT => get_string('prohibit', 'role')
|
||||
);
|
||||
return $strpermissions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,477 @@
|
||||
<?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/>.
|
||||
/**
|
||||
* Privacy test for core_role
|
||||
*
|
||||
* @package core_role
|
||||
* @category test
|
||||
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
use \core_role\privacy\provider;
|
||||
use \core_privacy\local\request\approved_contextlist;
|
||||
use \core_privacy\local\request\writer;
|
||||
use \core_privacy\tests\provider_testcase;
|
||||
use \core_privacy\local\request\transform;
|
||||
use \tool_cohortroles\api;
|
||||
|
||||
/**
|
||||
* Privacy test for core_role
|
||||
*
|
||||
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_role_privacy_testcase extends provider_testcase {
|
||||
/**
|
||||
* Test to check export_user_preferences.
|
||||
* returns user preferences data.
|
||||
*/
|
||||
public function test_export_user_preferences() {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
$showadvanced = 1;
|
||||
set_user_preference('definerole_showadvanced', $showadvanced);
|
||||
provider::export_user_preferences($user->id);
|
||||
$writer = writer::with_context(\context_system::instance());
|
||||
$prefs = $writer->get_user_preferences('core_role');
|
||||
$this->assertEquals(transform::yesno($showadvanced), transform::yesno($prefs->definerole_showadvanced->value));
|
||||
$this->assertEquals(get_string('privacy:metadata:preference:showadvanced', 'core_role'),
|
||||
$prefs->definerole_showadvanced->description);
|
||||
}
|
||||
/**
|
||||
* Check all contexts are returned if there is any user data for this user.
|
||||
*/
|
||||
public function test_get_contexts_for_userid() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->assertEmpty(provider::get_contexts_for_userid($user->id));
|
||||
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$usercontext2 = \context_user::instance($user2->id);
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$course2 = $this->getDataGenerator()->create_course();
|
||||
$coursecat = $this->getDataGenerator()->create_category();
|
||||
$cm = $this->getDataGenerator()->create_module('chat', ['course' => $course->id]);
|
||||
$cmcontext = \context_module::instance($cm->cmid);
|
||||
$page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
|
||||
$cmcontext2 = \context_module::instance($page->cmid);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
$coursecontext2 = \context_course::instance($course2->id);
|
||||
$coursecatcontext = \context_coursecat::instance($coursecat->id);
|
||||
$systemcontext = \context_system::instance();
|
||||
$block = $this->getDataGenerator()->create_block('online_users');
|
||||
$blockcontext = \context_block::instance($block->id);
|
||||
|
||||
$student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
|
||||
$manager = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
|
||||
|
||||
// Role assignments, where the user is assigned.
|
||||
role_assign($student->id, $user->id, $cmcontext2->id);
|
||||
role_assign($student->id, $user->id, $coursecontext2->id);
|
||||
role_assign($student->id, $user->id, $blockcontext->id);
|
||||
role_assign($manager->id, $user->id, $usercontext2->id);
|
||||
// Role assignments, where the user makes assignments.
|
||||
$this->setUser($user);
|
||||
role_assign($student->id, $user2->id, $coursecontext->id);
|
||||
role_assign($manager->id, $user2->id, $coursecatcontext->id);
|
||||
role_assign($manager->id, $user2->id, $systemcontext->id);
|
||||
|
||||
// Role capabilities.
|
||||
$this->setUser($user);
|
||||
$result = assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $student->id, $cmcontext->id);
|
||||
|
||||
$contextlist = provider::get_contexts_for_userid($user->id)->get_contextids();
|
||||
$this->assertCount(8, $contextlist);
|
||||
$this->assertTrue(in_array($cmcontext->id, $contextlist));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that user data is exported correctly.
|
||||
*/
|
||||
public function test_export_user_data() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$usercontext2 = \context_user::instance($user2->id);
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$course2 = $this->getDataGenerator()->create_course();
|
||||
$coursecat = $this->getDataGenerator()->create_category();
|
||||
$cm = $this->getDataGenerator()->create_module('chat', ['course' => $course->id]);
|
||||
$cmcontext = \context_module::instance($cm->cmid);
|
||||
$page = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
|
||||
$cmcontext2 = \context_module::instance($page->cmid);
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
$coursecontext2 = \context_course::instance($course2->id);
|
||||
$coursecatcontext = \context_coursecat::instance($coursecat->id);
|
||||
$systemcontext = \context_system::instance();
|
||||
$block = $this->getDataGenerator()->create_block('online_users');
|
||||
$blockcontext = \context_block::instance($block->id);
|
||||
|
||||
$student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
|
||||
$manager = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
|
||||
$rolesnames = self::get_roles_name();
|
||||
|
||||
$subcontextstudent = [
|
||||
get_string('privacy:metadata:role_assignments', 'core_role'),
|
||||
$rolesnames[$student->id]
|
||||
];
|
||||
$subcontextmanager = [
|
||||
get_string('privacy:metadata:role_assignments', 'core_role'),
|
||||
$rolesnames[$manager->id]
|
||||
];
|
||||
$subcontextrc = [
|
||||
get_string('privacy:metadata:role_capabilities', 'core_role'),
|
||||
$rolesnames[$student->id]
|
||||
];
|
||||
|
||||
// Test over role assignments.
|
||||
// Where the user is assigned.
|
||||
role_assign($student->id, $user->id, $cmcontext2->id);
|
||||
role_assign($student->id, $user->id, $coursecontext2->id);
|
||||
role_assign($student->id, $user->id, $blockcontext->id);
|
||||
role_assign($manager->id, $user->id, $usercontext2->id);
|
||||
// Where the user makes assignments.
|
||||
$this->setUser($user);
|
||||
role_assign($manager->id, $user2->id, $coursecatcontext->id);
|
||||
role_assign($manager->id, $user2->id, $systemcontext->id);
|
||||
|
||||
// Test overridable roles in module, course, category, user, system and block.
|
||||
assign_capability('moodle/backup:backupactivity', CAP_ALLOW, $student->id, $cmcontext->id, true);
|
||||
assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $student->id, $coursecontext->id, true);
|
||||
assign_capability('moodle/category:manage', CAP_ALLOW, $student->id, $coursecatcontext->id, true);
|
||||
assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $student->id, $systemcontext->id, true);
|
||||
assign_capability('moodle/block:edit', CAP_ALLOW, $student->id, $blockcontext->id, true);
|
||||
assign_capability('moodle/competency:evidencedelete', CAP_ALLOW, $student->id, $usercontext2->id, true);
|
||||
|
||||
// Retrieve the user's context ids.
|
||||
$contextlist = provider::get_contexts_for_userid($user->id);
|
||||
$approvedcontextlist = new approved_contextlist($user, 'core_role', $contextlist->get_contextids());
|
||||
|
||||
$strpermissions = array(
|
||||
CAP_INHERIT => get_string('inherit', 'role'),
|
||||
CAP_ALLOW => get_string('allow', 'role'),
|
||||
CAP_PREVENT => get_string('prevent', 'role'),
|
||||
CAP_PROHIBIT => get_string('prohibit', 'role')
|
||||
);
|
||||
// Retrieve role capabilities and role assignments.
|
||||
provider::export_user_data($approvedcontextlist);
|
||||
foreach ($contextlist as $context) {
|
||||
$writer = writer::with_context($context);
|
||||
$this->assertTrue($writer->has_any_data());
|
||||
if ($context->contextlevel == CONTEXT_MODULE) {
|
||||
if ($data = $writer->get_data($subcontextstudent)) {
|
||||
$this->assertEquals($user->id, reset($data)->userid);
|
||||
}
|
||||
if ($data = $writer->get_data($subcontextrc)) {
|
||||
$this->assertEquals('moodle/backup:backupactivity', reset($data)->capability);
|
||||
$this->assertEquals($strpermissions[CAP_ALLOW], reset($data)->permission);
|
||||
}
|
||||
}
|
||||
if ($context->contextlevel == CONTEXT_COURSE) {
|
||||
if ($data = $writer->get_data($subcontextstudent)) {
|
||||
$this->assertEquals($user->id, reset($data)->userid);
|
||||
}
|
||||
if ($data = $writer->get_data($subcontextrc)) {
|
||||
$this->assertEquals('moodle/backup:backupcourse', reset($data)->capability);
|
||||
}
|
||||
}
|
||||
if ($context->contextlevel == CONTEXT_COURSECAT) {
|
||||
if ($data = $writer->get_data($subcontextmanager)) {
|
||||
$this->assertEquals($user->id, reset($data)->modifierid);
|
||||
}
|
||||
if ($data = $writer->get_data($subcontextrc)) {
|
||||
$this->assertEquals('moodle/category:manage', reset($data)->capability);
|
||||
}
|
||||
}
|
||||
if ($context->contextlevel == CONTEXT_SYSTEM) {
|
||||
if ($data = $writer->get_data($subcontextmanager)) {
|
||||
$this->assertEquals($user->id, reset($data)->modifierid);
|
||||
}
|
||||
if ($data = $writer->get_data($subcontextrc)) {
|
||||
$this->assertEquals('moodle/backup:backupcourse', reset($data)->capability);
|
||||
}
|
||||
}
|
||||
if ($context->contextlevel == CONTEXT_BLOCK) {
|
||||
if ($data = $writer->get_data($subcontextstudent)) {
|
||||
$this->assertEquals($user->id, reset($data)->userid);
|
||||
}
|
||||
if ($data = $writer->get_data($subcontextrc)) {
|
||||
$this->assertEquals('moodle/block:edit', reset($data)->capability);
|
||||
}
|
||||
}
|
||||
if ($context->contextlevel == CONTEXT_USER) {
|
||||
if ($data = $writer->get_data($subcontextmanager)) {
|
||||
$this->assertEquals($user->id, reset($data)->userid);
|
||||
}
|
||||
if ($data = $writer->get_data($subcontextrc)) {
|
||||
$this->assertEquals('moodle/competency:evidencedelete', reset($data)->capability);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Test for provider::delete_data_for_all_users_in_context().
|
||||
*/
|
||||
public function test_delete_data_for_all_users_in_context() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$usercontext2 = \context_user::instance($user2->id);
|
||||
$user3 = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
$coursecat = $this->getDataGenerator()->create_category();
|
||||
$coursecatcontext = \context_coursecat::instance($coursecat->id);
|
||||
$systemcontext = \context_system::instance();
|
||||
$cm = $this->getDataGenerator()->create_module('chat', ['course' => $course->id]);
|
||||
$cmcontext = \context_module::instance($cm->cmid);
|
||||
$student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
|
||||
$manager = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
|
||||
$block = $this->getDataGenerator()->create_block('online_users');
|
||||
$blockcontext = \context_block::instance($block->id);
|
||||
|
||||
// Role assignments CONTEXT_COURSE.
|
||||
role_assign($student->id, $user->id, $coursecontext->id);
|
||||
role_assign($student->id, $user2->id, $coursecontext->id);
|
||||
role_assign($student->id, $user3->id, $coursecontext->id);
|
||||
$count = $DB->count_records('role_assignments', ['contextid' => $coursecontext->id]);
|
||||
$this->assertEquals(3, $count);
|
||||
// Role assignments CONTEXT_COURSECAT.
|
||||
role_assign($student->id, $user2->id, $coursecatcontext->id);
|
||||
role_assign($student->id, $user3->id, $coursecatcontext->id);
|
||||
$count = $DB->count_records('role_assignments', ['contextid' => $coursecatcontext->id]);
|
||||
$this->assertEquals(2, $count);
|
||||
// Role assignments CONTEXT_SYSTEM.
|
||||
role_assign($student->id, $user->id, $systemcontext->id);
|
||||
$count = $DB->count_records('role_assignments', ['contextid' => $systemcontext->id]);
|
||||
$this->assertEquals(1, $count);
|
||||
// Role assignments CONTEXT_MODULE.
|
||||
role_assign($student->id, $user->id, $cmcontext->id);
|
||||
$count = $DB->count_records('role_assignments', ['contextid' => $cmcontext->id]);
|
||||
$this->assertEquals(1, $count);
|
||||
// Role assigments CONTEXT_BLOCK.
|
||||
role_assign($student->id, $user->id, $blockcontext->id);
|
||||
$count = $DB->count_records('role_assignments', ['contextid' => $blockcontext->id]);
|
||||
$this->assertEquals(1, $count);
|
||||
// Role assigments CONTEXT_USER.
|
||||
role_assign($manager->id, $user->id, $usercontext2->id);
|
||||
$count = $DB->count_records('role_assignments', ['contextid' => $usercontext2->id]);
|
||||
$this->assertEquals(1, $count);
|
||||
|
||||
// Delete data based on CONTEXT_COURSE context.
|
||||
provider::delete_data_for_all_users_in_context($coursecontext);
|
||||
// After deletion, the role_assignments entries for this context should have been deleted.
|
||||
$count = $DB->count_records('role_assignments', ['contextid' => $coursecontext->id]);
|
||||
$this->assertEquals(0, $count);
|
||||
// Check it is not removing data on other contexts.
|
||||
$count = $DB->count_records('role_assignments', ['contextid' => $coursecatcontext->id]);
|
||||
$this->assertEquals(2, $count);
|
||||
$count = $DB->count_records('role_assignments', ['contextid' => $systemcontext->id]);
|
||||
$this->assertEquals(1, $count);
|
||||
$count = $DB->count_records('role_assignments', ['contextid' => $cmcontext->id]);
|
||||
$this->assertEquals(1, $count);
|
||||
// Delete data based on CONTEXT_COURSECAT context.
|
||||
provider::delete_data_for_all_users_in_context($coursecatcontext);
|
||||
// After deletion, the role_assignments entries for this context should have been deleted.
|
||||
$count = $DB->count_records('role_assignments', ['contextid' => $coursecatcontext->id]);
|
||||
$this->assertEquals(0, $count);
|
||||
// Delete data based on CONTEXT_SYSTEM context.
|
||||
provider::delete_data_for_all_users_in_context($systemcontext);
|
||||
// After deletion, the role_assignments entries for this context should have been deleted.
|
||||
$count = $DB->count_records('role_assignments', ['contextid' => $systemcontext->id]);
|
||||
$this->assertEquals(0, $count);
|
||||
// Delete data based on CONTEXT_MODULE context.
|
||||
provider::delete_data_for_all_users_in_context($cmcontext);
|
||||
// After deletion, the role_assignments entries for this context should have been deleted.
|
||||
$count = $DB->count_records('role_assignments', ['contextid' => $cmcontext->id]);
|
||||
$this->assertEquals(0, $count);
|
||||
// Delete data based on CONTEXT_BLOCK context.
|
||||
provider::delete_data_for_all_users_in_context($usercontext2);
|
||||
// After deletion, the role_assignments entries for this context should have been deleted.
|
||||
$count = $DB->count_records('role_assignments', ['contextid' => $usercontext2->id]);
|
||||
$this->assertEquals(0, $count);
|
||||
}
|
||||
/**
|
||||
* Test for provider::delete_data_for_user().
|
||||
*/
|
||||
public function test_delete_data_for_user() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$usercontext2 = \context_user::instance($user2->id);
|
||||
$user3 = $this->getDataGenerator()->create_user();
|
||||
$usercontext3 = \context_user::instance($user3->id);
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$course2 = $this->getDataGenerator()->create_course();
|
||||
$course3 = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
$coursecontext2 = \context_course::instance($course2->id);
|
||||
$coursecontext3 = \context_course::instance($course3->id);
|
||||
$coursecat = $this->getDataGenerator()->create_category();
|
||||
$coursecatcontext = \context_coursecat::instance($coursecat->id);
|
||||
$systemcontext = \context_system::instance();
|
||||
$cm = $this->getDataGenerator()->create_module('chat', ['course' => $course->id]);
|
||||
$cmcontext = \context_module::instance($cm->cmid);
|
||||
$student = $DB->get_record('role', array('shortname' => 'student'), '*', MUST_EXIST);
|
||||
$manager = $DB->get_record('role', array('shortname' => 'manager'), '*', MUST_EXIST);
|
||||
$block = $this->getDataGenerator()->create_block('online_users');
|
||||
$blockcontext = \context_block::instance($block->id);
|
||||
|
||||
// Role assignments, Where the user is assigned.
|
||||
role_assign($student->id, $user->id, $coursecontext->id);
|
||||
role_assign($student->id, $user->id, $coursecontext2->id);
|
||||
role_assign($student->id, $user->id, $coursecatcontext->id);
|
||||
role_assign($student->id, $user->id, $cmcontext->id);
|
||||
role_assign($student->id, $user->id, $systemcontext->id);
|
||||
role_assign($student->id, $user->id, $blockcontext->id);
|
||||
role_assign($manager->id, $user->id, $usercontext2->id);
|
||||
role_assign($manager->id, $user->id, $usercontext3->id);
|
||||
$count = $DB->count_records('role_assignments', ['userid' => $user->id]);
|
||||
$this->assertEquals(8, $count);
|
||||
// Role assignments, where the user makes assignments.
|
||||
$this->setUser($user);
|
||||
role_assign($student->id, $user2->id, $coursecontext3->id);
|
||||
role_assign($student->id, $user3->id, $coursecontext3->id);
|
||||
$count = $DB->count_records('role_assignments', ['modifierid' => $user->id]);
|
||||
$this->assertEquals(2, $count);
|
||||
|
||||
$contextlist = provider::get_contexts_for_userid($user->id);
|
||||
$approvedcontextlist = new approved_contextlist($user, 'core_role', $contextlist->get_contextids());
|
||||
provider::delete_data_for_user($approvedcontextlist);
|
||||
// After deletion, the role_assignments assigned to the user should have been deleted.
|
||||
$count = $DB->count_records('role_assignments', ['userid' => $user->id]);
|
||||
$this->assertEquals(0, $count);
|
||||
// After deletion, the role_assignments assigned by the user should not have been deleted.
|
||||
$count = $DB->count_records('role_assignments', ['modifierid' => $user->id]);
|
||||
$this->assertEquals(2, $count);
|
||||
}
|
||||
/**
|
||||
* Export for a user with a key against a script where no instance is specified.
|
||||
*/
|
||||
public function test_export_user_role_to_cohort() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
// Assign user roles to cohort.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$contextuser = \context_user::instance($user->id);
|
||||
$teacher = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
|
||||
$cohort = $this->getDataGenerator()->create_cohort();
|
||||
$userassignover = $this->getDataGenerator()->create_user();
|
||||
$contextuserassignover = \context_user::instance($userassignover->id);
|
||||
cohort_add_member($cohort->id, $userassignover->id);
|
||||
$this->setAdminUser();
|
||||
$params = (object) array(
|
||||
'userid' => $user->id,
|
||||
'roleid' => $teacher->id,
|
||||
'cohortid' => $cohort->id
|
||||
);
|
||||
api::create_cohort_role_assignment($params);
|
||||
api::sync_all_cohort_roles();
|
||||
$rolesnames = self::get_roles_name();
|
||||
$subcontextteacher = [
|
||||
get_string('privacy:metadata:role_cohortroles', 'core_role'),
|
||||
$rolesnames[$teacher->id]
|
||||
];
|
||||
// Test User is assigned role teacher to cohort.
|
||||
provider::export_user_role_to_cohort($user->id);
|
||||
$writer = writer::with_context($contextuserassignover);
|
||||
$this->assertTrue($writer->has_any_data());
|
||||
$exported = $writer->get_related_data($subcontextteacher, 'cohortroles');
|
||||
$this->assertEquals($user->id, reset($exported)->userid);
|
||||
|
||||
// Test User is member of a cohort which User2 is assigned to role to this cohort.
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$cohort2 = $this->getDataGenerator()->create_cohort();
|
||||
cohort_add_member($cohort2->id, $user->id);
|
||||
$params = (object) array(
|
||||
'userid' => $user2->id,
|
||||
'roleid' => $teacher->id,
|
||||
'cohortid' => $cohort2->id
|
||||
);
|
||||
api::create_cohort_role_assignment($params);
|
||||
api::sync_all_cohort_roles();
|
||||
provider::export_user_role_to_cohort($user->id);
|
||||
$writer = writer::with_context($contextuser);
|
||||
$this->assertTrue($writer->has_any_data());
|
||||
$exported = $writer->get_related_data($subcontextteacher, 'cohortroles');
|
||||
$this->assertEquals($user2->id, reset($exported)->userid);
|
||||
}
|
||||
/**
|
||||
* Test for provider::delete_user_role_to_cohort().
|
||||
*/
|
||||
public function test_delete_user_role_to_cohort() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
// Assign user roles to cohort.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$user3 = $this->getDataGenerator()->create_user();
|
||||
$user4 = $this->getDataGenerator()->create_user();
|
||||
$teacher = $DB->get_record('role', array('shortname' => 'teacher'), '*', MUST_EXIST);
|
||||
$cohort = $this->getDataGenerator()->create_cohort();
|
||||
cohort_add_member($cohort->id, $user2->id);
|
||||
cohort_add_member($cohort->id, $user3->id);
|
||||
cohort_add_member($cohort->id, $user4->id);
|
||||
$this->setAdminUser();
|
||||
$params = (object) array(
|
||||
'userid' => $user->id,
|
||||
'roleid' => $teacher->id,
|
||||
'cohortid' => $cohort->id
|
||||
);
|
||||
api::create_cohort_role_assignment($params);
|
||||
api::sync_all_cohort_roles();
|
||||
|
||||
$count = $DB->count_records('role_assignments', ['userid' => $user->id, 'component' => 'tool_cohortroles']);
|
||||
$this->assertEquals(3, $count);
|
||||
|
||||
provider::delete_user_role_to_cohort($user->id);
|
||||
$count = $DB->count_records('role_assignments', ['userid' => $user->id, 'component' => 'tool_cohortroles']);
|
||||
$this->assertEquals(0, $count);
|
||||
}
|
||||
/**
|
||||
* Supoort function to get all the localised roles name
|
||||
* in a simple array for testing.
|
||||
*
|
||||
* @return array Array of name of the roles by roleid.
|
||||
*/
|
||||
protected static function get_roles_name() {
|
||||
$roles = role_fix_names(get_all_roles(), \context_system::instance(), ROLENAME_ORIGINAL);
|
||||
$rolesnames = array();
|
||||
foreach ($roles as $role) {
|
||||
$rolesnames[$role->id] = $role->localname;
|
||||
}
|
||||
return $rolesnames;
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -29,15 +29,16 @@ $focus = '';
|
||||
// now we'll deal with the case that the admin has submitted the form with changed settings
|
||||
if ($data = data_submitted() and confirm_sesskey() and isset($data->action) and $data->action == 'save-settings') {
|
||||
require_capability('moodle/site:config', $context);
|
||||
if (admin_write_settings($data)) {
|
||||
redirect($PAGE->url, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
|
||||
}
|
||||
|
||||
$count = admin_write_settings($data);
|
||||
if (!empty($adminroot->errors)) {
|
||||
$errormsg = get_string('errorwithsettings', 'admin');
|
||||
$firsterror = reset($adminroot->errors);
|
||||
$focus = $firsterror->id;
|
||||
} else {
|
||||
// No errors. Did we change any setting? If so, then redirect with success.
|
||||
if ($count) {
|
||||
redirect($PAGE->url, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-3
@@ -39,11 +39,15 @@ $statusmsg = '';
|
||||
$errormsg = '';
|
||||
|
||||
if ($data = data_submitted() and confirm_sesskey()) {
|
||||
if (admin_write_settings($data)) {
|
||||
redirect($PAGE->url, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
|
||||
}
|
||||
|
||||
$count = admin_write_settings($data);
|
||||
// Regardless of whether any setting change was written (a positive count), check validation errors for those that didn't.
|
||||
if (empty($adminroot->errors)) {
|
||||
// No errors. Did we change any setting? If so, then redirect with success.
|
||||
if ($count) {
|
||||
redirect($PAGE->url, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
|
||||
}
|
||||
// We didn't change a setting.
|
||||
switch ($return) {
|
||||
case 'site': redirect("$CFG->wwwroot/");
|
||||
case 'admin': redirect("$CFG->wwwroot/$CFG->admin/");
|
||||
|
||||
@@ -199,7 +199,7 @@ preferences,moodle|/user/preferences.php|preferences',
|
||||
|
||||
// "documentation" settingpage
|
||||
$temp = new admin_settingpage('documentation', new lang_string('moodledocs'));
|
||||
$temp->add(new admin_setting_configtext('docroot', new lang_string('docroot', 'admin'), new lang_string('configdocroot', 'admin'), 'http://docs.moodle.org', PARAM_URL));
|
||||
$temp->add(new admin_setting_configtext('docroot', new lang_string('docroot', 'admin'), new lang_string('configdocroot', 'admin'), 'https://docs.moodle.org', PARAM_URL));
|
||||
$ltemp = array('' => get_string('forceno'));
|
||||
$ltemp += get_string_manager()->get_list_of_translations(true);
|
||||
$temp->add(new admin_setting_configselect('doclang', get_string('doclang', 'admin'), get_string('configdoclang', 'admin'), '', $ltemp));
|
||||
|
||||
@@ -28,7 +28,9 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
|
||||
$ADMIN->add('development', $temp);
|
||||
|
||||
// "Profiling" settingpage (conditionally if the 'xhprof' extension is available only).
|
||||
$xhprofenabled = extension_loaded('xhprof') || extension_loaded('tideways');
|
||||
$xhprofenabled = extension_loaded('tideways_xhprof');
|
||||
$xhprofenabled = $xhprofenabled || extension_loaded('tideways');
|
||||
$xhprofenabled = $xhprofenabled || extension_loaded('xhprof');
|
||||
$temp = new admin_settingpage('profiling', new lang_string('profiling', 'admin'), 'moodle/site:config', !$xhprofenabled);
|
||||
// Main profiling switch.
|
||||
$temp->add(new admin_setting_configcheckbox('profilingenabled', new lang_string('profilingenabled', 'admin'), new lang_string('profilingenabled_help', 'admin'), false));
|
||||
|
||||
@@ -49,8 +49,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
|
||||
3600 => new lang_string('numminutes', '', 60))));
|
||||
|
||||
$temp->add(new admin_setting_configcheckbox('extendedusernamechars', new lang_string('extendedusernamechars', 'admin'), new lang_string('configextendedusernamechars', 'admin'), 0));
|
||||
$temp->add(new admin_setting_configtext('sitepolicy', new lang_string('sitepolicy', 'admin'), new lang_string('sitepolicy_help', 'admin'), '', PARAM_RAW));
|
||||
$temp->add(new admin_setting_configtext('sitepolicyguest', new lang_string('sitepolicyguest', 'admin'), new lang_string('sitepolicyguest_help', 'admin'), (isset($CFG->sitepolicy) ? $CFG->sitepolicy : ''), PARAM_RAW));
|
||||
|
||||
$temp->add(new admin_setting_configcheckbox('extendedusernamechars', new lang_string('extendedusernamechars', 'admin'), new lang_string('configextendedusernamechars', 'admin'), 0));
|
||||
$temp->add(new admin_setting_configcheckbox('keeptagnamecase', new lang_string('keeptagnamecase','admin'),new lang_string('configkeeptagnamecase', 'admin'),'1'));
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
$ADMIN->add('users', new admin_category('accounts', new lang_string('accounts', 'admin')));
|
||||
$ADMIN->add('users', new admin_category('roles', new lang_string('permissions', 'role')));
|
||||
$ADMIN->add('users', new admin_category('privacy', new lang_string('privacyandpolicies', 'admin')));
|
||||
|
||||
if ($hassiteconfig
|
||||
or has_capability('moodle/user:create', $systemcontext)
|
||||
@@ -212,3 +213,48 @@ if ($hassiteconfig
|
||||
$ADMIN->add('roles', new admin_externalpage('checkpermissions', new lang_string('checkglobalpermissions', 'role'), "$CFG->wwwroot/$CFG->admin/roles/check.php?contextid=".$systemcontext->id, array('moodle/role:assign', 'moodle/role:safeoverride', 'moodle/role:override', 'moodle/role:manage')));
|
||||
|
||||
} // end of speedup
|
||||
|
||||
// Privacy settings.
|
||||
if ($hassiteconfig) {
|
||||
$temp = new admin_settingpage('privacysettings', new lang_string('privacysettings', 'admin'));
|
||||
|
||||
$options = array(
|
||||
0 => get_string('no'),
|
||||
1 => get_string('yes')
|
||||
);
|
||||
$url = new moodle_url('/admin/settings.php?section=supportcontact');
|
||||
$url = $url->out();
|
||||
$setting = new admin_setting_configselect('agedigitalconsentverification',
|
||||
new lang_string('agedigitalconsentverification', 'admin'),
|
||||
new lang_string('agedigitalconsentverification_desc', 'admin', $url), 0, $options);
|
||||
$setting->set_force_ltr(true);
|
||||
$temp->add($setting);
|
||||
|
||||
// See {@link https://gdpr-info.eu/art-8-gdpr/}.
|
||||
$ageofdigitalconsentmap = implode(PHP_EOL, [
|
||||
'*, 16',
|
||||
'AT, 14',
|
||||
'ES, 14',
|
||||
'US, 13'
|
||||
]);
|
||||
$setting = new admin_setting_agedigitalconsentmap('agedigitalconsentmap',
|
||||
new lang_string('ageofdigitalconsentmap', 'admin'),
|
||||
new lang_string('ageofdigitalconsentmap_desc', 'admin'),
|
||||
$ageofdigitalconsentmap,
|
||||
PARAM_RAW
|
||||
);
|
||||
$temp->add($setting);
|
||||
|
||||
$ADMIN->add('privacy', $temp);
|
||||
|
||||
// Policy settings.
|
||||
$temp = new admin_settingpage('policysettings', new lang_string('policysettings', 'admin'));
|
||||
$temp->add(new admin_settings_sitepolicy_handler_select('sitepolicyhandler', new lang_string('sitepolicyhandler', 'core_admin'),
|
||||
new lang_string('sitepolicyhandler_desc', 'core_admin')));
|
||||
$temp->add(new admin_setting_configtext('sitepolicy', new lang_string('sitepolicy', 'core_admin'),
|
||||
new lang_string('sitepolicy_help', 'core_admin'), '', PARAM_RAW));
|
||||
$temp->add(new admin_setting_configtext('sitepolicyguest', new lang_string('sitepolicyguest', 'core_admin'),
|
||||
new lang_string('sitepolicyguest_help', 'core_admin'), (isset($CFG->sitepolicy) ? $CFG->sitepolicy : ''), PARAM_RAW));
|
||||
|
||||
$ADMIN->add('privacy', $temp);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Privacy Subsystem implementation for tool_assignmentupgrade.
|
||||
*
|
||||
* @package tool_assignmentupgrade
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_assignmentupgrade\privacy;
|
||||
|
||||
use core_privacy\local\metadata\collection;
|
||||
use core_privacy\local\request\writer;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_assignmentupgrade implementing metadata, plugin, and user_preference providers.
|
||||
*
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements
|
||||
\core_privacy\local\metadata\provider,
|
||||
\core_privacy\local\request\user_preference_provider {
|
||||
|
||||
/**
|
||||
* Returns meta data about this system.
|
||||
*
|
||||
* @param collection $collection The initialised collection to add items to.
|
||||
* @return collection A listing of user data stored through this system.
|
||||
*/
|
||||
public static function get_metadata(collection $collection) {
|
||||
$collection->add_user_preference(
|
||||
'tool_assignmentupgrade_perpage',
|
||||
'privacy:metadata:preference:perpage'
|
||||
);
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export all user preferences for the plugin.
|
||||
*
|
||||
* @param int $userid The userid of the user whose data is to be exported.
|
||||
*/
|
||||
public static function export_user_preferences($userid) {
|
||||
$perpage = get_user_preferences('tool_assignmentupgrade_perpage', null, $userid);
|
||||
if ($perpage !== null) {
|
||||
writer::export_user_preference(
|
||||
'tool_assignmentupgrade',
|
||||
'perpage',
|
||||
$perpage,
|
||||
get_string('privacy:metadata:preference:perpage', 'tool_assignmentupgrade')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -58,3 +58,4 @@ $string['upgradeallconfirm'] = 'Upgrade all assignments?';
|
||||
$string['upgradeprogress'] = 'Upgrade assignment {$a->current} of {$a->total}';
|
||||
$string['upgradesingle'] = 'Upgrade single assignment';
|
||||
$string['viewcourse'] = 'View the course with the converted assignment';
|
||||
$string['privacy:metadata:preference:perpage'] = 'The assignment upgrade records per page preference set for the user.';
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Privacy tests for tool_assignmentupgrade.
|
||||
*
|
||||
* @package tool_assignmentupgrade
|
||||
* @category test
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use \core_privacy\tests\provider_testcase;
|
||||
use \core_privacy\local\request\writer;
|
||||
use \tool_assignmentupgrade\privacy\provider;
|
||||
|
||||
/**
|
||||
* Unit tests for tool_assignmentupgrade/classes/privacy/policy
|
||||
*
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_assignmentupgrade_privacy_testcase extends provider_testcase {
|
||||
|
||||
/**
|
||||
* Overriding setUp() function to always reset after tests.
|
||||
*/
|
||||
public function setUp() {
|
||||
$this->resetAfterTest(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for provider::test_export_user_preferences().
|
||||
*/
|
||||
public function test_export_user_preferences() {
|
||||
// Test setup.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
|
||||
// Add a user home page preference for the User.
|
||||
set_user_preference('tool_assignmentupgrade_perpage', '100', $user);
|
||||
|
||||
// Test the user preference exists.
|
||||
$params = [
|
||||
'userid' => $user->id,
|
||||
'name' => 'tool_assignmentupgrade_perpage'
|
||||
];
|
||||
|
||||
// Test the user preferences export contains 1 user preference record for the User.
|
||||
provider::export_user_preferences($user->id);
|
||||
$contextuser = context_user::instance($user->id);
|
||||
$writer = writer::with_context($contextuser);
|
||||
$this->assertTrue($writer->has_any_data());
|
||||
|
||||
$exportedpreferences = $writer->get_user_preferences('tool_assignmentupgrade');
|
||||
$this->assertCount(1, (array) $exportedpreferences);
|
||||
$this->assertEquals('100', $exportedpreferences->perpage->value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Privacy Subsystem implementation for tool_availabilityconditions.
|
||||
*
|
||||
* @package tool_availabilityconditions
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_availabilityconditions\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_availabilityconditions implementing null_provider.
|
||||
*
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements \core_privacy\local\metadata\null_provider {
|
||||
|
||||
/**
|
||||
* Get the language string identifier with the component's language
|
||||
* file to explain why this plugin stores no data.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_reason() {
|
||||
return 'privacy:metadata';
|
||||
}
|
||||
}
|
||||
@@ -24,3 +24,4 @@
|
||||
|
||||
$string['manageplugins'] = 'Manage restrictions';
|
||||
$string['pluginname'] = 'Availability condition management';
|
||||
$string['privacy:metadata'] = 'The Availability condition management plugin does not store any personal data.';
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Privacy Subsystem implementation for tool_behat.
|
||||
*
|
||||
* @package tool_behat
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_behat\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_behat implementing null_provider.
|
||||
*
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements \core_privacy\local\metadata\null_provider {
|
||||
|
||||
/**
|
||||
* Get the language string identifier with the component's language
|
||||
* file to explain why this plugin stores no data.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_reason() {
|
||||
return 'privacy:metadata';
|
||||
}
|
||||
}
|
||||
@@ -398,7 +398,7 @@ function print_combined_install_output($processes) {
|
||||
// Show process name in first row.
|
||||
foreach ($processes as $name => $process) {
|
||||
// If we don't have enough space to show full run name then show runX.
|
||||
if ($lengthofprocessline < strlen($name + 2)) {
|
||||
if ($lengthofprocessline < strlen($name) + 2) {
|
||||
$name = substr($name, -5);
|
||||
}
|
||||
// One extra padding as we are adding | separator for rest of the data.
|
||||
|
||||
@@ -50,3 +50,4 @@ $string['wrongbehatsetup'] = 'Something is wrong with the behat setup and so ste
|
||||
<li>$CFG->behat_dataroot, $CFG->behat_prefix and $CFG->behat_wwwroot are set in config.php with different values from $CFG->dataroot, $CFG->prefix and $CFG->wwwroot.</li>
|
||||
<li>You ran "{$a->behatinit}" from your Moodle root directory.</li>
|
||||
<li>Dependencies are installed in vendor/ and {$a->behatcommand} file has execution permissions.</li></ul>';
|
||||
$string['privacy:metadata'] = 'The Acceptance testing plugin does not store any personal data.';
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Privacy Subsystem implementation for tool_capability.
|
||||
*
|
||||
* @package tool_capability
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_capability\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_capability implementing null_provider.
|
||||
*
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements \core_privacy\local\metadata\null_provider {
|
||||
|
||||
/**
|
||||
* Get the language string identifier with the component's language
|
||||
* file to explain why this plugin stores no data.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_reason() {
|
||||
return 'privacy:metadata';
|
||||
}
|
||||
}
|
||||
@@ -34,3 +34,4 @@ $string['pluginname'] = 'Capability overview';
|
||||
$string['reportforcapability'] = 'Report for capability \'{$a}\'';
|
||||
$string['reportsettings'] = 'Report settings';
|
||||
$string['roleslabel'] = 'Roles:';
|
||||
$string['privacy:metadata'] = 'The Capability overview plugin does not store any personal data.';
|
||||
|
||||
@@ -167,7 +167,7 @@ class api {
|
||||
$params['roleid'] = $roleid;
|
||||
$params['userid'] = $userid;
|
||||
|
||||
$sql = 'SELECT u.id AS userid, ra.id, ctx.id AS contextid
|
||||
$sql = 'SELECT DISTINCT u.id AS userid, ra.id, ctx.id AS contextid
|
||||
FROM {user} u
|
||||
JOIN {cohort_members} cm ON u.id = cm.userid
|
||||
JOIN {context} ctx ON u.id = ctx.instanceid AND ctx.contextlevel = :usercontext
|
||||
|
||||
@@ -98,7 +98,7 @@ class cohort_role_assignments_table extends table_sql {
|
||||
);
|
||||
$context = context_helper::instance_by_id($data->cohortcontextid);
|
||||
|
||||
$exporter = new \tool_lp\external\cohort_summary_exporter($record, array('context' => $context));
|
||||
$exporter = new \core_cohort\external\cohort_summary_exporter($record, array('context' => $context));
|
||||
$cohort = $exporter->export($OUTPUT);
|
||||
|
||||
$html = $OUTPUT->render_from_template('tool_cohortroles/cohort-in-list', $cohort);
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Privacy Subsystem implementation for tool_cohortroles.
|
||||
*
|
||||
* @package tool_cohortroles
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_cohortroles\privacy;
|
||||
|
||||
use core_privacy\local\metadata\collection;
|
||||
use core_privacy\local\request\approved_contextlist;
|
||||
use core_privacy\local\request\context;
|
||||
use core_privacy\local\request\contextlist;
|
||||
use core_privacy\local\request\transform;
|
||||
use core_privacy\local\request\writer;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_cohortroles implementing metadata and plugin providers.
|
||||
*
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements \core_privacy\local\metadata\provider, \core_privacy\local\request\plugin\provider {
|
||||
|
||||
/**
|
||||
* Returns meta data about this system.
|
||||
*
|
||||
* @param collection $collection The initialised collection to add items to.
|
||||
* @return collection A listing of user data stored through this system.
|
||||
*/
|
||||
public static function get_metadata(collection $collection) {
|
||||
// The tool_cohortroles plugin utilises the mdl_tool_cohortroles table.
|
||||
$collection->add_database_table(
|
||||
'tool_cohortroles',
|
||||
[
|
||||
'id' => 'privacy:metadata:tool_cohortroles:id',
|
||||
'cohortid' => 'privacy:metadata:tool_cohortroles:cohortid',
|
||||
'roleid' => 'privacy:metadata:tool_cohortroles:roleid',
|
||||
'userid' => 'privacy:metadata:tool_cohortroles:userid',
|
||||
'timecreated' => 'privacy:metadata:tool_cohortroles:timecreated',
|
||||
'timemodified' => 'privacy:metadata:tool_cohortroles:timemodified',
|
||||
'usermodified' => 'privacy:metadata:tool_cohortroles:usermodified'
|
||||
],
|
||||
'privacy:metadata:tool_cohortroles'
|
||||
);
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of contexts that contain user information for the specified user.
|
||||
*
|
||||
* @param int $userid The user to search.
|
||||
* @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
|
||||
*/
|
||||
public static function get_contexts_for_userid($userid) {
|
||||
$contextlist = new contextlist();
|
||||
|
||||
// Retrieve the User context associated with tool_cohortroles records.
|
||||
$sql = "SELECT DISTINCT c.id
|
||||
FROM {context} c
|
||||
JOIN {tool_cohortroles} cr ON cr.userid = c.instanceid AND c.contextlevel = :contextuser
|
||||
WHERE cr.userid = :userid";
|
||||
|
||||
$params = [
|
||||
'contextuser' => CONTEXT_USER,
|
||||
'userid' => $userid
|
||||
];
|
||||
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
return $contextlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export all user data for the specified user, in the specified contexts.
|
||||
*
|
||||
* @param approved_contextlist $contextlist The approved contexts to export information for.
|
||||
*/
|
||||
public static function export_user_data(approved_contextlist $contextlist) {
|
||||
global $DB;
|
||||
|
||||
// If the user has tool_cohortroles data, then only the User context should be present so get the first context.
|
||||
$contexts = $contextlist->get_contexts();
|
||||
if (count($contexts) == 0) {
|
||||
return;
|
||||
}
|
||||
$context = reset($contexts);
|
||||
|
||||
// Sanity check that context is at the User context level, then get the userid.
|
||||
if ($context->contextlevel !== CONTEXT_USER) {
|
||||
return;
|
||||
}
|
||||
$userid = $context->instanceid;
|
||||
|
||||
// Retrieve the tool_cohortroles records created for the user.
|
||||
$sql = 'SELECT cr.id as cohortroleid,
|
||||
c.name as cohortname,
|
||||
c.idnumber as cohortidnumber,
|
||||
c.description as cohortdescription,
|
||||
r.shortname as roleshortname,
|
||||
cr.userid as userid,
|
||||
cr.timecreated as timecreated,
|
||||
cr.timemodified as timemodified
|
||||
FROM {tool_cohortroles} cr
|
||||
JOIN {cohort} c ON c.id = cr.cohortid
|
||||
JOIN {role} r ON r.id = cr.roleid
|
||||
WHERE cr.userid = :userid';
|
||||
|
||||
$params = [
|
||||
'userid' => $userid
|
||||
];
|
||||
|
||||
$cohortroles = $DB->get_records_sql($sql, $params);
|
||||
foreach ($cohortroles as $cohortrole) {
|
||||
// The tool_cohortroles data export is organised in:
|
||||
// {User Context}/Cohort roles management/{cohort name}/{role shortname}/data.json.
|
||||
$subcontext = [
|
||||
get_string('pluginname', 'tool_cohortroles'),
|
||||
$cohortrole->cohortname,
|
||||
$cohortrole->roleshortname
|
||||
];
|
||||
|
||||
$data = (object) [
|
||||
'cohortname' => $cohortrole->cohortname,
|
||||
'cohortidnumber' => $cohortrole->cohortidnumber,
|
||||
'cohortdescription' => $cohortrole->cohortdescription,
|
||||
'roleshortname' => $cohortrole->roleshortname,
|
||||
'userid' => transform::user($cohortrole->userid),
|
||||
'timecreated' => transform::datetime($cohortrole->timecreated),
|
||||
'timemodified' => transform::datetime($cohortrole->timemodified)
|
||||
];
|
||||
|
||||
writer::with_context($context)->export_data($subcontext, $data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all data for all users in the specified context.
|
||||
*
|
||||
* @param context $context The specific context to delete data for.
|
||||
*/
|
||||
public static function delete_data_for_all_users_in_context(\context $context) {
|
||||
global $DB;
|
||||
|
||||
// Sanity check that context is at the User context level, then get the userid.
|
||||
if ($context->contextlevel !== CONTEXT_USER) {
|
||||
return;
|
||||
}
|
||||
$userid = $context->instanceid;
|
||||
|
||||
// Delete the tool_cohortroles records created for the userid.
|
||||
$DB->delete_records('tool_cohortroles', ['userid' => $userid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all user data for the specified user, in the specified contexts.
|
||||
*
|
||||
* @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
|
||||
*/
|
||||
public static function delete_data_for_user(approved_contextlist $contextlist) {
|
||||
global $DB;
|
||||
|
||||
// If the user has tool_cohortroles data, then only the User context should be present so get the first context.
|
||||
$contexts = $contextlist->get_contexts();
|
||||
if (count($contexts) == 0) {
|
||||
return;
|
||||
}
|
||||
$context = reset($contexts);
|
||||
|
||||
// Sanity check that context is at the User context level, then get the userid.
|
||||
if ($context->contextlevel !== CONTEXT_USER) {
|
||||
return;
|
||||
}
|
||||
$userid = $context->instanceid;
|
||||
|
||||
// Delete the tool_cohortroles records created for the userid.
|
||||
$DB->delete_records('tool_cohortroles', ['userid' => $userid]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,3 +42,11 @@ $string['selectrole'] = 'Select role';
|
||||
$string['selectusers'] = 'Select users to assign role';
|
||||
$string['taskname'] = 'Sync cohort role assignments';
|
||||
$string['thisuserroles'] = 'Roles assigned relative to this user';
|
||||
$string['privacy:metadata:tool_cohortroles'] = 'The Cohort roles management plugin stores user cohort role mappings.';
|
||||
$string['privacy:metadata:tool_cohortroles:id'] = 'The ID of the cohort role mapping record.';
|
||||
$string['privacy:metadata:tool_cohortroles:cohortid'] = 'The ID of the cohort.';
|
||||
$string['privacy:metadata:tool_cohortroles:roleid'] = 'The ID of the role.';
|
||||
$string['privacy:metadata:tool_cohortroles:userid'] = 'The ID of the user.';
|
||||
$string['privacy:metadata:tool_cohortroles:timecreated'] = 'The date/time of when the cohort role mapping was created.';
|
||||
$string['privacy:metadata:tool_cohortroles:timemodified'] = 'The date/time of when the cohort role mapping was modified.';
|
||||
$string['privacy:metadata:tool_cohortroles:usermodified'] = 'The ID of the user who last modified the cohort role mapping.';
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Unit tests for the tool_cohortroles implementation of the privacy API.
|
||||
*
|
||||
* @package tool_cohortroles
|
||||
* @category test
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
|
||||
use \core_privacy\local\request\writer;
|
||||
use \core_privacy\local\request\approved_contextlist;
|
||||
use \tool_cohortroles\api;
|
||||
use \tool_cohortroles\privacy\provider;
|
||||
|
||||
/**
|
||||
* Unit tests for the tool_cohortroles implementation of the privacy API.
|
||||
*
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_cohortroles_privacy_testcase extends \core_privacy\tests\provider_testcase {
|
||||
|
||||
/**
|
||||
* Overriding setUp() function to always reset after tests.
|
||||
*/
|
||||
public function setUp() {
|
||||
$this->resetAfterTest(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for provider::get_contexts_for_userid().
|
||||
*/
|
||||
public function test_get_contexts_for_userid() {
|
||||
global $DB;
|
||||
|
||||
// Test setup.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
$this->setAdminUser();
|
||||
|
||||
$nocohortroles = 3;
|
||||
$this->setup_test_scenario_data($user->id, $nocohortroles);
|
||||
|
||||
// Test the User's assigned cohortroles matches 3.
|
||||
$cohortroles = $DB->get_records('tool_cohortroles', ['userid' => $user->id]);
|
||||
$this->assertCount($nocohortroles, $cohortroles);
|
||||
|
||||
// Test the User's retrieved contextlist contains only one context.
|
||||
$contextlist = provider::get_contexts_for_userid($user->id);
|
||||
$contexts = $contextlist->get_contexts();
|
||||
$this->assertCount(1, $contexts);
|
||||
|
||||
// Test the User's contexts equal the User's own context.
|
||||
$context = reset($contexts);
|
||||
$this->assertEquals(CONTEXT_USER, $context->contextlevel);
|
||||
$this->assertEquals($user->id, $context->instanceid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for provider::export_user_data().
|
||||
*/
|
||||
public function test_export_user_data() {
|
||||
// Test setup.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
$this->setAdminUser();
|
||||
|
||||
$nocohortroles = 3;
|
||||
$this->setup_test_scenario_data($user->id, $nocohortroles);
|
||||
|
||||
// Test the User's retrieved contextlist contains only one context.
|
||||
$contextlist = provider::get_contexts_for_userid($user->id);
|
||||
$contexts = $contextlist->get_contexts();
|
||||
$this->assertCount(1, $contexts);
|
||||
|
||||
// Test the User's contexts equal the User's own context.
|
||||
$context = reset($contexts);
|
||||
$this->assertEquals(CONTEXT_USER, $context->contextlevel);
|
||||
$this->assertEquals($user->id, $context->instanceid);
|
||||
|
||||
// Retrieve the User's tool_cohortroles data.
|
||||
$approvedcontextlist = new approved_contextlist($user, 'tool_cohortroles', $contextlist->get_contextids());
|
||||
provider::export_user_data($approvedcontextlist);
|
||||
|
||||
// Test the tool_cohortroles data is exported at the User context level.
|
||||
$writer = writer::with_context($context);
|
||||
$this->assertTrue($writer->has_any_data());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for provider::delete_data_for_all_users_in_context().
|
||||
*/
|
||||
public function test_delete_data_for_all_users_in_context() {
|
||||
global $DB;
|
||||
|
||||
// Test setup.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
$this->setAdminUser();
|
||||
|
||||
$nocohortroles = 4;
|
||||
$this->setup_test_scenario_data($user->id, $nocohortroles);
|
||||
|
||||
// Test the User's assigned cohortroles matches 4.
|
||||
$cohortroles = $DB->get_records('tool_cohortroles', ['userid' => $user->id]);
|
||||
$this->assertCount($nocohortroles, $cohortroles);
|
||||
|
||||
// Test the User's retrieved contextlist contains only one context.
|
||||
$contextlist = provider::get_contexts_for_userid($user->id);
|
||||
$contexts = $contextlist->get_contexts();
|
||||
$this->assertCount(1, $contexts);
|
||||
|
||||
// Test the User's contexts equal the User's own context.
|
||||
$context = reset($contexts);
|
||||
$this->assertEquals(CONTEXT_USER, $context->contextlevel);
|
||||
$this->assertEquals($user->id, $context->instanceid);
|
||||
|
||||
// Delete all the User's records in mdl_tool_cohortroles table by the specified User context.
|
||||
provider::delete_data_for_all_users_in_context($context);
|
||||
|
||||
// Test the cohort roles records in mdl_tool_cohortroles table is equals zero.
|
||||
$cohortroles = $DB->get_records('tool_cohortroles', ['userid' => $user->id]);
|
||||
$this->assertCount(0, $cohortroles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for provider::delete_data_for_user().
|
||||
*/
|
||||
public function test_delete_data_for_user() {
|
||||
global $DB;
|
||||
|
||||
// Test setup.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
$this->setAdminUser();
|
||||
|
||||
$nocohortroles = 4;
|
||||
$this->setup_test_scenario_data($user->id, $nocohortroles);
|
||||
|
||||
// Test the User's assigned cohortroles matches 4.
|
||||
$cohortroles = $DB->get_records('tool_cohortroles', ['userid' => $user->id]);
|
||||
$this->assertCount($nocohortroles, $cohortroles);
|
||||
|
||||
// Test the User's retrieved contextlist contains only one context.
|
||||
$contextlist = provider::get_contexts_for_userid($user->id);
|
||||
$contexts = $contextlist->get_contexts();
|
||||
$this->assertCount(1, $contexts);
|
||||
|
||||
// Test the User's contexts equal the User's own context.
|
||||
$context = reset($contexts);
|
||||
$this->assertEquals(CONTEXT_USER, $context->contextlevel);
|
||||
$this->assertEquals($user->id, $context->instanceid);
|
||||
|
||||
// Delete all the User's records in mdl_tool_cohortroles table by the specified User approved context list.
|
||||
$approvedcontextlist = new approved_contextlist($user, 'tool_cohortroles', $contextlist->get_contextids());
|
||||
provider::delete_data_for_user($approvedcontextlist);
|
||||
|
||||
// Test the records in mdl_tool_cohortroles table is equals zero.
|
||||
$cohortroles = $DB->get_records('tool_cohortroles', ['userid' => $user->id]);
|
||||
$this->assertCount(0, $cohortroles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to setup tool_cohortroles records for testing a specific user.
|
||||
*
|
||||
* @param int $userid The ID of the user used for testing.
|
||||
* @param int $nocohortroles The number of tool_cohortroles to create for the user.
|
||||
* @throws \core_competency\invalid_persistent_exception
|
||||
* @throws coding_exception
|
||||
*/
|
||||
protected function setup_test_scenario_data($userid, $nocohortroles) {
|
||||
$roleid = create_role('Sausage Roll', 'sausageroll', 'mmmm');
|
||||
|
||||
for ($c = 0; $c < $nocohortroles; $c++) {
|
||||
$cohort = $this->getDataGenerator()->create_cohort();
|
||||
|
||||
$params = (object)array(
|
||||
'userid' => $userid,
|
||||
'roleid' => $roleid,
|
||||
'cohortid' => $cohort->id
|
||||
);
|
||||
|
||||
api::create_cohort_role_assignment($params);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Privacy Subsystem implementation for tool_customlang.
|
||||
*
|
||||
* @package tool_customlang
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_customlang\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_customlang implementing null_provider.
|
||||
*
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements \core_privacy\local\metadata\null_provider {
|
||||
|
||||
/**
|
||||
* Get the language string identifier with the component's language
|
||||
* file to explain why this plugin stores no data.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_reason() {
|
||||
return 'privacy:metadata';
|
||||
}
|
||||
}
|
||||
@@ -59,3 +59,4 @@ $string['placeholderwarning'] = 'string contains a placeholder';
|
||||
$string['pluginname'] = 'Language customisation';
|
||||
$string['savecheckin'] = 'Save changes to the language pack';
|
||||
$string['savecontinue'] = 'Apply changes and continue editing';
|
||||
$string['privacy:metadata'] = 'The Language customisation plugin does not store any personal data.';
|
||||
|
||||
@@ -159,6 +159,11 @@ class tool_customlang_utils {
|
||||
$needsupdate = true;
|
||||
$current[$stringid]->local = $stringlocal;
|
||||
$current[$stringid]->timecustomized = $now;
|
||||
} else if (isset($currentlocal) && $stringlocal !== $currentlocal) {
|
||||
// If local string has been removed, we need to remove also the old local value from DB.
|
||||
$needsupdate = true;
|
||||
$current[$stringid]->local = null;
|
||||
$current[$stringid]->timecustomized = $now;
|
||||
}
|
||||
|
||||
if ($needsupdate) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
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)}}});
|
||||
@@ -0,0 +1 @@
|
||||
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)}}});
|
||||
@@ -0,0 +1 @@
|
||||
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"},{key:"deletecategorytext",component:"tool_dataprivacy",param:i},{key:"delete"}];d.get_strings(j).then(function(d){var g=d[0],i=d[1],j=d[2];return e.create({title:g,body:i,type:e.types.SAVE_CANCEL}).then(function(d){return d.setSaveButtonText(j),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}}});
|
||||
@@ -0,0 +1 @@
|
||||
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});
|
||||
@@ -0,0 +1 @@
|
||||
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)}}});
|
||||
@@ -0,0 +1 @@
|
||||
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"]',COMPLETE_BUTTON:'[data-action="complete"]'},i=function(a){d.call(this,a)};return i.TYPE="tool_dataprivacy-data_request",i.TYPE_ENQUIRY="tool_dataprivacy-complete_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)),this.getModal().on(c.events.activate,h.COMPLETE_BUTTON,function(b,c){var d=a.Event(f.complete);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"),e.register(i.TYPE_ENQUIRY,i,"tool_dataprivacy/mark_request_complete_modal"),g=!0),i});
|
||||
@@ -0,0 +1 @@
|
||||
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)}}});
|
||||
@@ -0,0 +1 @@
|
||||
define([],function(){return{approve:"tool_dataprivacy-data_request:approve",deny:"tool_dataprivacy-data_request:deny",complete:"tool_dataprivacy-data_request:complete"}});
|
||||
@@ -0,0 +1 @@
|
||||
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)})}}});
|
||||
@@ -0,0 +1 @@
|
||||
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)}}});
|
||||
@@ -0,0 +1 @@
|
||||
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}}});
|
||||
@@ -0,0 +1 @@
|
||||
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"},{key:"deletepurposetext",component:"tool_dataprivacy",param:i},{key:"delete"}];d.get_strings(j).then(function(d){var g=d[0],i=d[1],j=d[2];return e.create({title:g,body:i,type:e.types.SAVE_CANCEL}).then(function(d){return d.setSaveButtonText(j),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}}});
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/form-autocomplete","core/str","core/notification"],function(a,b,c,d){var e={REQUEST_FILTERS:"#request-filters"},f=function(){var f=[{key:"filter",component:"moodle"},{key:"nofiltersapplied",component:"tool_dataprivacy"}];c.get_strings(f).then(function(a){var c=a[0],d=a[1];return b.enhance(e.REQUEST_FILTERS,!1,"",c,!1,!0,d,!0)}).fail(d.exception);var g=a(e.REQUEST_FILTERS).val();a(e.REQUEST_FILTERS).on("change",function(){var b=a(this).val();g.join(",")!==b.join(",")&&(0===b.length&&a("#filters-cleared").val(1),a(this.form).submit())})};return{init:function(){f()}}});
|
||||
@@ -0,0 +1 @@
|
||||
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,b){var g=[],h="",j={requestid:b};switch(a){case i.approve:g=[{key:"approverequest",component:"tool_dataprivacy"},{key:"confirmapproval",component:"tool_dataprivacy"}],h="tool_dataprivacy_approve_data_request";break;case i.deny:g=[{key:"denyrequest",component:"tool_dataprivacy"},{key:"confirmdenial",component:"tool_dataprivacy"}],h="tool_dataprivacy_deny_data_request";break;case i.complete:g=[{key:"markcomplete",component:"tool_dataprivacy"},{key:"confirmcompletion",component:"tool_dataprivacy"}],h="tool_dataprivacy_mark_complete"}var l="";d.get_strings(g).then(function(a){l=a[0];var b=a[1];return e.create({title:l,body:b,type:e.types.SAVE_CANCEL})}).then(function(a){a.setSaveButtonText(l),a.getRoot().on(f.save,function(){k(h,j)}),a.getRoot().on(f.hidden,function(){a.destroy()}),a.show()})["catch"](c.exception)}function k(a,d){var e={methodname:a,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)}var l={APPROVE_REQUEST:'[data-action="approve"]',DENY_REQUEST:'[data-action="deny"]',VIEW_REQUEST:'[data-action="view"]',MARK_COMPLETE:'[data-action="complete"]'},m=function(){this.registerEvents()};return m.prototype.registerEvents=function(){a(l.VIEW_REQUEST).click(function(d){d.preventDefault();var l=a(this).data("requestid"),m={requestid:l},n={methodname:"tool_dataprivacy_get_data_request",args:m},o=b.call([n]);a.when(o[0]).then(function(a){return a.result?a.result:(c.addNotification({message:a.warnings[0].message,type:"error"}),!1)}).then(function(a){var b=e.types.DEFAULT;a.approvedeny?b=h.TYPE:a.canmarkcomplete&&(b=h.TYPE_ENQUIRY);var c=g.render("tool_dataprivacy/request_details",a);return e.create({title:a.typename,body:c,type:b,large:!0})}).then(function(a){a.getRoot().on(i.approve,function(){j(i.approve,l)}),a.getRoot().on(i.deny,function(){j(i.deny,l)}),a.getRoot().on(i.complete,function(){var a={requestid:l};k("tool_dataprivacy_mark_complete",a)}),a.getRoot().on(f.hidden,function(){a.destroy()}),a.show()})["catch"](c.exception)}),a(l.APPROVE_REQUEST).click(function(b){b.preventDefault();var c=a(this).data("requestid");j(i.approve,c)}),a(l.DENY_REQUEST).click(function(b){b.preventDefault();var c=a(this).data("requestid");j(i.deny,c)}),a(l.MARK_COMPLETE).click(function(b){b.preventDefault(),j(i.complete,a(this).data("requestid"))})},m});
|
||||
@@ -0,0 +1,172 @@
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
// 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'
|
||||
},
|
||||
{
|
||||
key: 'deletecategorytext',
|
||||
component: 'tool_dataprivacy',
|
||||
param: categoryname
|
||||
},
|
||||
{
|
||||
key: 'delete'
|
||||
}
|
||||
];
|
||||
|
||||
Str.get_strings(stringkeys).then(function(langStrings) {
|
||||
var title = langStrings[0];
|
||||
var confirmMessage = langStrings[1];
|
||||
var buttonText = langStrings[2];
|
||||
return ModalFactory.create({
|
||||
title: title,
|
||||
body: confirmMessage,
|
||||
type: ModalFactory.types.SAVE_CANCEL
|
||||
}).then(function(modal) {
|
||||
modal.setSaveButtonText(buttonText);
|
||||
|
||||
// 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();
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,156 @@
|
||||
// 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;
|
||||
});
|
||||
@@ -0,0 +1,318 @@
|
||||
// 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
|
||||
}]);
|
||||
return;
|
||||
}).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);
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
// 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"]',
|
||||
COMPLETE_BUTTON: '[data-action="complete"]'
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor for the Modal.
|
||||
*
|
||||
* @param {object} root The root jQuery element for the modal
|
||||
*/
|
||||
var ModalDataRequest = function(root) {
|
||||
Modal.call(this, root);
|
||||
};
|
||||
|
||||
ModalDataRequest.TYPE = 'tool_dataprivacy-data_request';
|
||||
ModalDataRequest.TYPE_ENQUIRY = 'tool_dataprivacy-complete_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));
|
||||
|
||||
this.getModal().on(CustomEvents.events.activate, SELECTORS.COMPLETE_BUTTON, function(e, data) {
|
||||
var completeEvent = $.Event(DataPrivacyEvents.complete);
|
||||
this.getRoot().trigger(completeEvent, this);
|
||||
|
||||
if (!completeEvent.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');
|
||||
ModalRegistry.register(ModalDataRequest.TYPE_ENQUIRY, ModalDataRequest, 'tool_dataprivacy/mark_request_complete_modal');
|
||||
registered = true;
|
||||
}
|
||||
|
||||
return ModalDataRequest;
|
||||
});
|
||||
@@ -0,0 +1,92 @@
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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',
|
||||
complete: 'tool_dataprivacy-data_request:complete'
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
return;
|
||||
}).catch(Notification.exception);
|
||||
|
||||
$(':header i.fa').each(function() {
|
||||
$(this).removeClass(iconclassnow);
|
||||
$(this).addClass(iconclassnext);
|
||||
});
|
||||
$(':header img.icon').each(function() {
|
||||
$(this).attr('src', imagenow);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
// 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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,221 @@
|
||||
// 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();
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,132 @@
|
||||
// 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'
|
||||
},
|
||||
{
|
||||
key: 'deletepurposetext',
|
||||
component: 'tool_dataprivacy',
|
||||
param: purposename
|
||||
},
|
||||
{
|
||||
key: 'delete'
|
||||
}
|
||||
];
|
||||
|
||||
Str.get_strings(stringkeys).then(function(langStrings) {
|
||||
var title = langStrings[0];
|
||||
var confirmMessage = langStrings[1];
|
||||
var buttonText = langStrings[2];
|
||||
return ModalFactory.create({
|
||||
title: title,
|
||||
body: confirmMessage,
|
||||
type: ModalFactory.types.SAVE_CANCEL
|
||||
}).then(function(modal) {
|
||||
modal.setSaveButtonText(buttonText);
|
||||
|
||||
// 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();
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* JS module for the data requests filter.
|
||||
*
|
||||
* @module tool_dataprivacy/request_filter
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/form-autocomplete', 'core/str', 'core/notification'], function($, Autocomplete, Str, Notification) {
|
||||
|
||||
/**
|
||||
* Selectors.
|
||||
*
|
||||
* @access private
|
||||
* @type {{REQUEST_FILTERS: string}}
|
||||
*/
|
||||
var SELECTORS = {
|
||||
REQUEST_FILTERS: '#request-filters'
|
||||
};
|
||||
|
||||
/**
|
||||
* Init function.
|
||||
*
|
||||
* @method init
|
||||
* @private
|
||||
*/
|
||||
var init = function() {
|
||||
var stringkeys = [
|
||||
{
|
||||
key: 'filter',
|
||||
component: 'moodle'
|
||||
},
|
||||
{
|
||||
key: 'nofiltersapplied',
|
||||
component: 'tool_dataprivacy'
|
||||
}
|
||||
];
|
||||
|
||||
Str.get_strings(stringkeys).then(function(langstrings) {
|
||||
var placeholder = langstrings[0];
|
||||
var noSelectionString = langstrings[1];
|
||||
return Autocomplete.enhance(SELECTORS.REQUEST_FILTERS, false, '', placeholder, false, true, noSelectionString, true);
|
||||
}).fail(Notification.exception);
|
||||
|
||||
var last = $(SELECTORS.REQUEST_FILTERS).val();
|
||||
$(SELECTORS.REQUEST_FILTERS).on('change', function() {
|
||||
var current = $(this).val();
|
||||
// Prevent form from submitting unnecessarily, eg. on blur when no filter is selected.
|
||||
if (last.join(',') !== current.join(',')) {
|
||||
// If we're submitting without filters, set the hidden input 'filters-cleared' to 1.
|
||||
if (current.length === 0) {
|
||||
$('#filters-cleared').val(1);
|
||||
}
|
||||
$(this.form).submit();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return /** @alias module:core/form-autocomplete */ {
|
||||
/**
|
||||
* Initialise the unified user filter.
|
||||
*
|
||||
* @method init
|
||||
*/
|
||||
init: function() {
|
||||
init();
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,272 @@
|
||||
// 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}}
|
||||
* @type {{MARK_COMPLETE: string}}
|
||||
*/
|
||||
var ACTIONS = {
|
||||
APPROVE_REQUEST: '[data-action="approve"]',
|
||||
DENY_REQUEST: '[data-action="deny"]',
|
||||
VIEW_REQUEST: '[data-action="view"]',
|
||||
MARK_COMPLETE: '[data-action="complete"]'
|
||||
};
|
||||
|
||||
/**
|
||||
* 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]);
|
||||
$.when(promises[0]).then(function(data) {
|
||||
if (data.result) {
|
||||
return data.result;
|
||||
}
|
||||
// Fail.
|
||||
Notification.addNotification({
|
||||
message: data.warnings[0].message,
|
||||
type: 'error'
|
||||
});
|
||||
return false;
|
||||
|
||||
}).then(function(data) {
|
||||
// Determine the type of modal to show.
|
||||
var modalType = ModalFactory.types.DEFAULT;
|
||||
if (data.approvedeny) {
|
||||
modalType = ModalDataRequest.TYPE;
|
||||
} else if (data.canmarkcomplete) {
|
||||
modalType = ModalDataRequest.TYPE_ENQUIRY;
|
||||
}
|
||||
var body = Templates.render('tool_dataprivacy/request_details', data);
|
||||
return ModalFactory.create({
|
||||
title: data.typename,
|
||||
body: body,
|
||||
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 send event.
|
||||
modal.getRoot().on(DataPrivacyEvents.complete, function() {
|
||||
var params = {
|
||||
'requestid': requestId
|
||||
};
|
||||
handleSave('tool_dataprivacy_mark_complete', params);
|
||||
});
|
||||
|
||||
// Handle hidden event.
|
||||
modal.getRoot().on(ModalEvents.hidden, function() {
|
||||
// Destroy when hidden.
|
||||
modal.destroy();
|
||||
});
|
||||
|
||||
// Show the modal!
|
||||
modal.show();
|
||||
|
||||
return;
|
||||
|
||||
}).catch(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);
|
||||
});
|
||||
|
||||
$(ACTIONS.MARK_COMPLETE).click(function(e) {
|
||||
e.preventDefault();
|
||||
showConfirmation(DataPrivacyEvents.complete, $(this).data('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 = '';
|
||||
var params = {
|
||||
'requestid': requestId
|
||||
};
|
||||
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;
|
||||
case DataPrivacyEvents.complete:
|
||||
keys = [
|
||||
{
|
||||
key: 'markcomplete',
|
||||
component: 'tool_dataprivacy'
|
||||
},
|
||||
{
|
||||
key: 'confirmcompletion',
|
||||
component: 'tool_dataprivacy'
|
||||
}
|
||||
];
|
||||
wsfunction = 'tool_dataprivacy_mark_complete';
|
||||
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() {
|
||||
handleSave(wsfunction, params);
|
||||
});
|
||||
|
||||
// Handle hidden event.
|
||||
modal.getRoot().on(ModalEvents.hidden, function() {
|
||||
// Destroy when hidden.
|
||||
modal.destroy();
|
||||
});
|
||||
|
||||
modal.show();
|
||||
|
||||
return;
|
||||
|
||||
}).catch(Notification.exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a web service function and reloads the page on success and shows a notification.
|
||||
* Displays an error notification, otherwise.
|
||||
*
|
||||
* @param {String} wsfunction The web service function to call.
|
||||
* @param {Object} params The parameters for the web service functoon.
|
||||
*/
|
||||
function handleSave(wsfunction, params) {
|
||||
// Confirm the request.
|
||||
var request = {
|
||||
methodname: wsfunction,
|
||||
args: params
|
||||
};
|
||||
|
||||
Ajax.call([request])[0].done(function(data) {
|
||||
if (data.result) {
|
||||
// On success, reload the page so that the data request table will be updated.
|
||||
// TODO: Probably in the future, better to reload the table or the target data request via AJAX.
|
||||
window.location.reload();
|
||||
} else {
|
||||
// Add the notification.
|
||||
Notification.addNotification({
|
||||
message: data.warnings[0].message,
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
}).fail(Notification.exception);
|
||||
}
|
||||
|
||||
return RequestActions;
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
<?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');
|
||||
|
||||
require_login(null, false);
|
||||
|
||||
$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();
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,91 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<?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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?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'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?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) {
|
||||
$contextlistpersistent = new contextlist();
|
||||
return $contextlistpersistent->set('component', $contextlist->get_component());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?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
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
<?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,
|
||||
api::DATAREQUEST_STATUS_DOWNLOAD_READY,
|
||||
api::DATAREQUEST_STATUS_EXPIRED,
|
||||
api::DATAREQUEST_STATUS_DELETED,
|
||||
],
|
||||
'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
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a completed data export request has expired.
|
||||
* The response will be valid regardless of the expiry scheduled task having run.
|
||||
*
|
||||
* @param data_request $request the data request object whose expiry will be checked.
|
||||
* @return bool true if the request has expired.
|
||||
*/
|
||||
public static function is_expired(data_request $request) {
|
||||
$result = false;
|
||||
|
||||
// Only export requests expire.
|
||||
if ($request->get('type') == api::DATAREQUEST_TYPE_EXPORT) {
|
||||
switch ($request->get('status')) {
|
||||
// Expired requests are obviously expired.
|
||||
case api::DATAREQUEST_STATUS_EXPIRED:
|
||||
$result = true;
|
||||
break;
|
||||
// Complete requests are expired if the expiry time has elapsed.
|
||||
case api::DATAREQUEST_STATUS_DOWNLOAD_READY:
|
||||
$expiryseconds = get_config('tool_dataprivacy', 'privacyrequestexpiry');
|
||||
if ($expiryseconds > 0 && time() >= ($request->get('timemodified') + $expiryseconds)) {
|
||||
$result = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Fetch completed data requests which are due to expire.
|
||||
*
|
||||
* @param int $userid Optional user ID to filter by.
|
||||
*
|
||||
* @return array Details of completed requests which are due to expire.
|
||||
*/
|
||||
public static function get_expired_requests($userid = 0) {
|
||||
global $DB;
|
||||
|
||||
$expiryseconds = get_config('tool_dataprivacy', 'privacyrequestexpiry');
|
||||
$expirytime = strtotime("-{$expiryseconds} second");
|
||||
$table = self::TABLE;
|
||||
$sqlwhere = 'type = :export_type AND status = :completestatus AND timemodified <= :expirytime';
|
||||
$params = array(
|
||||
'export_type' => api::DATAREQUEST_TYPE_EXPORT,
|
||||
'completestatus' => api::DATAREQUEST_STATUS_DOWNLOAD_READY,
|
||||
'expirytime' => $expirytime,
|
||||
);
|
||||
$sort = 'id';
|
||||
$fields = 'id, userid';
|
||||
|
||||
// Filter by user ID if specified.
|
||||
if ($userid > 0) {
|
||||
$sqlwhere .= ' AND (userid = :userid OR requestedby = :requestedby)';
|
||||
$params['userid'] = $userid;
|
||||
$params['requestedby'] = $userid;
|
||||
}
|
||||
|
||||
return $DB->get_records_select_menu($table, $sqlwhere, $params, $sort, $fields, 0, 2000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expire a given set of data requests.
|
||||
* Update request status and delete the files.
|
||||
*
|
||||
* @param array $expiredrequests [requestid => userid]
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function expire($expiredrequests) {
|
||||
global $DB;
|
||||
|
||||
$ids = array_keys($expiredrequests);
|
||||
|
||||
if (count($ids) > 0) {
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($ids);
|
||||
$initialparams = array(api::DATAREQUEST_STATUS_EXPIRED, time());
|
||||
$params = array_merge($initialparams, $inparams);
|
||||
|
||||
$update = "UPDATE {" . self::TABLE . "}
|
||||
SET status = ?, timemodified = ?
|
||||
WHERE id $insql";
|
||||
|
||||
if ($DB->execute($update, $params)) {
|
||||
$fs = get_file_storage();
|
||||
|
||||
foreach ($expiredrequests as $id => $userid) {
|
||||
$usercontext = \context_user::instance($userid);
|
||||
$fs->delete_area_files($usercontext->id, 'tool_dataprivacy', 'export', $id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
<?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 core_privacy\manager;
|
||||
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 manager();
|
||||
$privacymanager->set_observer(new \tool_dataprivacy\manager_observer());
|
||||
|
||||
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 manager $privacymanager
|
||||
* @param expired_context $expiredctx
|
||||
* @return \context|false
|
||||
*/
|
||||
protected function delete_expired_context(manager $privacymanager, 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?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 core_privacy\manager;
|
||||
|
||||
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 manager $privacymanager
|
||||
* @param expired_context $expiredctx
|
||||
* @return \context|false
|
||||
*/
|
||||
protected function delete_expired_context(manager $privacymanager, 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
@@ -0,0 +1,56 @@
|
||||
<?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',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?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 context instance.
|
||||
*
|
||||
* @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 context instance.
|
||||
*
|
||||
* @copyright 2018 David Monllao
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class context_instance_exporter extends persistent_exporter {
|
||||
|
||||
/**
|
||||
* Defines the persistent class.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function define_class() {
|
||||
return \tool_dataprivacy\context_instance::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
<?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
|
||||
],
|
||||
'approvedeny' => [
|
||||
'type' => PARAM_BOOL,
|
||||
'optional' => true,
|
||||
'default' => false
|
||||
],
|
||||
'canmarkcomplete' => [
|
||||
'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'));
|
||||
|
||||
$requesttype = $this->persistent->get('type');
|
||||
$values['typename'] = helper::get_request_type_string($requesttype);
|
||||
$values['typenameshort'] = helper::get_shortened_request_type_string($requesttype);
|
||||
|
||||
$values['canreview'] = false;
|
||||
$values['approvedeny'] = 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-info';
|
||||
// Request can be manually completed for general enquiry requests.
|
||||
$values['canmarkcomplete'] = $requesttype == api::DATAREQUEST_TYPE_OTHERS;
|
||||
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;
|
||||
// Whether the DPO can approve or deny the request.
|
||||
$values['approvedeny'] = in_array($requesttype, [api::DATAREQUEST_TYPE_EXPORT, api::DATAREQUEST_TYPE_DELETE]);
|
||||
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:
|
||||
case api::DATAREQUEST_STATUS_DOWNLOAD_READY:
|
||||
case api::DATAREQUEST_STATUS_DELETED:
|
||||
$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;
|
||||
case api::DATAREQUEST_STATUS_EXPIRED:
|
||||
$values['statuslabelclass'] = 'label-default';
|
||||
break;
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?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,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
<?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;
|
||||
use tool_dataprivacy\purpose;
|
||||
|
||||
/**
|
||||
* 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 purpose $effectivepurpose
|
||||
* @param int $retentioncontextlevel
|
||||
* @param \context $context The context, just for displaying (filters) purposes.
|
||||
* @return string
|
||||
*/
|
||||
protected static function get_retention_display_text(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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?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'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?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/>.
|
||||
|
||||
|
||||
/**
|
||||
* Filter selector field.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_dataprivacy\form;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use MoodleQuickForm_autocomplete;
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/form/autocomplete.php');
|
||||
|
||||
/**
|
||||
* Form field type for choosing a user on the data request creation form.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class request_user_autocomplete extends MoodleQuickForm_autocomplete {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $elementname Element name
|
||||
* @param mixed $elementlabel Label(s) for an element
|
||||
* @param array $options Options to control the element's display
|
||||
* Valid options are:
|
||||
* - multiple bool Whether or not the field accepts more than one values.
|
||||
*/
|
||||
public function __construct($elementname = null, $elementlabel = null, $options = array()) {
|
||||
$validattributes = array(
|
||||
'ajax' => 'tool_dataprivacy/form-user-selector',
|
||||
);
|
||||
if (!empty($options['multiple'])) {
|
||||
$validattributes['multiple'] = 'multiple';
|
||||
}
|
||||
|
||||
parent::__construct($elementname, $elementlabel, array(), $validattributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of this element.
|
||||
*
|
||||
* @param string|array $value The value to set.
|
||||
* @return boolean
|
||||
*/
|
||||
public function setValue($value) {
|
||||
global $DB;
|
||||
$values = (array) $value;
|
||||
$ids = [];
|
||||
foreach ($values as $onevalue) {
|
||||
if (!empty($onevalue) && (!$this->optionExists($onevalue)) &&
|
||||
($onevalue !== '_qf__force_multiselect_submission')) {
|
||||
array_push($ids, $onevalue);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($ids)) {
|
||||
return $this->setSelected([]);
|
||||
}
|
||||
|
||||
$toselect = [];
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED);
|
||||
$allusernames = get_all_user_name_fields(true);
|
||||
// Exclude admins and guest user.
|
||||
$excludedusers = array_keys(get_admins()) + [guest_user()->id];
|
||||
$sort = 'firstname ASC';
|
||||
$fields = 'id, email, ' . $allusernames;
|
||||
// Fetch the selected users, exclude the admins and guest users.
|
||||
$users = get_users(true, '', true, $excludedusers, $sort, '', '', 0, 30, $fields, "id $insql", $inparams);
|
||||
foreach ($users as $user) {
|
||||
$userdata = (object)[
|
||||
'name' => fullname($user),
|
||||
'email' => $user->email
|
||||
];
|
||||
$this->addOption(get_string('nameemail', 'tool_dataprivacy', $userdata), $user->id);
|
||||
array_push($toselect, $user->id);
|
||||
}
|
||||
|
||||
return $this->setSelected($toselect);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
<?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 {
|
||||
/** The default number of results to be shown per page. */
|
||||
const DEFAULT_PAGE_SIZE = 20;
|
||||
|
||||
/** Filter constant associated with the request type filter. */
|
||||
const FILTER_TYPE = 1;
|
||||
|
||||
/** Filter constant associated with the request status filter. */
|
||||
const FILTER_STATUS = 2;
|
||||
|
||||
/** The request filters preference key. */
|
||||
const PREF_REQUEST_FILTERS = 'tool_dataprivacy_request-filters';
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
$types = self::get_request_types();
|
||||
if (!isset($types[$requesttype])) {
|
||||
throw new moodle_exception('errorinvalidrequesttype', 'tool_dataprivacy');
|
||||
}
|
||||
return $types[$requesttype];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
$types = self::get_request_types_short();
|
||||
if (!isset($types[$requesttype])) {
|
||||
throw new moodle_exception('errorinvalidrequesttype', 'tool_dataprivacy');
|
||||
}
|
||||
return $types[$requesttype];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key value-pairs of request type code and their string value.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_request_types() {
|
||||
return [
|
||||
api::DATAREQUEST_TYPE_EXPORT => get_string('requesttypeexport', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_TYPE_DELETE => get_string('requesttypedelete', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_TYPE_OTHERS => get_string('requesttypeothers', 'tool_dataprivacy'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key value-pairs of request type code and their shortened string value.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_request_types_short() {
|
||||
return [
|
||||
api::DATAREQUEST_TYPE_EXPORT => get_string('requesttypeexportshort', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_TYPE_DELETE => get_string('requesttypedeleteshort', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_TYPE_OTHERS => get_string('requesttypeothersshort', 'tool_dataprivacy'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the human-readable value of a data request status.
|
||||
*
|
||||
* @param int $status The request status.
|
||||
* @return string
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public static function get_request_status_string($status) {
|
||||
$statuses = self::get_request_statuses();
|
||||
if (!isset($statuses[$status])) {
|
||||
throw new moodle_exception('errorinvalidrequeststatus', 'tool_dataprivacy');
|
||||
}
|
||||
|
||||
return $statuses[$status];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key value-pairs of request status code and string value.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_request_statuses() {
|
||||
return [
|
||||
api::DATAREQUEST_STATUS_PENDING => get_string('statuspending', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_PREPROCESSING => get_string('statuspreprocessing', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_AWAITING_APPROVAL => get_string('statusawaitingapproval', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_APPROVED => get_string('statusapproved', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_PROCESSING => get_string('statusprocessing', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_COMPLETE => get_string('statuscomplete', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_DOWNLOAD_READY => get_string('statusready', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_EXPIRED => get_string('statusexpired', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_CANCELLED => get_string('statuscancelled', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_REJECTED => get_string('statusrejected', 'tool_dataprivacy'),
|
||||
api::DATAREQUEST_STATUS_DELETED => get_string('statusdeleted', '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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get options for the data requests filter.
|
||||
*
|
||||
* @return array
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public static function get_request_filter_options() {
|
||||
$filters = [
|
||||
self::FILTER_TYPE => (object)[
|
||||
'name' => get_string('requesttype', 'tool_dataprivacy'),
|
||||
'options' => self::get_request_types_short()
|
||||
],
|
||||
self::FILTER_STATUS => (object)[
|
||||
'name' => get_string('requeststatus', 'tool_dataprivacy'),
|
||||
'options' => self::get_request_statuses()
|
||||
],
|
||||
];
|
||||
$options = [];
|
||||
foreach ($filters as $category => $filtercategory) {
|
||||
foreach ($filtercategory->options as $key => $name) {
|
||||
$option = (object)[
|
||||
'category' => $filtercategory->name,
|
||||
'name' => $name
|
||||
];
|
||||
$options["{$category}:{$key}"] = get_string('filteroption', 'tool_dataprivacy', $option);
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?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 \tool_dataprivacy\manager_observer.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_dataprivacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* A failure observer for the \core_privacy\manager.
|
||||
*
|
||||
* @package tool_dataprivacy
|
||||
* @copyright 2018 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class manager_observer implements \core_privacy\manager_observer {
|
||||
/**
|
||||
* Notifies all DPOs that an exception occurred.
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @param string $component
|
||||
* @param string $interface
|
||||
* @param string $methodname
|
||||
* @param array $params
|
||||
*/
|
||||
public function handle_component_failure($e, $component, $interface, $methodname, array $params) {
|
||||
// Get the list of the site Data Protection Officers.
|
||||
$dpos = api::get_site_dpos();
|
||||
|
||||
$messagesubject = get_string('exceptionnotificationsubject', 'tool_dataprivacy');
|
||||
$a = (object)[
|
||||
'fullmethodname' => \core_privacy\manager::get_provider_classname_for_component($component) . '::' . $methodname,
|
||||
'component' => $component,
|
||||
'message' => $e->getMessage(),
|
||||
'backtrace' => $e->getTraceAsString()
|
||||
];
|
||||
$messagebody = get_string('exceptionnotificationbody', 'tool_dataprivacy', $a);
|
||||
|
||||
// Email the data request to the Data Protection Officer(s)/Admin(s).
|
||||
foreach ($dpos as $dpo) {
|
||||
$message = new \core\message\message();
|
||||
$message->courseid = SITEID;
|
||||
$message->component = 'tool_dataprivacy';
|
||||
$message->name = 'notifyexceptions';
|
||||
$message->userfrom = \core_user::get_noreply_user();
|
||||
$message->subject = $messagesubject;
|
||||
$message->fullmessageformat = FORMAT_HTML;
|
||||
$message->notification = 1;
|
||||
$message->userto = $dpo;
|
||||
$message->fullmessagehtml = $messagebody;
|
||||
$message->fullmessage = html_to_text($messagebody);
|
||||
|
||||
// Send message.
|
||||
message_send($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?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();
|
||||
$manager->set_observer(new \tool_dataprivacy\manager_observer());
|
||||
|
||||
$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.
|
||||
// Plugin names can contain _ characters, limit to 2 to just remove initial plugintype.
|
||||
$componentshortname = explode('_', $component, 2);
|
||||
$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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?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 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\local\helper;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
/** @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 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 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(helper::DEFAULT_PAGE_SIZE, true);
|
||||
$expiredcontexts = ob_get_contents();
|
||||
ob_end_clean();
|
||||
$data->expiredcontexts = $expiredcontexts;
|
||||
|
||||
$data->existingcontexts = $this->expiredcontextstable->rawdata ? true : false;
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user