Compare commits
992 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 56ff85a36b | |||
| c2a6b2b6ec | |||
| 96b0818716 | |||
| 51f0dbbcda | |||
| 1465548ba5 | |||
| 06c8d3bf55 | |||
| fd982f3030 | |||
| 876cdf05c4 | |||
| b98339b09a | |||
| 4b6f3f6cc1 | |||
| 8862838f25 | |||
| 5255e8fea9 | |||
| decd3731df | |||
| 60f6712ce1 | |||
| 8d85f96696 | |||
| 23a203618c | |||
| c0c33ab059 | |||
| 691e92efa7 | |||
| 1b70269f4e | |||
| 8197937d22 | |||
| d60618cbea | |||
| 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 | |||
| 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 | |||
| c16fd22bbc | |||
| b8cf9f06dc | |||
| 879c847aed | |||
| 1b091c789c | |||
| 15646264c4 | |||
| d716aa1587 | |||
| 287089b1a2 |
+1
-1
@@ -249,7 +249,7 @@ script:
|
||||
- >
|
||||
if [ "$TASK" = 'PHPUNIT' ];
|
||||
then
|
||||
vendor/bin/phpunit;
|
||||
vendor/bin/phpunit --fail-on-risky --disallow-test-output --verbose;
|
||||
fi
|
||||
|
||||
- >
|
||||
|
||||
+4
-2
@@ -75,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()) {
|
||||
@@ -129,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';
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
@@ -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,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_dbtransfer.
|
||||
*
|
||||
* @package tool_dbtransfer
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_dbtransfer\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_dbtransfer 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';
|
||||
}
|
||||
}
|
||||
@@ -40,4 +40,4 @@ $string['transferdata'] = 'Transfer data';
|
||||
$string['transferdbintro'] = 'This script will transfer the entire contents of this database to another database server. It is often used for migration of data to different database type.';
|
||||
$string['transferdbtoserver'] = 'Transfer this Moodle database to another server';
|
||||
$string['transferringdbto'] = 'Transferring this {$a->dbtypefrom} database to {$a->dbtype} database "{$a->dbname}" on "{$a->dbhost}"';
|
||||
|
||||
$string['privacy:metadata'] = 'The Database transfer 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_filetypes.
|
||||
*
|
||||
* @package tool_filetypes
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_filetypes\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_filetypes 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';
|
||||
}
|
||||
}
|
||||
@@ -67,3 +67,4 @@ $string['source_custom'] = 'Custom';
|
||||
$string['source_deleted'] = 'Deleted';
|
||||
$string['source_modified'] = 'Modified';
|
||||
$string['source_standard'] = 'Standard';
|
||||
$string['privacy:metadata'] = 'The File types 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_generator.
|
||||
*
|
||||
* @package tool_generator
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_generator\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_generator 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';
|
||||
}
|
||||
}
|
||||
@@ -122,3 +122,4 @@ $string['testplansize_4'] = 'XL ({$a->users} users, {$a->loops} loops and {$a->r
|
||||
$string['testplansize_5'] = 'XXL ({$a->users} users, {$a->loops} loops and {$a->rampup} rampup period)';
|
||||
$string['updateuserspassword'] = 'Update course users password';
|
||||
$string['updateuserspassword_help'] = 'JMeter needs to login as the course users, you can set the users password using $CFG->tool_generator_users_password in config.php; this setting updates the course user\'s password according to $CFG->tool_generator_users_password. It can be useful in case you are using a course not generated by tool_generator or $CFG->tool_generator_users_password was not set when you created the test courses.';
|
||||
$string['privacy:metadata'] = 'The Development data generator 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_health.
|
||||
*
|
||||
* @package tool_health
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_health\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_health 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';
|
||||
}
|
||||
}
|
||||
@@ -29,3 +29,4 @@ $string['healthproblemsolution'] = 'Health problem solution';
|
||||
$string['healthreturntomain'] = 'Continue';
|
||||
$string['healthsolution'] = 'Solution';
|
||||
$string['pluginname'] = 'Health center';
|
||||
$string['privacy:metadata'] = 'The Health center 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_innodb.
|
||||
*
|
||||
* @package tool_innodb
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_innodb\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_innodb 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['pluginname'] = 'Convert to InnoDB';
|
||||
$string['privacy:metadata'] = 'The Convert to InnoDB 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_installaddon.
|
||||
*
|
||||
* @package tool_installaddon
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_installaddon\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_installaddon 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['remoterequestpluginfoexception'] = 'Oops... An error occurred while try
|
||||
$string['remoterequestnoninstallable'] = 'There is a request to install plugin {$a->name} ({$a->component}) version {$a->version} from the Moodle plugins directory on this site. However, the plugin installation pre-check failed (reason code: {$a->reason}).';
|
||||
$string['typedetectionfailed'] = 'Unable to detect the plugin type. Please choose the plugin type manually.';
|
||||
$string['typedetectionmismatch'] = 'The selected plugin type does not match the one declared by the plugin: {$a}';
|
||||
$string['privacy:metadata'] = 'The Plugin installer 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_langimport.
|
||||
*
|
||||
* @package tool_langimport
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_langimport\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_langimport 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';
|
||||
}
|
||||
}
|
||||
@@ -48,3 +48,4 @@ $string['selectlangs'] = 'Select languages to uninstall';
|
||||
$string['uninstall'] = 'Uninstall selected language pack(s)';
|
||||
$string['uninstallconfirm'] = 'You are about to completely uninstall these language packs: <strong>{$a}</strong>. Are you sure?';
|
||||
$string['updatelangs'] = 'Update all installed language packs';
|
||||
$string['privacy:metadata'] = 'The Language packs plugin does not store any personal data.';
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
<?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 helper.
|
||||
*
|
||||
* @package tool_log
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_log\local\privacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use core_privacy\local\request\transform;
|
||||
|
||||
/**
|
||||
* Privacy helper class.
|
||||
*
|
||||
* @package tool_log
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class helper {
|
||||
|
||||
/**
|
||||
* Returns an event from a standard record.
|
||||
*
|
||||
* @see \logstore_standard\log\store::get_log_event()
|
||||
* @param object $data Log data.
|
||||
* @return \core\event\base
|
||||
*/
|
||||
protected static function restore_event_from_standard_record($data) {
|
||||
$extra = ['origin' => $data->origin, 'ip' => $data->ip, 'realuserid' => $data->realuserid];
|
||||
$data = (array) $data;
|
||||
$id = $data['id'];
|
||||
$data['other'] = unserialize($data['other']);
|
||||
if ($data['other'] === false) {
|
||||
$data['other'] = [];
|
||||
}
|
||||
unset($data['origin']);
|
||||
unset($data['ip']);
|
||||
unset($data['realuserid']);
|
||||
unset($data['id']);
|
||||
|
||||
if (!$event = \core\event\base::restore($data, $extra)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a standard log record for a user.
|
||||
*
|
||||
* @param object $record The record.
|
||||
* @param int $userid The user ID.
|
||||
* @return array
|
||||
*/
|
||||
public static function transform_standard_log_record_for_userid($record, $userid) {
|
||||
|
||||
// Restore the event to try to get the name, description and other field.
|
||||
$restoredevent = static::restore_event_from_standard_record($record);
|
||||
if ($restoredevent) {
|
||||
$name = $restoredevent->get_name();
|
||||
$description = $restoredevent->get_description();
|
||||
$other = $restoredevent->other;
|
||||
|
||||
} else {
|
||||
$name = $record->eventname;
|
||||
$description = "Unknown event ({$name})";
|
||||
$other = unserialize($record->other);
|
||||
}
|
||||
|
||||
$realuserid = $record->realuserid;
|
||||
$isauthor = $record->userid == $userid;
|
||||
$isrelated = $record->relateduserid == $userid;
|
||||
$isrealuser = $realuserid == $userid;
|
||||
$ismasqueraded = $realuserid !== null && $record->userid != $realuserid;
|
||||
$ismasquerading = $isrealuser && !$isauthor;
|
||||
$isanonymous = $record->anonymous;
|
||||
|
||||
$data = [
|
||||
'name' => $name,
|
||||
'description' => $description,
|
||||
'timecreated' => transform::datetime($record->timecreated),
|
||||
'ip' => $record->ip,
|
||||
'origin' => static::transform_origin($record->origin),
|
||||
'other' => $other ? $other : []
|
||||
];
|
||||
|
||||
if ($isanonymous) {
|
||||
$data['action_was_done_anonymously'] = transform::yesno($isanonymous);
|
||||
}
|
||||
if ($isauthor || !$isanonymous) {
|
||||
$data['authorid'] = transform::user($record->userid);
|
||||
$data['author_of_the_action_was_you'] = transform::yesno($isauthor);
|
||||
}
|
||||
|
||||
if ($record->relateduserid) {
|
||||
$data['relateduserid'] = transform::user($record->relateduserid);
|
||||
$data['related_user_was_you'] = transform::yesno($isrelated);
|
||||
}
|
||||
|
||||
if ($ismasqueraded) {
|
||||
$data['author_of_the_action_was_masqueraded'] = transform::yesno(true);
|
||||
if ($ismasquerading || !$isanonymous) {
|
||||
$data['masqueradinguserid'] = transform::user($realuserid);
|
||||
$data['masquerading_user_was_you'] = transform::yesno($ismasquerading);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform origin.
|
||||
*
|
||||
* @param string $origin The page request origin.
|
||||
* @return string
|
||||
*/
|
||||
public static function transform_origin($origin) {
|
||||
switch ($origin) {
|
||||
case 'cli':
|
||||
case 'restore':
|
||||
case 'web':
|
||||
case 'ws':
|
||||
return get_string('privacy:request:origin:' . $origin, 'tool_log');
|
||||
break;
|
||||
}
|
||||
return $origin;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Logstore provider interface.
|
||||
*
|
||||
* @package tool_log
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_log\local\privacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use context;
|
||||
use core_privacy\local\request\contextlist;
|
||||
use core_privacy\local\request\approved_contextlist;
|
||||
|
||||
/**
|
||||
* Logstore provider interface.
|
||||
*
|
||||
* Logstore subplugins providers must implement this interface.
|
||||
*
|
||||
* @package tool_log
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
interface logstore_provider extends \core_privacy\local\request\plugin\subplugin_provider {
|
||||
|
||||
/**
|
||||
* Add contexts that contain user information for the specified user.
|
||||
*
|
||||
* @param contextlist $contextlist The contextlist to add the contexts to.
|
||||
* @param int $userid The user to find the contexts for.
|
||||
* @return void
|
||||
*/
|
||||
public static function add_contexts_for_userid(contextlist $contextlist, $userid);
|
||||
|
||||
/**
|
||||
* Export all user data for the specified user, in the specified contexts.
|
||||
*
|
||||
* @param approved_contextlist $contextlist The approved contexts to export information for.
|
||||
* @return void
|
||||
*/
|
||||
public static function export_user_data(approved_contextlist $contextlist);
|
||||
|
||||
/**
|
||||
* Delete all data for all users in the specified context.
|
||||
*
|
||||
* @param context $context The specific context to delete data for.
|
||||
* @return void
|
||||
*/
|
||||
public static function delete_data_for_all_users_in_context(context $context);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return void
|
||||
*/
|
||||
public static function delete_data_for_user(approved_contextlist $contextlist);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Moodle database: export and delete.
|
||||
*
|
||||
* @package tool_log
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_log\local\privacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use context;
|
||||
use core_privacy\local\request\approved_contextlist;
|
||||
use core_privacy\local\request\writer;
|
||||
|
||||
/**
|
||||
* Moodle database: export and delete trait.
|
||||
*
|
||||
* This is to be used with logstores which use a database and table with the same columns
|
||||
* as the core plugin 'logstore_standard'.
|
||||
*
|
||||
* This trait expects the following methods to be present in the object:
|
||||
*
|
||||
* - public static function get_database_and_table(): [moodle_database|null, string|null]
|
||||
* - public static function get_export_subcontext(): []
|
||||
*
|
||||
* @package tool_log
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
trait moodle_database_export_and_delete {
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
list($db, $table) = static::get_database_and_table();
|
||||
if (!$db || !$table) {
|
||||
return;
|
||||
}
|
||||
|
||||
$userid = $contextlist->get_user()->id;
|
||||
list($insql, $inparams) = $db->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
|
||||
|
||||
$sql = "(userid = :userid1 OR relateduserid = :userid2 OR realuserid = :userid3) AND contextid $insql";
|
||||
$params = array_merge($inparams, [
|
||||
'userid1' => $userid,
|
||||
'userid2' => $userid,
|
||||
'userid3' => $userid,
|
||||
]);
|
||||
|
||||
$path = static::get_export_subcontext();
|
||||
$flush = function($lastcontextid, $data) use ($path) {
|
||||
$context = context::instance_by_id($lastcontextid);
|
||||
writer::with_context($context)->export_data($path, (object) ['logs' => $data]);
|
||||
};
|
||||
|
||||
$lastcontextid = null;
|
||||
$data = [];
|
||||
$recordset = $db->get_recordset_select($table, $sql, $params, 'contextid, timecreated, id');
|
||||
foreach ($recordset as $record) {
|
||||
if ($lastcontextid && $lastcontextid != $record->contextid) {
|
||||
$flush($lastcontextid, $data);
|
||||
$data = [];
|
||||
}
|
||||
$data[] = helper::transform_standard_log_record_for_userid($record, $userid);
|
||||
$lastcontextid = $record->contextid;
|
||||
}
|
||||
if ($lastcontextid) {
|
||||
$flush($lastcontextid, $data);
|
||||
}
|
||||
$recordset->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
list($db, $table) = static::get_database_and_table();
|
||||
if (!$db || !$table) {
|
||||
return;
|
||||
}
|
||||
$db->delete_records($table, ['contextid' => $context->id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
list($db, $table) = static::get_database_and_table();
|
||||
if (!$db || !$table) {
|
||||
return;
|
||||
}
|
||||
list($insql, $inparams) = $db->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
|
||||
$params = array_merge($inparams, ['userid' => $contextlist->get_user()->id]);
|
||||
$db->delete_records_select($table, "userid = :userid AND contextid $insql", $params);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?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 provider.
|
||||
*
|
||||
* @package tool_log
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_log\privacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use context;
|
||||
use core_privacy\local\metadata\collection;
|
||||
use core_privacy\local\request\approved_contextlist;
|
||||
use core_privacy\local\request\transform;
|
||||
use core_privacy\local\request\writer;
|
||||
use tool_log\log\manager;
|
||||
|
||||
/**
|
||||
* Data provider class.
|
||||
*
|
||||
* @package tool_log
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @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 {
|
||||
|
||||
/**
|
||||
* Returns metadata.
|
||||
*
|
||||
* @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_plugintype_link('logstore', [], 'privacy:metadata:logstore');
|
||||
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 \core_privacy\local\request\contextlist();
|
||||
static::call_subplugins_method_with_args('add_contexts_for_userid', [$contextlist, $userid]);
|
||||
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) {
|
||||
static::call_subplugins_method_with_args('export_user_data', [$contextlist]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
static::call_subplugins_method_with_args('delete_data_for_all_users_in_context', [$context]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
static::call_subplugins_method_with_args('delete_data_for_user', [$contextlist]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the subplugins method with arguments.
|
||||
*
|
||||
* @param string $method The method name.
|
||||
* @param array $args The arguments.
|
||||
* @return void
|
||||
*/
|
||||
protected static function call_subplugins_method_with_args($method, array $args = []) {
|
||||
$interface = \tool_log\local\privacy\logstore_provider::class;
|
||||
\core_privacy\manager::plugintype_class_callback('logstore', $interface, $method, $args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,13 @@ $string['actlogshdr'] = 'Available log stores';
|
||||
$string['configlogplugins'] = 'Please enable all required plugins and arrange them in appropriate order.';
|
||||
$string['logging'] = 'Logging';
|
||||
$string['managelogging'] = 'Manage log stores';
|
||||
$string['reportssupported'] = 'Reports supported';
|
||||
$string['pluginname'] = 'Log store manager';
|
||||
$string['privacy:metadata:logstore'] = 'The log stores';
|
||||
$string['privacy:path:logs'] = 'Logs';
|
||||
$string['privacy:request:origin:cli'] = 'Command line tool';
|
||||
$string['privacy:request:origin:restore'] = 'Backup being restored';
|
||||
$string['privacy:request:origin:web'] = 'Standard web request';
|
||||
$string['privacy:request:origin:ws'] = 'Mobile app or web service';
|
||||
$string['reportssupported'] = 'Reports supported';
|
||||
$string['subplugintype_logstore'] = 'Log store';
|
||||
$string['subplugintype_logstore_plural'] = 'Log stores';
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
<?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 provider.
|
||||
*
|
||||
* @package logstore_database
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace logstore_database\privacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use context;
|
||||
use core_privacy\local\metadata\collection;
|
||||
use core_privacy\local\request\contextlist;
|
||||
|
||||
/**
|
||||
* Data provider class.
|
||||
*
|
||||
* @package logstore_database
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements
|
||||
\core_privacy\local\metadata\provider,
|
||||
\tool_log\local\privacy\logstore_provider {
|
||||
|
||||
use \tool_log\local\privacy\moodle_database_export_and_delete;
|
||||
|
||||
/**
|
||||
* Returns metadata.
|
||||
*
|
||||
* @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_external_location_link('log', [
|
||||
'eventname' => 'privacy:metadata:log:eventname',
|
||||
'userid' => 'privacy:metadata:log:userid',
|
||||
'relateduserid' => 'privacy:metadata:log:relateduserid',
|
||||
'anonymous' => 'privacy:metadata:log:anonymous',
|
||||
'other' => 'privacy:metadata:log:other',
|
||||
'timecreated' => 'privacy:metadata:log:timecreated',
|
||||
'origin' => 'privacy:metadata:log:origin',
|
||||
'ip' => 'privacy:metadata:log:ip',
|
||||
'realuserid' => 'privacy:metadata:log:realuserid',
|
||||
], 'privacy:metadata:log');
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add contexts that contain user information for the specified user.
|
||||
*
|
||||
* @param contextlist $contextlist The contextlist to add the contexts to.
|
||||
* @param int $userid The user to find the contexts for.
|
||||
* @return void
|
||||
*/
|
||||
public static function add_contexts_for_userid(contextlist $contextlist, $userid) {
|
||||
list($db, $table) = static::get_database_and_table();
|
||||
if (!$db || !$table) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sql = 'userid = :userid1 OR relateduserid = :userid2 OR realuserid = :userid3';
|
||||
$params = ['userid1' => $userid, 'userid2' => $userid, 'userid3' => $userid];
|
||||
$contextids = $db->get_fieldset_select($table, 'DISTINCT contextid', $sql, $params);
|
||||
if (empty($contextids)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sql = implode(' UNION ', array_map(function($id) use ($db) {
|
||||
return 'SELECT ' . $id . $db->sql_null_from_clause();
|
||||
}, $contextids));
|
||||
$contextlist->add_from_sql($sql, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database object.
|
||||
*
|
||||
* @return array Containing moodle_database, string, or null values.
|
||||
*/
|
||||
protected static function get_database_and_table() {
|
||||
$manager = get_log_manager();
|
||||
$store = new \logstore_database\log\store($manager);
|
||||
$db = $store->get_extdb();
|
||||
return $db ? [$db, $store->get_config_value('dbtable')] : [null, null];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to export the logs to.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function get_export_subcontext() {
|
||||
return [get_string('privacy:path:logs', 'tool_log'), get_string('pluginname', 'logstore_database')];
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,16 @@ $string['other'] = 'Other';
|
||||
$string['participating'] = 'Participating';
|
||||
$string['pluginname'] = 'External database log';
|
||||
$string['pluginname_desc'] = 'A log plugin that stores log entries in an external database table.';
|
||||
$string['privacy:metadata:log'] = 'A collection of past events';
|
||||
$string['privacy:metadata:log:anonymous'] = 'Whether the event was flagged as anonymous';
|
||||
$string['privacy:metadata:log:eventname'] = 'The event name';
|
||||
$string['privacy:metadata:log:ip'] = 'The IP address used at the time of the event';
|
||||
$string['privacy:metadata:log:origin'] = 'The origin of the event';
|
||||
$string['privacy:metadata:log:other'] = 'Additional information about the event';
|
||||
$string['privacy:metadata:log:realuserid'] = 'The ID of the real user behind the event, when masquerading a user.';
|
||||
$string['privacy:metadata:log:relateduserid'] = 'The ID of a user related to this event';
|
||||
$string['privacy:metadata:log:timecreated'] = 'The time at which the event occurred';
|
||||
$string['privacy:metadata:log:userid'] = 'The ID of the user who triggered this event';
|
||||
$string['read'] = 'Read';
|
||||
$string['tablenotfound'] = 'Specified table was not found';
|
||||
$string['teaching'] = 'Teaching';
|
||||
|
||||
@@ -0,0 +1,416 @@
|
||||
<?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 provider tests.
|
||||
*
|
||||
* @package logstore_database
|
||||
* @category test
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
|
||||
use core_privacy\tests\provider_testcase;
|
||||
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;
|
||||
use logstore_database\privacy\provider;
|
||||
|
||||
require_once(__DIR__ . '/fixtures/event.php');
|
||||
|
||||
/**
|
||||
* Data provider testcase class.
|
||||
*
|
||||
* This testcase is almost identical to the logstore_standard testcase, aside from the
|
||||
* initialisation of the relevant logstore obviously.
|
||||
*
|
||||
* @package logstore_database
|
||||
* @category test
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class logstore_database_privacy_testcase extends provider_testcase {
|
||||
|
||||
public function setUp() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
$this->preventResetByRollback(); // Logging waits till the transaction gets committed.
|
||||
|
||||
// Fake the settings, we will abuse the standard plugin table here...
|
||||
set_config('dbdriver', $CFG->dblibrary . '/' . $CFG->dbtype, 'logstore_database');
|
||||
set_config('dbhost', $CFG->dbhost, 'logstore_database');
|
||||
set_config('dbuser', $CFG->dbuser, 'logstore_database');
|
||||
set_config('dbpass', $CFG->dbpass, 'logstore_database');
|
||||
set_config('dbname', $CFG->dbname, 'logstore_database');
|
||||
set_config('dbtable', $CFG->prefix . 'logstore_standard_log', 'logstore_database');
|
||||
if (!empty($CFG->dboptions['dbpersist'])) {
|
||||
set_config('dbpersist', 1, 'logstore_database');
|
||||
} else {
|
||||
set_config('dbpersist', 0, 'logstore_database');
|
||||
}
|
||||
if (!empty($CFG->dboptions['dbsocket'])) {
|
||||
set_config('dbsocket', $CFG->dboptions['dbsocket'], 'logstore_database');
|
||||
} else {
|
||||
set_config('dbsocket', '', 'logstore_database');
|
||||
}
|
||||
if (!empty($CFG->dboptions['dbport'])) {
|
||||
set_config('dbport', $CFG->dboptions['dbport'], 'logstore_database');
|
||||
} else {
|
||||
set_config('dbport', '', 'logstore_database');
|
||||
}
|
||||
if (!empty($CFG->dboptions['dbschema'])) {
|
||||
set_config('dbschema', $CFG->dboptions['dbschema'], 'logstore_database');
|
||||
} else {
|
||||
set_config('dbschema', '', 'logstore_database');
|
||||
}
|
||||
if (!empty($CFG->dboptions['dbcollation'])) {
|
||||
set_config('dbcollation', $CFG->dboptions['dbcollation'], 'logstore_database');
|
||||
} else {
|
||||
set_config('dbcollation', '', 'logstore_database');
|
||||
}
|
||||
if (!empty($CFG->dboptions['dbhandlesoptions'])) {
|
||||
set_config('dbhandlesoptions', $CFG->dboptions['dbhandlesoptions'], 'logstore_database');
|
||||
} else {
|
||||
set_config('dbhandlesoptions', false, 'logstore_database');
|
||||
}
|
||||
}
|
||||
|
||||
public function test_get_contexts_for_userid() {
|
||||
$admin = \core_user::get_user(2);
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$u3 = $this->getDataGenerator()->create_user();
|
||||
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]);
|
||||
$c2 = $this->getDataGenerator()->create_course();
|
||||
$cm2 = $this->getDataGenerator()->create_module('url', ['course' => $c2]);
|
||||
|
||||
$sysctx = context_system::instance();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
$c2ctx = context_course::instance($c2->id);
|
||||
$cm1ctx = context_module::instance($cm1->cmid);
|
||||
$cm2ctx = context_module::instance($cm2->cmid);
|
||||
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
|
||||
// User 1 is the author.
|
||||
$this->setUser($u1);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u1), []);
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $cm1ctx]);
|
||||
$e->trigger();
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u1), [$cm1ctx]);
|
||||
|
||||
// User 2 is the related user.
|
||||
$this->setUser(0);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u2), []);
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $cm2ctx, 'relateduserid' => $u2->id]);
|
||||
$e->trigger();
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u2), [$cm2ctx]);
|
||||
|
||||
// Admin user is the real user.
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($admin), []);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u3), []);
|
||||
$this->setAdminUser();
|
||||
\core\session\manager::loginas($u3->id, $sysctx);
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($admin), [$sysctx, $c1ctx]);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u3), [$sysctx, $c1ctx]);
|
||||
|
||||
// By admin user masquerading u1 related to u3.
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u1), [$cm1ctx]);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u3), [$sysctx, $c1ctx]);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($admin), [$sysctx, $c1ctx]);
|
||||
$this->setAdminUser();
|
||||
\core\session\manager::loginas($u1->id, context_system::instance());
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx, 'relateduserid' => $u3->id]);
|
||||
$e->trigger();
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u1), [$sysctx, $cm1ctx, $c2ctx]);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u3), [$sysctx, $c1ctx, $c2ctx]);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($admin), [$sysctx, $c1ctx, $c2ctx]);
|
||||
}
|
||||
|
||||
public function test_delete_data_for_user() {
|
||||
global $DB;
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$c2 = $this->getDataGenerator()->create_course();
|
||||
$sysctx = context_system::instance();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
$c2ctx = context_course::instance($c2->id);
|
||||
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
|
||||
// User 1 is the author.
|
||||
$this->setUser($u1);
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx]);
|
||||
$e->trigger();
|
||||
|
||||
// User 2 is the author.
|
||||
$this->setUser($u2);
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx]);
|
||||
$e->trigger();
|
||||
|
||||
// Confirm data present.
|
||||
$this->assertTrue($DB->record_exists('logstore_standard_log', ['userid' => $u1->id, 'contextid' => $c1ctx->id]));
|
||||
$this->assertEquals(3, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(2, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
|
||||
|
||||
// Delete all the things!
|
||||
provider::delete_data_for_user(new approved_contextlist($u1, 'logstore_database', [$c1ctx->id]));
|
||||
$this->assertFalse($DB->record_exists('logstore_standard_log', ['userid' => $u1->id, 'contextid' => $c1ctx->id]));
|
||||
$this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(2, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
|
||||
}
|
||||
|
||||
public function test_delete_data_for_all_users_in_context() {
|
||||
global $DB;
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$c2 = $this->getDataGenerator()->create_course();
|
||||
$sysctx = context_system::instance();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
$c2ctx = context_course::instance($c2->id);
|
||||
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
|
||||
// User 1 is the author.
|
||||
$this->setUser($u1);
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx]);
|
||||
$e->trigger();
|
||||
|
||||
// User 2 is the author.
|
||||
$this->setUser($u2);
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx]);
|
||||
$e->trigger();
|
||||
|
||||
// Confirm data present.
|
||||
$this->assertTrue($DB->record_exists('logstore_standard_log', ['contextid' => $c1ctx->id]));
|
||||
$this->assertEquals(3, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(2, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
|
||||
|
||||
// Delete all the things!
|
||||
provider::delete_data_for_all_users_in_context($c1ctx);
|
||||
$this->assertFalse($DB->record_exists('logstore_standard_log', ['contextid' => $c1ctx->id]));
|
||||
$this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
|
||||
}
|
||||
|
||||
public function test_export_data_for_user() {
|
||||
$admin = \core_user::get_user(2);
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$u3 = $this->getDataGenerator()->create_user();
|
||||
$u4 = $this->getDataGenerator()->create_user();
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]);
|
||||
$c2 = $this->getDataGenerator()->create_course();
|
||||
$cm2 = $this->getDataGenerator()->create_module('url', ['course' => $c2]);
|
||||
$sysctx = context_system::instance();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
$c2ctx = context_course::instance($c2->id);
|
||||
$cm1ctx = context_module::instance($cm1->cmid);
|
||||
$cm2ctx = context_module::instance($cm2->cmid);
|
||||
|
||||
$path = [get_string('privacy:path:logs', 'tool_log'), get_string('pluginname', 'logstore_database')];
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
|
||||
// User 1 is the author.
|
||||
$this->setUser($u1);
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx, 'other' => ['i' => 0]]);
|
||||
$e->trigger();
|
||||
|
||||
// User 2 is related.
|
||||
$this->setUser(0);
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx, 'relateduserid' => $u2->id,
|
||||
'other' => ['i' => 1]]);
|
||||
$e->trigger();
|
||||
|
||||
// Admin user masquerades u3, which is related to u4.
|
||||
$this->setAdminUser();
|
||||
\core\session\manager::loginas($u3->id, $sysctx);
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c1ctx, 'relateduserid' => $u4->id,
|
||||
'other' => ['i' => 2]]);
|
||||
$e->trigger();
|
||||
|
||||
// Confirm data present for u1.
|
||||
provider::export_user_data(new approved_contextlist($u1, 'logstore_database', [$c2ctx->id, $c1ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertEmpty($data);
|
||||
$data = writer::with_context($c1ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']);
|
||||
$this->assertSame(0, $data->logs[0]['other']['i']);
|
||||
|
||||
// Confirm data present for u2.
|
||||
writer::reset();
|
||||
provider::export_user_data(new approved_contextlist($u2, 'logstore_database', [$c2ctx->id, $c1ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertEmpty($data);
|
||||
$data = writer::with_context($c1ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(false), $data->logs[0]['author_of_the_action_was_you']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']);
|
||||
$this->assertSame(1, $data->logs[0]['other']['i']);
|
||||
|
||||
// Confirm data present for u3.
|
||||
writer::reset();
|
||||
provider::export_user_data(new approved_contextlist($u3, 'logstore_database', [$c2ctx->id, $c1ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertEmpty($data);
|
||||
$data = writer::with_context($c1ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']);
|
||||
$this->assertEquals(transform::yesno(false), $data->logs[0]['related_user_was_you']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']);
|
||||
$this->assertEquals(transform::yesno(false), $data->logs[0]['masquerading_user_was_you']);
|
||||
$this->assertSame(2, $data->logs[0]['other']['i']);
|
||||
|
||||
// Confirm data present for u4.
|
||||
writer::reset();
|
||||
provider::export_user_data(new approved_contextlist($u4, 'logstore_database', [$c2ctx->id, $c1ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertEmpty($data);
|
||||
$data = writer::with_context($c1ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(false), $data->logs[0]['author_of_the_action_was_you']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']);
|
||||
$this->assertEquals(transform::yesno(false), $data->logs[0]['masquerading_user_was_you']);
|
||||
$this->assertSame(2, $data->logs[0]['other']['i']);
|
||||
|
||||
// Add anonymous events.
|
||||
$this->setUser($u1);
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx, 'relateduserid' => $u2->id,
|
||||
'anonymous' => true]);
|
||||
$e->trigger();
|
||||
$this->setAdminUser();
|
||||
\core\session\manager::loginas($u3->id, $sysctx);
|
||||
$e = \logstore_database\event\unittest_executed::create(['context' => $c2ctx, 'relateduserid' => $u4->id,
|
||||
'anonymous' => true]);
|
||||
$e->trigger();
|
||||
|
||||
// Confirm data present for u1.
|
||||
provider::export_user_data(new approved_contextlist($u1, 'logstore_database', [$c2ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']);
|
||||
|
||||
// Confirm data present for u2.
|
||||
writer::reset();
|
||||
provider::export_user_data(new approved_contextlist($u2, 'logstore_database', [$c2ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']);
|
||||
$this->assertArrayNotHasKey('author_of_the_action_was_you', $data->logs[0]);
|
||||
$this->assertArrayNotHasKey('authorid', $data->logs[0]);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']);
|
||||
|
||||
// Confirm data present for u3.
|
||||
writer::reset();
|
||||
provider::export_user_data(new approved_contextlist($u3, 'logstore_database', [$c2ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']);
|
||||
$this->assertArrayNotHasKey('masquerading_user_was_you', $data->logs[0]);
|
||||
$this->assertArrayNotHasKey('masqueradinguserid', $data->logs[0]);
|
||||
|
||||
// Confirm data present for u4.
|
||||
writer::reset();
|
||||
provider::export_user_data(new approved_contextlist($u4, 'logstore_database', [$c2ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']);
|
||||
$this->assertArrayNotHasKey('author_of_the_action_was_you', $data->logs[0]);
|
||||
$this->assertArrayNotHasKey('authorid', $data->logs[0]);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']);
|
||||
$this->assertArrayNotHasKey('masquerading_user_was_you', $data->logs[0]);
|
||||
$this->assertArrayNotHasKey('masqueradinguserid', $data->logs[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the content of a context list.
|
||||
*
|
||||
* @param contextlist $contextlist The collection.
|
||||
* @param array $expected List of expected contexts or IDs.
|
||||
* @return void
|
||||
*/
|
||||
protected function assert_contextlist_equals($contextlist, array $expected) {
|
||||
$expectedids = array_map(function($context) {
|
||||
if (is_object($context)) {
|
||||
return $context->id;
|
||||
}
|
||||
return $context;
|
||||
}, $expected);
|
||||
$contextids = array_map('intval', $contextlist->get_contextids());
|
||||
sort($contextids);
|
||||
sort($expectedids);
|
||||
$this->assertEquals($expectedids, $contextids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable logging.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function enable_logging() {
|
||||
set_config('enabled_stores', 'logstore_database', 'tool_log');
|
||||
set_config('buffersize', 0, 'logstore_database');
|
||||
set_config('logguests', 1, 'logstore_database');
|
||||
get_log_manager(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contextlist for a user.
|
||||
*
|
||||
* @param object $user The user.
|
||||
* @return contextlist
|
||||
*/
|
||||
protected function get_contextlist_for_user($user) {
|
||||
$contextlist = new contextlist();
|
||||
provider::add_contexts_for_userid($contextlist, $user->id);
|
||||
return $contextlist;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
<?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 provider.
|
||||
*
|
||||
* @package logstore_legacy
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace logstore_legacy\privacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use context;
|
||||
use core_privacy\local\metadata\collection;
|
||||
use core_privacy\local\request\approved_contextlist;
|
||||
use core_privacy\local\request\contextlist;
|
||||
use core_privacy\local\request\transform;
|
||||
use core_privacy\local\request\writer;
|
||||
use tool_log\local\privacy\helper;
|
||||
|
||||
/**
|
||||
* Data provider class.
|
||||
*
|
||||
* @package logstore_legacy
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements
|
||||
\core_privacy\local\metadata\provider,
|
||||
\tool_log\local\privacy\logstore_provider {
|
||||
|
||||
/**
|
||||
* Returns metadata.
|
||||
*
|
||||
* @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_external_location_link('log', [
|
||||
'time' => 'privacy:metadata:log:time',
|
||||
'userid' => 'privacy:metadata:log:userid',
|
||||
'ip' => 'privacy:metadata:log:ip',
|
||||
'action' => 'privacy:metadata:log:action',
|
||||
'url' => 'privacy:metadata:log:url',
|
||||
'info' => 'privacy:metadata:log:info',
|
||||
], 'privacy:metadata:log');
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add contexts that contain user information for the specified user.
|
||||
*
|
||||
* @param contextlist $contextlist The contextlist to add the contexts to.
|
||||
* @param int $userid The user to find the contexts for.
|
||||
* @return void
|
||||
*/
|
||||
public static function add_contexts_for_userid(contextlist $contextlist, $userid) {
|
||||
$sql = "
|
||||
SELECT ctx.id
|
||||
FROM {context} ctx
|
||||
JOIN {log} l
|
||||
ON (l.cmid = 0 AND l.course = ctx.instanceid AND ctx.contextlevel = :courselevel)
|
||||
OR (l.cmid > 0 AND l.cmid = ctx.instanceid AND ctx.contextlevel = :modulelevel)
|
||||
OR (l.course <= 0 AND ctx.id = :syscontextid)
|
||||
WHERE l.userid = :userid";
|
||||
$params = [
|
||||
'courselevel' => CONTEXT_COURSE,
|
||||
'modulelevel' => CONTEXT_MODULE,
|
||||
'syscontextid' => SYSCONTEXTID,
|
||||
'userid' => $userid,
|
||||
];
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
$userid = $contextlist->get_user()->id;
|
||||
list($insql, $inparams) = static::get_sql_where_from_contexts($contextlist->get_contexts());
|
||||
if (empty($insql)) {
|
||||
return;
|
||||
}
|
||||
$sql = "userid = :userid AND $insql";
|
||||
$params = array_merge($inparams, ['userid' => $userid]);
|
||||
|
||||
$path = [get_string('privacy:path:logs', 'tool_log'), get_string('pluginname', 'logstore_legacy')];
|
||||
$flush = function($lastcontextid, $data) use ($path) {
|
||||
$context = context::instance_by_id($lastcontextid);
|
||||
writer::with_context($context)->export_data($path, (object) ['logs' => $data]);
|
||||
};
|
||||
|
||||
$lastcontextid = null;
|
||||
$data = [];
|
||||
$recordset = $DB->get_recordset_select('log', $sql, $params, 'course, cmid, time, id');
|
||||
foreach ($recordset as $record) {
|
||||
$event = \logstore_legacy\event\legacy_logged::restore_legacy($record);
|
||||
$context = $event->get_context();
|
||||
if ($lastcontextid && $lastcontextid != $context->id) {
|
||||
$flush($lastcontextid, $data);
|
||||
$data = [];
|
||||
}
|
||||
|
||||
$extra = $event->get_logextra();
|
||||
$data[] = [
|
||||
'name' => $event->get_name(),
|
||||
'description' => $event->get_description(),
|
||||
'timecreated' => transform::datetime($event->timecreated),
|
||||
'ip' => $extra['ip'],
|
||||
'origin' => helper::transform_origin($extra['origin']),
|
||||
];
|
||||
|
||||
$lastcontextid = $context->id;
|
||||
}
|
||||
if ($lastcontextid) {
|
||||
$flush($lastcontextid, $data);
|
||||
}
|
||||
$recordset->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
list($sql, $params) = static::get_sql_where_from_contexts([$context]);
|
||||
if (empty($sql)) {
|
||||
return;
|
||||
}
|
||||
$DB->delete_records_select('log', $sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
list($sql, $params) = static::get_sql_where_from_contexts($contextlist->get_contexts());
|
||||
if (empty($sql)) {
|
||||
return;
|
||||
}
|
||||
$userid = $contextlist->get_user()->id;
|
||||
$DB->delete_records_select('log', "$sql AND userid = :userid", array_merge($params, ['userid' => $userid]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an SQL where statement from a list of contexts.
|
||||
*
|
||||
* @param array $contexts The contexts.
|
||||
* @return array [$sql, $params]
|
||||
*/
|
||||
protected static function get_sql_where_from_contexts(array $contexts) {
|
||||
global $DB;
|
||||
|
||||
$sorted = array_reduce($contexts, function ($carry, $context) {
|
||||
$level = $context->contextlevel;
|
||||
if ($level == CONTEXT_MODULE || $level == CONTEXT_COURSE) {
|
||||
$carry[$level][] = $context->instanceid;
|
||||
} else if ($level == CONTEXT_SYSTEM) {
|
||||
$carry[$level] = $context->id;
|
||||
}
|
||||
return $carry;
|
||||
}, [
|
||||
CONTEXT_COURSE => [],
|
||||
CONTEXT_MODULE => [],
|
||||
CONTEXT_SYSTEM => null,
|
||||
]);
|
||||
|
||||
$sqls = [];
|
||||
$params = [];
|
||||
|
||||
if (!empty($sorted[CONTEXT_MODULE])) {
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($sorted[CONTEXT_MODULE], SQL_PARAMS_NAMED);
|
||||
$sqls[] = "cmid $insql";
|
||||
$params = array_merge($params, $inparams);
|
||||
}
|
||||
|
||||
if (!empty($sorted[CONTEXT_COURSE])) {
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($sorted[CONTEXT_COURSE], SQL_PARAMS_NAMED);
|
||||
|
||||
$sqls[] = "cmid = 0 AND course $insql";
|
||||
$params = array_merge($params, $inparams);
|
||||
}
|
||||
|
||||
if (!empty($sorted[CONTEXT_SYSTEM])) {
|
||||
$sqls[] = "course <= 0";
|
||||
}
|
||||
|
||||
if (empty($sqls)) {
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
return ['((' . implode(') OR (', $sqls) . '))', $params];
|
||||
}
|
||||
}
|
||||
@@ -27,4 +27,11 @@ $string['loglegacy'] = 'Log legacy data';
|
||||
$string['loglegacy_help'] = 'This plugin records log data to the legacy log table (mdl_log). This functionality has been replaced by newer, richer and more efficient logging plugins, so you should only run this plugin if you have old custom reports that directly query the old log table. Writing to the legacy logs will increase load, so it is recommended that you disable this plugin for performance reasons when it is not needed.';
|
||||
$string['pluginname'] = 'Legacy log';
|
||||
$string['pluginname_desc'] = 'A log plugin that stores log entries in the legacy log table.';
|
||||
$string['privacy:metadata:log'] = 'A collection of past events';
|
||||
$string['privacy:metadata:log:action'] = 'A description of the action';
|
||||
$string['privacy:metadata:log:info'] = 'Additional information';
|
||||
$string['privacy:metadata:log:ip'] = 'The IP address used at the time of the event';
|
||||
$string['privacy:metadata:log:time'] = 'The date at wich the action took place';
|
||||
$string['privacy:metadata:log:url'] = 'The URL related to the event';
|
||||
$string['privacy:metadata:log:userid'] = 'The ID of the user who performed the action';
|
||||
$string['taskcleanup'] = 'Legacy log table cleanup';
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
<?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 provider tests.
|
||||
*
|
||||
* @package logstore_legacy
|
||||
* @category test
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
|
||||
use core_privacy\tests\provider_testcase;
|
||||
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;
|
||||
use logstore_legacy\privacy\provider;
|
||||
use logstore_legacy\event\unittest_executed;
|
||||
|
||||
require_once(__DIR__ . '/fixtures/event.php');
|
||||
|
||||
/**
|
||||
* Data provider testcase class.
|
||||
*
|
||||
* @package logstore_legacy
|
||||
* @category test
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class logstore_legacy_privacy_testcase extends provider_testcase {
|
||||
|
||||
public function setUp() {
|
||||
$this->resetAfterTest();
|
||||
}
|
||||
|
||||
public function test_get_contexts_for_userid() {
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$u3 = $this->getDataGenerator()->create_user();
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]);
|
||||
$sysctx = context_system::instance();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
$cm1ctx = context_module::instance($cm1->cmid);
|
||||
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
|
||||
// User 1 is the author.
|
||||
$this->setUser($u1);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u1), []);
|
||||
$e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 1]]);
|
||||
$e->trigger();
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u1), [$cm1ctx]);
|
||||
|
||||
// User 2 is the author.
|
||||
$this->setUser($u2);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u2), []);
|
||||
$e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 2]]);
|
||||
$e->trigger();
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u2), [$cm1ctx]);
|
||||
|
||||
// User 3 is the author.
|
||||
$this->setUser($u3);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u3), []);
|
||||
$e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 3]]);
|
||||
$e->trigger();
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u3), [$sysctx]);
|
||||
}
|
||||
|
||||
public function test_delete_data_for_user() {
|
||||
global $DB;
|
||||
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$u3 = $this->getDataGenerator()->create_user();
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$c2 = $this->getDataGenerator()->create_course();
|
||||
$cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]);
|
||||
$sysctx = context_system::instance();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
$c2ctx = context_course::instance($c2->id);
|
||||
$cm1ctx = context_module::instance($cm1->cmid);
|
||||
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
|
||||
// User 1 is the author.
|
||||
$this->setUser($u1);
|
||||
$e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 1]]);
|
||||
$e->trigger();
|
||||
$e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 2]]);
|
||||
$e->trigger();
|
||||
$e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 3]]);
|
||||
$e->trigger();
|
||||
$e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 4]]);
|
||||
$e->trigger();
|
||||
|
||||
// User 2 is the author.
|
||||
$this->setUser($u2);
|
||||
$e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 5]]);
|
||||
$e->trigger();
|
||||
$e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 6]]);
|
||||
$e->trigger();
|
||||
$e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 7]]);
|
||||
$e->trigger();
|
||||
|
||||
// Assert what we have.
|
||||
$this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => $cm1->cmid, 'course' => $c1->id]));
|
||||
$this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => $c1->id]));
|
||||
$this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => 0]));
|
||||
$this->assertEquals(4, $DB->count_records('log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(3, $DB->count_records('log', ['userid' => $u2->id]));
|
||||
|
||||
// Delete other context.
|
||||
provider::delete_data_for_user(new approved_contextlist($u1, 'logstore_legacy', [$c2ctx->id]));
|
||||
$this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => $cm1->cmid, 'course' => $c1->id]));
|
||||
$this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => $c1->id]));
|
||||
$this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => 0]));
|
||||
$this->assertEquals(4, $DB->count_records('log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(3, $DB->count_records('log', ['userid' => $u2->id]));
|
||||
|
||||
// Delete system.
|
||||
provider::delete_data_for_user(new approved_contextlist($u1, 'logstore_legacy', [$sysctx->id]));
|
||||
$this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => $cm1->cmid, 'course' => $c1->id]));
|
||||
$this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => $c1->id]));
|
||||
$this->assertFalse($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => 0]));
|
||||
$this->assertEquals(3, $DB->count_records('log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(3, $DB->count_records('log', ['userid' => $u2->id]));
|
||||
|
||||
// Delete course.
|
||||
provider::delete_data_for_user(new approved_contextlist($u1, 'logstore_legacy', [$c1ctx->id]));
|
||||
$this->assertTrue($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => $cm1->cmid, 'course' => $c1->id]));
|
||||
$this->assertFalse($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => $c1->id]));
|
||||
$this->assertFalse($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => 0]));
|
||||
$this->assertEquals(2, $DB->count_records('log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(3, $DB->count_records('log', ['userid' => $u2->id]));
|
||||
|
||||
// Delete course.
|
||||
provider::delete_data_for_user(new approved_contextlist($u1, 'logstore_legacy', [$cm1ctx->id]));
|
||||
$this->assertFalse($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => $cm1->cmid, 'course' => $c1->id]));
|
||||
$this->assertFalse($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => $c1->id]));
|
||||
$this->assertFalse($DB->record_exists('log', ['userid' => $u1->id, 'cmid' => 0, 'course' => 0]));
|
||||
$this->assertEquals(0, $DB->count_records('log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(3, $DB->count_records('log', ['userid' => $u2->id]));
|
||||
}
|
||||
|
||||
public function test_delete_data_for_all_users_in_context() {
|
||||
global $DB;
|
||||
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$u3 = $this->getDataGenerator()->create_user();
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$c2 = $this->getDataGenerator()->create_course();
|
||||
$cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]);
|
||||
$sysctx = context_system::instance();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
$c2ctx = context_course::instance($c2->id);
|
||||
$cm1ctx = context_module::instance($cm1->cmid);
|
||||
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
|
||||
// User 1 is the author.
|
||||
$this->setUser($u1);
|
||||
$e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 1]]);
|
||||
$e->trigger();
|
||||
$e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 2]]);
|
||||
$e->trigger();
|
||||
$e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 3]]);
|
||||
$e->trigger();
|
||||
$e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 4]]);
|
||||
$e->trigger();
|
||||
|
||||
// User 2 is the author.
|
||||
$this->setUser($u2);
|
||||
$e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 5]]);
|
||||
$e->trigger();
|
||||
$e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 6]]);
|
||||
$e->trigger();
|
||||
$e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 7]]);
|
||||
$e->trigger();
|
||||
|
||||
// Assert what we have.
|
||||
$this->assertTrue($DB->record_exists('log', ['cmid' => $cm1->cmid, 'course' => $c1->id]));
|
||||
$this->assertTrue($DB->record_exists('log', ['cmid' => 0, 'course' => $c1->id]));
|
||||
$this->assertTrue($DB->record_exists('log', ['cmid' => 0, 'course' => 0]));
|
||||
$this->assertEquals(4, $DB->count_records('log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(3, $DB->count_records('log', ['userid' => $u2->id]));
|
||||
|
||||
// Delete other context.
|
||||
provider::delete_data_for_all_users_in_context($c2ctx);
|
||||
$this->assertTrue($DB->record_exists('log', ['cmid' => $cm1->cmid, 'course' => $c1->id]));
|
||||
$this->assertTrue($DB->record_exists('log', ['cmid' => 0, 'course' => $c1->id]));
|
||||
$this->assertTrue($DB->record_exists('log', ['cmid' => 0, 'course' => 0]));
|
||||
$this->assertEquals(4, $DB->count_records('log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(3, $DB->count_records('log', ['userid' => $u2->id]));
|
||||
|
||||
// Delete system.
|
||||
provider::delete_data_for_all_users_in_context($sysctx);
|
||||
$this->assertTrue($DB->record_exists('log', ['cmid' => $cm1->cmid, 'course' => $c1->id]));
|
||||
$this->assertTrue($DB->record_exists('log', ['cmid' => 0, 'course' => $c1->id]));
|
||||
$this->assertFalse($DB->record_exists('log', ['cmid' => 0, 'course' => 0]));
|
||||
$this->assertEquals(3, $DB->count_records('log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(2, $DB->count_records('log', ['userid' => $u2->id]));
|
||||
|
||||
// Delete course.
|
||||
provider::delete_data_for_all_users_in_context($c1ctx);
|
||||
$this->assertTrue($DB->record_exists('log', ['cmid' => $cm1->cmid, 'course' => $c1->id]));
|
||||
$this->assertFalse($DB->record_exists('log', ['cmid' => 0, 'course' => $c1->id]));
|
||||
$this->assertFalse($DB->record_exists('log', ['cmid' => 0, 'course' => 0]));
|
||||
$this->assertEquals(2, $DB->count_records('log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(1, $DB->count_records('log', ['userid' => $u2->id]));
|
||||
|
||||
// Delete course.
|
||||
provider::delete_data_for_all_users_in_context($cm1ctx);
|
||||
$this->assertFalse($DB->record_exists('log', ['cmid' => $cm1->cmid, 'course' => $c1->id]));
|
||||
$this->assertFalse($DB->record_exists('log', ['cmid' => 0, 'course' => $c1->id]));
|
||||
$this->assertFalse($DB->record_exists('log', ['cmid' => 0, 'course' => 0]));
|
||||
$this->assertEquals(0, $DB->count_records('log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(0, $DB->count_records('log', ['userid' => $u2->id]));
|
||||
}
|
||||
|
||||
public function test_export_data_for_user() {
|
||||
global $DB;
|
||||
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$u3 = $this->getDataGenerator()->create_user();
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$c2 = $this->getDataGenerator()->create_course();
|
||||
$cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]);
|
||||
$sysctx = context_system::instance();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
$c2ctx = context_course::instance($c2->id);
|
||||
$cm1ctx = context_module::instance($cm1->cmid);
|
||||
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
$path = [get_string('privacy:path:logs', 'tool_log'), get_string('pluginname', 'logstore_legacy')];
|
||||
|
||||
// User 1 is the author.
|
||||
$this->setUser($u1);
|
||||
$e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 1]]);
|
||||
$e->trigger();
|
||||
$e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 2]]);
|
||||
$e->trigger();
|
||||
$e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 3]]);
|
||||
$e->trigger();
|
||||
$e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 4]]);
|
||||
$e->trigger();
|
||||
|
||||
// User 2 is the author.
|
||||
$this->setUser($u2);
|
||||
$e = unittest_executed::create(['context' => $cm1ctx, 'other' => ['sample' => 5]]);
|
||||
$e->trigger();
|
||||
$e = unittest_executed::create(['context' => $c1ctx, 'other' => ['sample' => 6]]);
|
||||
$e->trigger();
|
||||
$e = unittest_executed::create(['context' => $sysctx, 'other' => ['sample' => 7]]);
|
||||
$e->trigger();
|
||||
|
||||
// Test export.
|
||||
provider::export_user_data(new approved_contextlist($u1, 'logstore_legacy', [$cm1ctx->id]));
|
||||
$data = writer::with_context($c1ctx)->get_data($path);
|
||||
$this->assertEmpty($data);
|
||||
$data = writer::with_context($cm1ctx)->get_data($path);
|
||||
$this->assertCount(2, $data->logs);
|
||||
|
||||
writer::reset();
|
||||
provider::export_user_data(new approved_contextlist($u1, 'logstore_legacy', [$c1ctx->id]));
|
||||
$data = writer::with_context($cm1ctx)->get_data($path);
|
||||
$this->assertEmpty($data);
|
||||
$data = writer::with_context($c1ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
|
||||
writer::reset();
|
||||
provider::export_user_data(new approved_contextlist($u1, 'logstore_legacy', [$sysctx->id]));
|
||||
$data = writer::with_context($sysctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the content of a context list.
|
||||
*
|
||||
* @param contextlist $contextlist The collection.
|
||||
* @param array $expected List of expected contexts or IDs.
|
||||
* @return void
|
||||
*/
|
||||
protected function assert_contextlist_equals($contextlist, array $expected) {
|
||||
$expectedids = array_map(function($context) {
|
||||
if (is_object($context)) {
|
||||
return $context->id;
|
||||
}
|
||||
return $context;
|
||||
}, $expected);
|
||||
$contextids = array_map('intval', $contextlist->get_contextids());
|
||||
sort($contextids);
|
||||
sort($expectedids);
|
||||
$this->assertEquals($expectedids, $contextids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable logging.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function enable_logging() {
|
||||
set_config('enabled_stores', 'logstore_legacy', 'tool_log');
|
||||
set_config('loglegacy', 1, 'logstore_legacy');
|
||||
get_log_manager(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contextlist for a user.
|
||||
*
|
||||
* @param object $user The user.
|
||||
* @return contextlist
|
||||
*/
|
||||
protected function get_contextlist_for_user($user) {
|
||||
$contextlist = new contextlist();
|
||||
provider::add_contexts_for_userid($contextlist, $user->id);
|
||||
return $contextlist;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?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 provider.
|
||||
*
|
||||
* @package logstore_standard
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace logstore_standard\privacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use context;
|
||||
use core_privacy\local\metadata\collection;
|
||||
use core_privacy\local\request\contextlist;
|
||||
|
||||
/**
|
||||
* Data provider class.
|
||||
*
|
||||
* @package logstore_standard
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements
|
||||
\core_privacy\local\metadata\provider,
|
||||
\tool_log\local\privacy\logstore_provider {
|
||||
|
||||
use \tool_log\local\privacy\moodle_database_export_and_delete;
|
||||
|
||||
/**
|
||||
* Returns metadata.
|
||||
*
|
||||
* @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_database_table('logstore_standard_log', [
|
||||
'eventname' => 'privacy:metadata:log:eventname',
|
||||
'userid' => 'privacy:metadata:log:userid',
|
||||
'relateduserid' => 'privacy:metadata:log:relateduserid',
|
||||
'anonymous' => 'privacy:metadata:log:anonymous',
|
||||
'other' => 'privacy:metadata:log:other',
|
||||
'timecreated' => 'privacy:metadata:log:timecreated',
|
||||
'origin' => 'privacy:metadata:log:origin',
|
||||
'ip' => 'privacy:metadata:log:ip',
|
||||
'realuserid' => 'privacy:metadata:log:realuserid',
|
||||
], 'privacy:metadata:log');
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add contexts that contain user information for the specified user.
|
||||
*
|
||||
* @param contextlist $contextlist The contextlist to add the contexts to.
|
||||
* @param int $userid The user to find the contexts for.
|
||||
* @return void
|
||||
*/
|
||||
public static function add_contexts_for_userid(contextlist $contextlist, $userid) {
|
||||
$sql = "
|
||||
SELECT l.contextid
|
||||
FROM {logstore_standard_log} l
|
||||
WHERE l.userid = :userid1
|
||||
OR l.relateduserid = :userid2
|
||||
OR l.realuserid = :userid3";
|
||||
$contextlist->add_from_sql($sql, [
|
||||
'userid1' => $userid,
|
||||
'userid2' => $userid,
|
||||
'userid3' => $userid,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database object.
|
||||
*
|
||||
* @return array Containing moodle_database, string, or null values.
|
||||
*/
|
||||
protected static function get_database_and_table() {
|
||||
global $DB;
|
||||
return [$DB, 'logstore_standard_log'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to export the logs to.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function get_export_subcontext() {
|
||||
return [get_string('privacy:path:logs', 'tool_log'), get_string('pluginname', 'logstore_standard')];
|
||||
}
|
||||
}
|
||||
@@ -25,4 +25,14 @@
|
||||
$string['buffersize'] = 'Write buffer size';
|
||||
$string['pluginname'] = 'Standard log';
|
||||
$string['pluginname_desc'] = 'A log plugin stores log entries in a Moodle database table.';
|
||||
$string['privacy:metadata:log'] = 'A collection of past events';
|
||||
$string['privacy:metadata:log:anonymous'] = 'Whether the event was flagged as anonymous';
|
||||
$string['privacy:metadata:log:eventname'] = 'The event name';
|
||||
$string['privacy:metadata:log:ip'] = 'The IP address used at the time of the event';
|
||||
$string['privacy:metadata:log:origin'] = 'The origin of the event';
|
||||
$string['privacy:metadata:log:other'] = 'Additional information about the event';
|
||||
$string['privacy:metadata:log:realuserid'] = 'The ID of the real user behind the event, when masquerading a user.';
|
||||
$string['privacy:metadata:log:relateduserid'] = 'The ID of a user related to this event';
|
||||
$string['privacy:metadata:log:timecreated'] = 'The time at which the event occurred';
|
||||
$string['privacy:metadata:log:userid'] = 'The ID of the user who triggered this event';
|
||||
$string['taskcleanup'] = 'Log table cleanup';
|
||||
|
||||
@@ -0,0 +1,373 @@
|
||||
<?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 provider tests.
|
||||
*
|
||||
* @package logstore_standard
|
||||
* @category test
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
|
||||
use core_privacy\tests\provider_testcase;
|
||||
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;
|
||||
use logstore_standard\privacy\provider;
|
||||
|
||||
require_once(__DIR__ . '/fixtures/event.php');
|
||||
|
||||
/**
|
||||
* Data provider testcase class.
|
||||
*
|
||||
* @package logstore_standard
|
||||
* @category test
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class logstore_standard_privacy_testcase extends provider_testcase {
|
||||
|
||||
public function setUp() {
|
||||
$this->resetAfterTest();
|
||||
$this->preventResetByRollback(); // Logging waits till the transaction gets committed.
|
||||
}
|
||||
|
||||
public function test_get_contexts_for_userid() {
|
||||
$admin = \core_user::get_user(2);
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$u3 = $this->getDataGenerator()->create_user();
|
||||
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]);
|
||||
$c2 = $this->getDataGenerator()->create_course();
|
||||
$cm2 = $this->getDataGenerator()->create_module('url', ['course' => $c2]);
|
||||
|
||||
$sysctx = context_system::instance();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
$c2ctx = context_course::instance($c2->id);
|
||||
$cm1ctx = context_module::instance($cm1->cmid);
|
||||
$cm2ctx = context_module::instance($cm2->cmid);
|
||||
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
|
||||
// User 1 is the author.
|
||||
$this->setUser($u1);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u1), []);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $cm1ctx]);
|
||||
$e->trigger();
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u1), [$cm1ctx]);
|
||||
|
||||
// User 2 is the related user.
|
||||
$this->setUser(0);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u2), []);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $cm2ctx, 'relateduserid' => $u2->id]);
|
||||
$e->trigger();
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u2), [$cm2ctx]);
|
||||
|
||||
// Admin user is the real user.
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($admin), []);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u3), []);
|
||||
$this->setAdminUser();
|
||||
\core\session\manager::loginas($u3->id, $sysctx);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($admin), [$sysctx, $c1ctx]);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u3), [$sysctx, $c1ctx]);
|
||||
|
||||
// By admin user masquerading u1 related to u3.
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u1), [$cm1ctx]);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u3), [$sysctx, $c1ctx]);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($admin), [$sysctx, $c1ctx]);
|
||||
$this->setAdminUser();
|
||||
\core\session\manager::loginas($u1->id, context_system::instance());
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c2ctx, 'relateduserid' => $u3->id]);
|
||||
$e->trigger();
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u1), [$sysctx, $cm1ctx, $c2ctx]);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($u3), [$sysctx, $c1ctx, $c2ctx]);
|
||||
$this->assert_contextlist_equals($this->get_contextlist_for_user($admin), [$sysctx, $c1ctx, $c2ctx]);
|
||||
}
|
||||
|
||||
public function test_delete_data_for_user() {
|
||||
global $DB;
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$c2 = $this->getDataGenerator()->create_course();
|
||||
$sysctx = context_system::instance();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
$c2ctx = context_course::instance($c2->id);
|
||||
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
|
||||
// User 1 is the author.
|
||||
$this->setUser($u1);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c2ctx]);
|
||||
$e->trigger();
|
||||
|
||||
// User 2 is the author.
|
||||
$this->setUser($u2);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c2ctx]);
|
||||
$e->trigger();
|
||||
|
||||
// Confirm data present.
|
||||
$this->assertTrue($DB->record_exists('logstore_standard_log', ['userid' => $u1->id, 'contextid' => $c1ctx->id]));
|
||||
$this->assertEquals(3, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(2, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
|
||||
|
||||
// Delete all the things!
|
||||
provider::delete_data_for_user(new approved_contextlist($u1, 'logstore_standard', [$c1ctx->id]));
|
||||
$this->assertFalse($DB->record_exists('logstore_standard_log', ['userid' => $u1->id, 'contextid' => $c1ctx->id]));
|
||||
$this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(2, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
|
||||
}
|
||||
|
||||
public function test_delete_data_for_all_users_in_context() {
|
||||
global $DB;
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$c2 = $this->getDataGenerator()->create_course();
|
||||
$sysctx = context_system::instance();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
$c2ctx = context_course::instance($c2->id);
|
||||
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
|
||||
// User 1 is the author.
|
||||
$this->setUser($u1);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c2ctx]);
|
||||
$e->trigger();
|
||||
|
||||
// User 2 is the author.
|
||||
$this->setUser($u2);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c2ctx]);
|
||||
$e->trigger();
|
||||
|
||||
// Confirm data present.
|
||||
$this->assertTrue($DB->record_exists('logstore_standard_log', ['contextid' => $c1ctx->id]));
|
||||
$this->assertEquals(3, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(2, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
|
||||
|
||||
// Delete all the things!
|
||||
provider::delete_data_for_all_users_in_context($c1ctx);
|
||||
$this->assertFalse($DB->record_exists('logstore_standard_log', ['contextid' => $c1ctx->id]));
|
||||
$this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
|
||||
}
|
||||
|
||||
public function test_export_data_for_user() {
|
||||
$admin = \core_user::get_user(2);
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$u3 = $this->getDataGenerator()->create_user();
|
||||
$u4 = $this->getDataGenerator()->create_user();
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$cm1 = $this->getDataGenerator()->create_module('url', ['course' => $c1]);
|
||||
$c2 = $this->getDataGenerator()->create_course();
|
||||
$cm2 = $this->getDataGenerator()->create_module('url', ['course' => $c2]);
|
||||
$sysctx = context_system::instance();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
$c2ctx = context_course::instance($c2->id);
|
||||
$cm1ctx = context_module::instance($cm1->cmid);
|
||||
$cm2ctx = context_module::instance($cm2->cmid);
|
||||
|
||||
$path = [get_string('privacy:path:logs', 'tool_log'), get_string('pluginname', 'logstore_standard')];
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
|
||||
// User 1 is the author.
|
||||
$this->setUser($u1);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx, 'other' => ['i' => 0]]);
|
||||
$e->trigger();
|
||||
|
||||
// User 2 is related.
|
||||
$this->setUser(0);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx, 'relateduserid' => $u2->id,
|
||||
'other' => ['i' => 1]]);
|
||||
$e->trigger();
|
||||
|
||||
// Admin user masquerades u3, which is related to u4.
|
||||
$this->setAdminUser();
|
||||
\core\session\manager::loginas($u3->id, $sysctx);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx, 'relateduserid' => $u4->id,
|
||||
'other' => ['i' => 2]]);
|
||||
$e->trigger();
|
||||
|
||||
// Confirm data present for u1.
|
||||
provider::export_user_data(new approved_contextlist($u1, 'logstore_standard', [$c2ctx->id, $c1ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertEmpty($data);
|
||||
$data = writer::with_context($c1ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']);
|
||||
$this->assertSame(0, $data->logs[0]['other']['i']);
|
||||
|
||||
// Confirm data present for u2.
|
||||
writer::reset();
|
||||
provider::export_user_data(new approved_contextlist($u2, 'logstore_standard', [$c2ctx->id, $c1ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertEmpty($data);
|
||||
$data = writer::with_context($c1ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(false), $data->logs[0]['author_of_the_action_was_you']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']);
|
||||
$this->assertSame(1, $data->logs[0]['other']['i']);
|
||||
|
||||
// Confirm data present for u3.
|
||||
writer::reset();
|
||||
provider::export_user_data(new approved_contextlist($u3, 'logstore_standard', [$c2ctx->id, $c1ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertEmpty($data);
|
||||
$data = writer::with_context($c1ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']);
|
||||
$this->assertEquals(transform::yesno(false), $data->logs[0]['related_user_was_you']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']);
|
||||
$this->assertEquals(transform::yesno(false), $data->logs[0]['masquerading_user_was_you']);
|
||||
$this->assertSame(2, $data->logs[0]['other']['i']);
|
||||
|
||||
// Confirm data present for u4.
|
||||
writer::reset();
|
||||
provider::export_user_data(new approved_contextlist($u4, 'logstore_standard', [$c2ctx->id, $c1ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertEmpty($data);
|
||||
$data = writer::with_context($c1ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(false), $data->logs[0]['author_of_the_action_was_you']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']);
|
||||
$this->assertEquals(transform::yesno(false), $data->logs[0]['masquerading_user_was_you']);
|
||||
$this->assertSame(2, $data->logs[0]['other']['i']);
|
||||
|
||||
// Add anonymous events.
|
||||
$this->setUser($u1);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c2ctx, 'relateduserid' => $u2->id,
|
||||
'anonymous' => true]);
|
||||
$e->trigger();
|
||||
$this->setAdminUser();
|
||||
\core\session\manager::loginas($u3->id, $sysctx);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c2ctx, 'relateduserid' => $u4->id,
|
||||
'anonymous' => true]);
|
||||
$e->trigger();
|
||||
|
||||
// Confirm data present for u1.
|
||||
provider::export_user_data(new approved_contextlist($u1, 'logstore_standard', [$c2ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']);
|
||||
|
||||
// Confirm data present for u2.
|
||||
writer::reset();
|
||||
provider::export_user_data(new approved_contextlist($u2, 'logstore_standard', [$c2ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']);
|
||||
$this->assertArrayNotHasKey('author_of_the_action_was_you', $data->logs[0]);
|
||||
$this->assertArrayNotHasKey('authorid', $data->logs[0]);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']);
|
||||
|
||||
// Confirm data present for u3.
|
||||
writer::reset();
|
||||
provider::export_user_data(new approved_contextlist($u3, 'logstore_standard', [$c2ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']);
|
||||
$this->assertArrayNotHasKey('masquerading_user_was_you', $data->logs[0]);
|
||||
$this->assertArrayNotHasKey('masqueradinguserid', $data->logs[0]);
|
||||
|
||||
// Confirm data present for u4.
|
||||
writer::reset();
|
||||
provider::export_user_data(new approved_contextlist($u4, 'logstore_standard', [$c2ctx->id]));
|
||||
$data = writer::with_context($c2ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['action_was_done_anonymously']);
|
||||
$this->assertArrayNotHasKey('author_of_the_action_was_you', $data->logs[0]);
|
||||
$this->assertArrayNotHasKey('authorid', $data->logs[0]);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['related_user_was_you']);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_masqueraded']);
|
||||
$this->assertArrayNotHasKey('masquerading_user_was_you', $data->logs[0]);
|
||||
$this->assertArrayNotHasKey('masqueradinguserid', $data->logs[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the content of a context list.
|
||||
*
|
||||
* @param contextlist $contextlist The collection.
|
||||
* @param array $expected List of expected contexts or IDs.
|
||||
* @return void
|
||||
*/
|
||||
protected function assert_contextlist_equals($contextlist, array $expected) {
|
||||
$expectedids = array_map(function($context) {
|
||||
if (is_object($context)) {
|
||||
return $context->id;
|
||||
}
|
||||
return $context;
|
||||
}, $expected);
|
||||
$contextids = array_map('intval', $contextlist->get_contextids());
|
||||
sort($contextids);
|
||||
sort($expectedids);
|
||||
$this->assertEquals($expectedids, $contextids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable logging.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function enable_logging() {
|
||||
set_config('enabled_stores', 'logstore_standard', 'tool_log');
|
||||
set_config('buffersize', 0, 'logstore_standard');
|
||||
set_config('logguests', 1, 'logstore_standard');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contextlist for a user.
|
||||
*
|
||||
* @param object $user The user.
|
||||
* @return contextlist
|
||||
*/
|
||||
protected function get_contextlist_for_user($user) {
|
||||
$contextlist = new contextlist();
|
||||
provider::add_contexts_for_userid($contextlist, $user->id);
|
||||
return $contextlist;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
<?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 provider tests.
|
||||
*
|
||||
* @package tool_log
|
||||
* @category test
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
|
||||
use core_privacy\tests\provider_testcase;
|
||||
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;
|
||||
use tool_log\privacy\provider;
|
||||
|
||||
require_once($CFG->dirroot . '/admin/tool/log/store/standard/tests/fixtures/event.php');
|
||||
|
||||
/**
|
||||
* Data provider testcase class.
|
||||
*
|
||||
* We're not testing the full functionality, just that the provider passes the requests
|
||||
* down to at least one of its subplugin. Each subplugin should have tests to cover the
|
||||
* different provider methods in depth.
|
||||
*
|
||||
* @package tool_log
|
||||
* @category test
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_log_privacy_testcase extends provider_testcase {
|
||||
|
||||
public function setUp() {
|
||||
$this->resetAfterTest();
|
||||
$this->preventResetByRollback(); // Logging waits till the transaction gets committed.
|
||||
}
|
||||
|
||||
public function test_get_contexts_for_userid() {
|
||||
$admin = \core_user::get_user(2);
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
|
||||
$this->setUser($u1);
|
||||
$this->assertEmpty(provider::get_contexts_for_userid($u1->id)->get_contextids(), []);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$this->assertEquals($c1ctx->id, provider::get_contexts_for_userid($u1->id)->get_contextids()[0]);
|
||||
}
|
||||
|
||||
public function test_delete_data_for_user() {
|
||||
global $DB;
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
|
||||
// User 1 is the author.
|
||||
$this->setUser($u1);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
|
||||
// User 2 is the author.
|
||||
$this->setUser($u2);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
|
||||
// Confirm data present.
|
||||
$this->assertTrue($DB->record_exists('logstore_standard_log', ['userid' => $u1->id, 'contextid' => $c1ctx->id]));
|
||||
$this->assertEquals(2, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
|
||||
|
||||
// Delete all the things!
|
||||
provider::delete_data_for_user(new approved_contextlist($u1, 'logstore_standard', [$c1ctx->id]));
|
||||
$this->assertFalse($DB->record_exists('logstore_standard_log', ['userid' => $u1->id, 'contextid' => $c1ctx->id]));
|
||||
$this->assertEquals(0, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
|
||||
}
|
||||
|
||||
public function test_delete_data_for_all_users_in_context() {
|
||||
global $DB;
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
|
||||
// User 1 is the author.
|
||||
$this->setUser($u1);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
|
||||
// User 2 is the author.
|
||||
$this->setUser($u2);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx]);
|
||||
$e->trigger();
|
||||
|
||||
// Confirm data present.
|
||||
$this->assertTrue($DB->record_exists('logstore_standard_log', ['contextid' => $c1ctx->id]));
|
||||
$this->assertEquals(2, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(1, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
|
||||
|
||||
// Delete all the things!
|
||||
provider::delete_data_for_all_users_in_context($c1ctx);
|
||||
$this->assertFalse($DB->record_exists('logstore_standard_log', ['contextid' => $c1ctx->id]));
|
||||
$this->assertEquals(0, $DB->count_records('logstore_standard_log', ['userid' => $u1->id]));
|
||||
$this->assertEquals(0, $DB->count_records('logstore_standard_log', ['userid' => $u2->id]));
|
||||
}
|
||||
|
||||
public function test_export_data_for_user() {
|
||||
$admin = \core_user::get_user(2);
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$c1 = $this->getDataGenerator()->create_course();
|
||||
$c1ctx = context_course::instance($c1->id);
|
||||
|
||||
$path = [get_string('privacy:path:logs', 'tool_log'), get_string('pluginname', 'logstore_standard')];
|
||||
$this->enable_logging();
|
||||
$manager = get_log_manager(true);
|
||||
|
||||
// User 1 is the author.
|
||||
$this->setUser($u1);
|
||||
$e = \logstore_standard\event\unittest_executed::create(['context' => $c1ctx, 'other' => ['i' => 123]]);
|
||||
$e->trigger();
|
||||
|
||||
// Confirm data present for u1.
|
||||
provider::export_user_data(new approved_contextlist($u1, 'tool_log', [$c1ctx->id]));
|
||||
$data = writer::with_context($c1ctx)->get_data($path);
|
||||
$this->assertCount(1, $data->logs);
|
||||
$this->assertEquals(transform::yesno(true), $data->logs[0]['author_of_the_action_was_you']);
|
||||
$this->assertSame(123, $data->logs[0]['other']['i']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable logging.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function enable_logging() {
|
||||
set_config('enabled_stores', 'logstore_standard', 'tool_log');
|
||||
set_config('buffersize', 0, 'logstore_standard');
|
||||
set_config('logguests', 1, 'logstore_standard');
|
||||
}
|
||||
}
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -129,6 +129,11 @@ define(['jquery',
|
||||
* @return {Promise}
|
||||
*/
|
||||
PlanActions.prototype._callAndRefresh = function(calls, planData) {
|
||||
// Because this function causes a refresh, we must track the JS completion from start to finish to prevent
|
||||
// stale reference issues in Behat.
|
||||
var callKey = 'tool_lp/planactions:_callAndRefresh-' + Math.floor(Math.random() * Math.floor(1000));
|
||||
M.util.js_pending(callKey);
|
||||
|
||||
var self = this;
|
||||
|
||||
calls.push({
|
||||
@@ -141,7 +146,10 @@ define(['jquery',
|
||||
.then(function() {
|
||||
self._renderView(arguments[arguments.length - 1]);
|
||||
})
|
||||
.fail(notification.exception);
|
||||
.fail(notification.exception)
|
||||
.always(function() {
|
||||
return M.util.js_complete(callKey);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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_lp.
|
||||
*
|
||||
* @package tool_lp
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_lp\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_lp 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';
|
||||
}
|
||||
}
|
||||
@@ -292,3 +292,4 @@ $string['xcompetencieslinkedoutofy'] = '{$a->x} out of {$a->y} competencies link
|
||||
$string['xcompetenciesproficientoutofy'] = '{$a->x} out of {$a->y} competencies are proficient';
|
||||
$string['xcompetenciesproficientoutofyincourse'] = 'You are proficient in {$a->x} out of {$a->y} competencies in this course.';
|
||||
$string['xplanscompletedoutofy'] = '{$a->x} out of {$a->y} learning plans completed for this template';
|
||||
$string['privacy:metadata'] = 'The Learning plans 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_lpimportcsv.
|
||||
*
|
||||
* @package tool_lpimportcsv
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_lpimportcsv\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_lpimportcsv 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';
|
||||
}
|
||||
}
|
||||
@@ -53,3 +53,4 @@ $string['scaleconfiguration'] = 'Scale configuration';
|
||||
$string['scalevalues'] = 'Scale values';
|
||||
$string['shortname'] = 'Short name';
|
||||
$string['taxonomy'] = 'Taxonomy';
|
||||
$string['privacy:metadata'] = 'The Import competency framework 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_lpmigrate.
|
||||
*
|
||||
* @package tool_lpmigrate
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_lpmigrate\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_lpmigrate 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';
|
||||
}
|
||||
}
|
||||
@@ -69,3 +69,4 @@ $string['warningcouldnotremovemodulecompetency'] = 'The activity or resource com
|
||||
$string['warningdestinationcoursecompetencyalreadyexists'] = 'The destination course competency already exists.';
|
||||
$string['warningdestinationmodulecompetencyalreadyexists'] = 'The destination activity or resource competency already exists.';
|
||||
$string['warnings'] = 'Warnings';
|
||||
$string['privacy:metadata'] = 'The Competencies migration tool plugin does not store any personal data.';
|
||||
|
||||
@@ -310,6 +310,16 @@ class manager {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove older verification failures.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function tidy_old_verification_failures() {
|
||||
global $DB;
|
||||
$DB->delete_records_select('messageinbound_messagelist', 'timecreated < :time', ['time' => time() - DAYSECS]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a message and pass it through the Inbound Message handling systems.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
<?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 provider.
|
||||
*
|
||||
* @package tool_messageinbound
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_messageinbound\privacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use context;
|
||||
use context_user;
|
||||
use core_privacy\local\metadata\collection;
|
||||
use core_privacy\local\request\approved_contextlist;
|
||||
use core_privacy\local\request\transform;
|
||||
use core_privacy\local\request\writer;
|
||||
|
||||
/**
|
||||
* Data provider class.
|
||||
*
|
||||
* @package tool_messageinbound
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @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 metadata.
|
||||
*
|
||||
* @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_database_table('messageinbound_messagelist', [
|
||||
'messageid' => 'privacy:metadata:messagelist:messageid',
|
||||
'userid' => 'privacy:metadata:messagelist:userid',
|
||||
'address' => 'privacy:metadata:messagelist:address',
|
||||
'timecreated' => 'privacy:metadata:messagelist:timecreated',
|
||||
], 'privacy:metadata:messagelist');
|
||||
|
||||
// Arguably the keys are handled by \core\message\inbound\address_manager and thus could/should be handled by core.
|
||||
$collection->add_subsystem_link('core_userkey', [], 'privacy:metadata:coreuserkey');
|
||||
|
||||
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 \core_privacy\local\request\contextlist();
|
||||
|
||||
// Always add the user context so we're sure we're not dodging user keys, besides it's not costly to do so.
|
||||
$contextlist->add_user_context($userid);
|
||||
|
||||
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 (!static::approved_contextlist_contains_my_context($contextlist)) {
|
||||
// We only care about the user's user context.
|
||||
return;
|
||||
}
|
||||
|
||||
$userid = $contextlist->get_user()->id;
|
||||
$context = context_user::instance($userid);
|
||||
$path = [get_string('messageinbound', 'tool_messageinbound')];
|
||||
|
||||
// Export user keys.
|
||||
\core_userkey\privacy\provider::export_userkeys($context, $path, 'messageinbound_handler');
|
||||
|
||||
// Export the message list.
|
||||
$data = [];
|
||||
$recordset = $DB->get_recordset('messageinbound_messagelist', ['userid' => $userid], 'timecreated, id');
|
||||
foreach ($recordset as $record) {
|
||||
$data[] = [
|
||||
'received_at' => $record->address,
|
||||
'timecreated' => transform::datetime($record->timecreated),
|
||||
];
|
||||
}
|
||||
$recordset->close();
|
||||
writer::with_context($context)->export_data($path, (object) ['messages_pending_validation' => $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;
|
||||
if ($context->contextlevel != CONTEXT_USER) {
|
||||
return;
|
||||
}
|
||||
|
||||
static::delete_user_data($context->instanceid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (!static::approved_contextlist_contains_my_context($contextlist)) {
|
||||
// We only care about the user's user context.
|
||||
return;
|
||||
}
|
||||
|
||||
static::delete_user_data($contextlist->get_user()->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a user's data.
|
||||
*
|
||||
* @param int $userid The user ID.
|
||||
* @return void
|
||||
*/
|
||||
protected static function delete_user_data($userid) {
|
||||
global $DB;
|
||||
$DB->delete_records_select('messageinbound_messagelist', 'userid = :userid', ['userid' => $userid]);
|
||||
\core_userkey\privacy\provider::delete_userkeys('messageinbound_handler', $userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the contextlist contains our own context.
|
||||
*
|
||||
* @param approved_contextlist $contextlist The contextlist
|
||||
* @return bool
|
||||
*/
|
||||
protected static function approved_contextlist_contains_my_context(approved_contextlist $contextlist) {
|
||||
$userid = $contextlist->get_user()->id;
|
||||
foreach ($contextlist->get_contexts() as $context) {
|
||||
if ($context->contextlevel == CONTEXT_USER && $context->instanceid == $userid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -49,6 +49,7 @@ class cleanup_task extends \core\task\scheduled_task {
|
||||
*/
|
||||
public function execute() {
|
||||
$manager = new \tool_messageinbound\manager();
|
||||
return $manager->tidy_old_messages();
|
||||
$manager->tidy_old_messages();
|
||||
$manager->tidy_old_verification_failures();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
$string['classname'] = 'Class name';
|
||||
$string['component'] = 'Component';
|
||||
$string['configmessageinboundhost'] = 'The address of the server that Moodle should check mail against. To specify a non-default port, you can use the [server]:[port], for example mail.example.com:587 format. If you leave this field blank, Moodle will use the default port for the type of mail server you specify.';
|
||||
$string['configmessageinboundhost'] = 'The address of the server that Moodle should check mail against. To specify a non-default port, use [server]:[port], for example mail.example.com:587. If a port isn\'t specified, the default port for the type of mail server will be used.';
|
||||
$string['defaultexpiration'] = 'Default address expiry period';
|
||||
$string['defaultexpiration_help'] = 'When an email address is generated by the handler, it can be set to automatically expire after a period of time, so that it can no longer be used. It is advisable to set an expiry period.';
|
||||
$string['description'] = 'Description';
|
||||
@@ -94,6 +94,12 @@ $string['onehour'] = 'One hour';
|
||||
$string['oneweek'] = 'One week';
|
||||
$string['oneyear'] = 'One year';
|
||||
$string['pluginname'] = 'Inbound message configuration';
|
||||
$string['privacy:metadata:coreuserkey'] = 'User\'s keys to validate the email received';
|
||||
$string['privacy:metadata:messagelist'] = 'A list of message identifiers which failed validation and requires further authorisation';
|
||||
$string['privacy:metadata:messagelist:address'] = 'The address at which the email was sent';
|
||||
$string['privacy:metadata:messagelist:messageid'] = 'The message ID';
|
||||
$string['privacy:metadata:messagelist:timecreated'] = 'The time at which the record was made';
|
||||
$string['privacy:metadata:messagelist:userid'] = 'The ID of user who need to approve the message';
|
||||
$string['replysubjectprefix'] = 'Re:';
|
||||
$string['requirevalidation'] = 'Validate sender address';
|
||||
$string['name'] = 'Name';
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Manager tests.
|
||||
*
|
||||
* @package tool_messageinbound
|
||||
* @category test
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
|
||||
use core_privacy\tests\provider_testcase;
|
||||
use core_privacy\local\request\approved_contextlist;
|
||||
use core_privacy\local\request\transform;
|
||||
use core_privacy\local\request\writer;
|
||||
use tool_messageinbound\privacy\provider;
|
||||
|
||||
/**
|
||||
* Manager testcase class.
|
||||
*
|
||||
* @package tool_messageinbound
|
||||
* @category test
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_messageinbound_manager_testcase extends provider_testcase {
|
||||
|
||||
public function setUp() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Pretend the system is enabled.
|
||||
$CFG->messageinbound_enabled = true;
|
||||
$CFG->messageinbound_mailbox = 'mailbox';
|
||||
$CFG->messageinbound_domain = 'example.com';
|
||||
}
|
||||
|
||||
public function test_tidy_old_verification_failures() {
|
||||
global $DB;
|
||||
|
||||
$now = time();
|
||||
$stale = $now - DAYSECS - 1; // Make a second older because PHP Unit is too damn fast!!
|
||||
|
||||
$this->create_messagelist(['timecreated' => $now]);
|
||||
$this->create_messagelist(['timecreated' => $now - HOURSECS]);
|
||||
$this->create_messagelist(['timecreated' => $stale]);
|
||||
$this->create_messagelist(['timecreated' => $stale - HOURSECS]);
|
||||
$this->create_messagelist(['timecreated' => $stale - YEARSECS]);
|
||||
|
||||
$this->assertEquals(5, $DB->count_records('messageinbound_messagelist', []));
|
||||
$this->assertEquals(3, $DB->count_records_select('messageinbound_messagelist', 'timecreated < :t', ['t' => $stale + 1]));
|
||||
|
||||
$manager = new \tool_messageinbound\manager();
|
||||
$manager->tidy_old_verification_failures();
|
||||
|
||||
$this->assertEquals(2, $DB->count_records('messageinbound_messagelist', []));
|
||||
$this->assertEquals(0, $DB->count_records_select('messageinbound_messagelist', 'timecreated < :t', ['t' => $stale + 1]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a message to validate.
|
||||
*
|
||||
* @param array $params The params.
|
||||
* @return stdClass
|
||||
*/
|
||||
protected function create_messagelist(array $params) {
|
||||
global $DB, $USER;
|
||||
$record = (object) array_merge([
|
||||
'messageid' => 'abc',
|
||||
'userid' => $USER->id,
|
||||
'address' => 'text@example.com',
|
||||
'timecreated' => time(),
|
||||
], $params);
|
||||
$record->id = $DB->insert_record('messageinbound_messagelist', $record);
|
||||
return $record;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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/>.
|
||||
|
||||
/**
|
||||
* Data provider tests.
|
||||
*
|
||||
* @package tool_messageinbound
|
||||
* @category test
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
|
||||
use core_privacy\tests\provider_testcase;
|
||||
use core_privacy\local\request\approved_contextlist;
|
||||
use core_privacy\local\request\transform;
|
||||
use core_privacy\local\request\writer;
|
||||
use tool_messageinbound\privacy\provider;
|
||||
|
||||
/**
|
||||
* Data provider testcase class.
|
||||
*
|
||||
* @package tool_messageinbound
|
||||
* @category test
|
||||
* @copyright 2018 Frédéric Massart
|
||||
* @author Frédéric Massart <fred@branchup.tech>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_messageinbound_privacy_testcase extends provider_testcase {
|
||||
|
||||
public function setUp() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Pretend the system is enabled.
|
||||
$CFG->messageinbound_enabled = true;
|
||||
$CFG->messageinbound_mailbox = 'mailbox';
|
||||
$CFG->messageinbound_domain = 'example.com';
|
||||
}
|
||||
|
||||
public function test_get_contexts_for_userid() {
|
||||
$dg = $this->getDataGenerator();
|
||||
$u1 = $dg->create_user();
|
||||
$u2 = $dg->create_user();
|
||||
$u1ctx = context_user::instance($u1->id);
|
||||
$u2ctx = context_user::instance($u2->id);
|
||||
|
||||
$contexts = provider::get_contexts_for_userid($u1->id)->get_contexts();
|
||||
$this->assertCount(1, $contexts);
|
||||
$this->assertEquals($u1ctx->id, $contexts[0]->id);
|
||||
|
||||
$contexts = provider::get_contexts_for_userid($u2->id)->get_contexts();
|
||||
$this->assertCount(1, $contexts);
|
||||
$this->assertEquals($u2ctx->id, $contexts[0]->id);
|
||||
}
|
||||
|
||||
public function test_delete_data_for_user() {
|
||||
global $DB;
|
||||
$dg = $this->getDataGenerator();
|
||||
$u1 = $dg->create_user();
|
||||
$u2 = $dg->create_user();
|
||||
$u1ctx = context_user::instance($u1->id);
|
||||
$u2ctx = context_user::instance($u2->id);
|
||||
|
||||
$addressmanager = new \core\message\inbound\address_manager();
|
||||
$addressmanager->set_handler('\tool_messageinbound\message\inbound\invalid_recipient_handler');
|
||||
$addressmanager->set_data(123);
|
||||
|
||||
// Create a user key for both users.
|
||||
$addressmanager->generate($u1->id);
|
||||
$addressmanager->generate($u2->id);
|
||||
|
||||
// Create a messagelist for both users.
|
||||
$this->create_messagelist(['userid' => $u1->id]);
|
||||
$this->create_messagelist(['userid' => $u2->id]);
|
||||
|
||||
$this->assertTrue($DB->record_exists('user_private_key', ['userid' => $u1->id, 'script' => 'messageinbound_handler']));
|
||||
$this->assertTrue($DB->record_exists('user_private_key', ['userid' => $u2->id, 'script' => 'messageinbound_handler']));
|
||||
$this->assertTrue($DB->record_exists('messageinbound_messagelist', ['userid' => $u1->id]));
|
||||
$this->assertTrue($DB->record_exists('messageinbound_messagelist', ['userid' => $u2->id]));
|
||||
|
||||
// Passing another user's context does not do anything.
|
||||
provider::delete_data_for_user(new approved_contextlist($u1, 'tool_messageinbound', [$u2ctx->id]));
|
||||
$this->assertTrue($DB->record_exists('user_private_key', ['userid' => $u1->id, 'script' => 'messageinbound_handler']));
|
||||
$this->assertTrue($DB->record_exists('user_private_key', ['userid' => $u2->id, 'script' => 'messageinbound_handler']));
|
||||
$this->assertTrue($DB->record_exists('messageinbound_messagelist', ['userid' => $u1->id]));
|
||||
$this->assertTrue($DB->record_exists('messageinbound_messagelist', ['userid' => $u2->id]));
|
||||
|
||||
// Deleting user 1.
|
||||
provider::delete_data_for_user(new approved_contextlist($u1, 'tool_messageinbound', [$u1ctx->id]));
|
||||
$this->assertFalse($DB->record_exists('user_private_key', ['userid' => $u1->id, 'script' => 'messageinbound_handler']));
|
||||
$this->assertTrue($DB->record_exists('user_private_key', ['userid' => $u2->id, 'script' => 'messageinbound_handler']));
|
||||
$this->assertFalse($DB->record_exists('messageinbound_messagelist', ['userid' => $u1->id]));
|
||||
$this->assertTrue($DB->record_exists('messageinbound_messagelist', ['userid' => $u2->id]));
|
||||
}
|
||||
|
||||
public function test_delete_data_for_all_users_in_context() {
|
||||
global $DB;
|
||||
$dg = $this->getDataGenerator();
|
||||
$u1 = $dg->create_user();
|
||||
$u2 = $dg->create_user();
|
||||
$u1ctx = context_user::instance($u1->id);
|
||||
$u2ctx = context_user::instance($u2->id);
|
||||
|
||||
$addressmanager = new \core\message\inbound\address_manager();
|
||||
$addressmanager->set_handler('\tool_messageinbound\message\inbound\invalid_recipient_handler');
|
||||
$addressmanager->set_data(123);
|
||||
|
||||
// Create a user key for both users.
|
||||
$addressmanager->generate($u1->id);
|
||||
$addressmanager->generate($u2->id);
|
||||
|
||||
// Create a messagelist for both users.
|
||||
$this->create_messagelist(['userid' => $u1->id]);
|
||||
$this->create_messagelist(['userid' => $u2->id]);
|
||||
|
||||
$this->assertTrue($DB->record_exists('user_private_key', ['userid' => $u1->id, 'script' => 'messageinbound_handler']));
|
||||
$this->assertTrue($DB->record_exists('user_private_key', ['userid' => $u2->id, 'script' => 'messageinbound_handler']));
|
||||
$this->assertTrue($DB->record_exists('messageinbound_messagelist', ['userid' => $u1->id]));
|
||||
$this->assertTrue($DB->record_exists('messageinbound_messagelist', ['userid' => $u2->id]));
|
||||
|
||||
// Deleting user 1.
|
||||
provider::delete_data_for_all_users_in_context($u1ctx);
|
||||
$this->assertFalse($DB->record_exists('user_private_key', ['userid' => $u1->id, 'script' => 'messageinbound_handler']));
|
||||
$this->assertTrue($DB->record_exists('user_private_key', ['userid' => $u2->id, 'script' => 'messageinbound_handler']));
|
||||
$this->assertFalse($DB->record_exists('messageinbound_messagelist', ['userid' => $u1->id]));
|
||||
$this->assertTrue($DB->record_exists('messageinbound_messagelist', ['userid' => $u2->id]));
|
||||
}
|
||||
|
||||
public function test_export_data_for_user() {
|
||||
$dg = $this->getDataGenerator();
|
||||
$u1 = $dg->create_user();
|
||||
$u2 = $dg->create_user();
|
||||
$u1ctx = context_user::instance($u1->id);
|
||||
$u2ctx = context_user::instance($u2->id);
|
||||
|
||||
$addressmanager = new \core\message\inbound\address_manager();
|
||||
$addressmanager->set_handler('\tool_messageinbound\message\inbound\invalid_recipient_handler');
|
||||
$addressmanager->set_data(123);
|
||||
|
||||
// Create a user key for both users.
|
||||
$addressmanager->generate($u1->id);
|
||||
$addressmanager->generate($u2->id);
|
||||
|
||||
// Create a messagelist for both users.
|
||||
$this->create_messagelist(['userid' => $u1->id, 'address' => 'u1@example1.com']);
|
||||
$this->create_messagelist(['userid' => $u1->id, 'address' => 'u1@example2.com']);
|
||||
$this->create_messagelist(['userid' => $u2->id, 'address' => 'u2@example1.com']);
|
||||
|
||||
// Export for user.
|
||||
$this->setUser($u1);
|
||||
provider::export_user_data(new approved_contextlist($u1, 'tool_messageinbound', [$u1ctx->id, $u2ctx->id]));
|
||||
$data = writer::with_context($u2ctx)->get_data([get_string('messageinbound', 'tool_messageinbound')]);
|
||||
$this->assertEmpty($data);
|
||||
$data = writer::with_context($u1ctx)->get_data([get_string('messageinbound', 'tool_messageinbound')]);
|
||||
$this->assertCount(2, $data->messages_pending_validation);
|
||||
$this->assertEquals('u1@example1.com', $data->messages_pending_validation[0]['received_at']);
|
||||
$this->assertEquals('u1@example2.com', $data->messages_pending_validation[1]['received_at']);
|
||||
|
||||
$data = writer::with_context($u2ctx)->get_related_data([get_string('messageinbound', 'tool_messageinbound')], 'userkeys');
|
||||
$this->assertEmpty($data);
|
||||
$data = writer::with_context($u1ctx)->get_related_data([get_string('messageinbound', 'tool_messageinbound')], 'userkeys');
|
||||
$this->assertCount(1, $data->keys);
|
||||
$this->assertEquals('messageinbound_handler', $data->keys[0]->script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a message to validate.
|
||||
*
|
||||
* @param array $params The params.
|
||||
* @return stdClass
|
||||
*/
|
||||
protected function create_messagelist(array $params) {
|
||||
global $DB, $USER;
|
||||
$record = (object) array_merge([
|
||||
'messageid' => 'abc',
|
||||
'userid' => $USER->id,
|
||||
'address' => 'text@example.com',
|
||||
'timecreated' => time(),
|
||||
], $params);
|
||||
$record->id = $DB->insert_record('messageinbound_messagelist', $record);
|
||||
return $record;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -211,7 +211,9 @@ class api {
|
||||
}
|
||||
|
||||
if (empty($section) or $section == 'sitepolicies') {
|
||||
$settings->sitepolicy = $CFG->sitepolicy;
|
||||
$manager = new \core_privacy\local\sitepolicy\manager();
|
||||
$settings->sitepolicy = ($sitepolicy = $manager->get_embed_url()) ? $sitepolicy->out(false) : '';
|
||||
$settings->sitepolicyhandler = $CFG->sitepolicyhandler;
|
||||
$settings->disableuserimages = $CFG->disableuserimages;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
<?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_mobile.
|
||||
*
|
||||
* @package tool_mobile
|
||||
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_mobile\privacy;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
use \core_privacy\local\request\writer;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Privacy provider for tool_mobile.
|
||||
*
|
||||
* @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\user_preference_provider,
|
||||
\core_privacy\local\request\plugin\provider {
|
||||
/**
|
||||
* Returns meta data about this system.
|
||||
*
|
||||
* @param collection $collection The initialised item collection to add items to.
|
||||
* @return collection A listing of user data stored through this system.
|
||||
*/
|
||||
public static function get_metadata(collection $collection) {
|
||||
// There is a one user preference.
|
||||
$collection->add_user_preference('tool_mobile_autologin_request_last',
|
||||
'privacy:metadata:preference:tool_mobile_autologin_request_last');
|
||||
$collection->add_subsystem_link('core_userkey', [], 'privacy:metadata:core_userkey');
|
||||
|
||||
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) {
|
||||
$sql = "SELECT ctx.id
|
||||
FROM {user_private_key} k
|
||||
JOIN {user} u ON k.userid = u.id
|
||||
JOIN {context} ctx ON ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel
|
||||
WHERE k.userid = :userid AND k.script = 'tool_mobile'";
|
||||
$params = ['userid' => $userid, 'contextlevel' => CONTEXT_USER];
|
||||
$contextlist = new contextlist();
|
||||
$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) {
|
||||
// If the user has data, then only the CONTEXT_USER 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;
|
||||
}
|
||||
// Export associated userkeys.
|
||||
\core_userkey\privacy\provider::export_userkeys($context, [], 'tool_mobile');
|
||||
}
|
||||
/**
|
||||
* 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) {
|
||||
$autologinrequestlast = get_user_preferences('tool_mobile_autologin_request_last', null, $userid);
|
||||
if ($autologinrequestlast !== null) {
|
||||
$time = transform::datetime($autologinrequestlast);
|
||||
writer::export_user_preference('tool_mobile',
|
||||
'tool_mobile_autologin_request_last',
|
||||
$time,
|
||||
get_string('privacy:metadata:preference:tool_mobile_autologin_request_last', 'tool_mobile')
|
||||
);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Delete all use data which matches the specified deletion_criteria.
|
||||
*
|
||||
* @param context $context A user context.
|
||||
*/
|
||||
public static function delete_data_for_all_users_in_context(\context $context) {
|
||||
// Sanity check that context is at the user context level, then get the userid.
|
||||
if ($context->contextlevel !== CONTEXT_USER) {
|
||||
return;
|
||||
}
|
||||
$userid = $context->instanceid;
|
||||
// Delete all the userkeys.
|
||||
\core_userkey\privacy\provider::delete_userkeys('tool_mobile', $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) {
|
||||
// If the user has 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 all the userkeys.
|
||||
\core_userkey\privacy\provider::delete_userkeys('tool_mobile', $userid);
|
||||
}
|
||||
}
|
||||
@@ -74,4 +74,6 @@ $string['smartappbanners'] = 'App Banners';
|
||||
$string['pluginnotenabledorconfigured'] = 'Plugin not enabled or configured.';
|
||||
$string['remoteaddons'] = 'Remote add-ons';
|
||||
$string['typeoflogin'] = 'Type of login';
|
||||
$string['typeoflogin_desc'] = 'If the site uses a SSO authentication method, then select via a browser window or via an embedded browser. An embedded browser provides a better user experience, though it doesn\'t work with all SSO plugins. If using SSO, autologinguests should be disabled.';
|
||||
$string['typeoflogin_desc'] = 'If the site uses a SSO authentication method, then select via a browser window or via an embedded browser. An embedded browser provides a better user experience, though it doesn\'t work with all SSO plugins.';
|
||||
$string['privacy:metadata:preference:tool_mobile_autologin_request_last'] = 'The date of the last auto-login key request. Between each request 6 minutes are required.';
|
||||
$string['privacy:metadata:core_userkey'] = 'User\'s keys used to create auto-login key for the current user.';
|
||||
|
||||
@@ -150,6 +150,7 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
|
||||
array('name' => 'newsitems', 'value' => $SITE->newsitems),
|
||||
array('name' => 'commentsperpage', 'value' => $CFG->commentsperpage),
|
||||
array('name' => 'sitepolicy', 'value' => $mysitepolicy),
|
||||
array('name' => 'sitepolicyhandler', 'value' => ''),
|
||||
array('name' => 'disableuserimages', 'value' => $CFG->disableuserimages),
|
||||
array('name' => 'mygradesurl', 'value' => user_mygrades_url()->out(false)),
|
||||
array('name' => 'tool_mobile_forcelogout', 'value' => 0),
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
<?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/>.
|
||||
/**
|
||||
* Base class for unit tests for tool_mobile.
|
||||
*
|
||||
* @package tool_mobile
|
||||
* @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_privacy\local\request\writer;
|
||||
use \core_privacy\local\request\transform;
|
||||
use \core_privacy\local\request\approved_contextlist;
|
||||
use \tool_mobile\privacy\provider;
|
||||
|
||||
/**
|
||||
* Unit tests for the tool_mobile implementation of the privacy API.
|
||||
*
|
||||
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_mobile_privacy_testcase extends \core_privacy\tests\provider_testcase {
|
||||
|
||||
/**
|
||||
* Basic setup for these tests.
|
||||
*/
|
||||
public function setUp() {
|
||||
$this->resetAfterTest(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to check export_user_preferences.
|
||||
* returns user preferences data.
|
||||
*/
|
||||
public function test_export_user_preferences() {
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$expectedtime = time();
|
||||
set_user_preference('tool_mobile_autologin_request_last', time(), $user);
|
||||
provider::export_user_preferences($user->id);
|
||||
$writer = writer::with_context(\context_system::instance());
|
||||
$prefs = $writer->get_user_preferences('tool_mobile');
|
||||
$time = transform::datetime($expectedtime);
|
||||
$this->assertEquals($time, $prefs->tool_mobile_autologin_request_last->value);
|
||||
$this->assertEquals(get_string('privacy:metadata:preference:tool_mobile_autologin_request_last', 'tool_mobile'),
|
||||
$prefs->tool_mobile_autologin_request_last->description);
|
||||
}
|
||||
/**
|
||||
* Test getting the context for the user ID related to this plugin.
|
||||
*/
|
||||
public function test_get_contexts_for_userid() {
|
||||
// Create user and Mobile user keys.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$context = \context_user::instance($user->id);
|
||||
$key = get_user_key('tool_mobile', $user->id);
|
||||
$contextlist = provider::get_contexts_for_userid($user->id);
|
||||
$this->assertEquals($context->id, $contextlist->current()->id);
|
||||
}
|
||||
/**
|
||||
* Test that data is exported correctly for this plugin.
|
||||
*/
|
||||
public function test_export_user_data() {
|
||||
global $DB;
|
||||
// Create user and Mobile user keys.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$context = \context_user::instance($user->id);
|
||||
$keyvalue = get_user_key('tool_mobile', $user->id);
|
||||
$key = $DB->get_record('user_private_key', ['value' => $keyvalue]);
|
||||
// Validate exported data.
|
||||
$this->setUser($user);
|
||||
$writer = writer::with_context($context);
|
||||
$this->assertFalse($writer->has_any_data());
|
||||
$this->export_context_data_for_user($user->id, $context, 'tool_mobile');
|
||||
$userkeydata = $writer->get_related_data([], 'userkeys');
|
||||
$this->assertCount(1, $userkeydata->keys);
|
||||
$this->assertEquals($key->script, reset($userkeydata->keys)->script);
|
||||
}
|
||||
/**
|
||||
* Test for provider::delete_data_for_all_users_in_context().
|
||||
*/
|
||||
public function test_delete_data_for_all_users_in_context() {
|
||||
global $DB;
|
||||
// Create user and Mobile user keys.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$context = \context_user::instance($user->id);
|
||||
$keyvalue = get_user_key('tool_mobile', $user->id);
|
||||
$key = $DB->get_record('user_private_key', ['value' => $keyvalue]);
|
||||
// Before deletion, we should have 1 user_private_key.
|
||||
$count = $DB->count_records('user_private_key', ['script' => 'tool_mobile']);
|
||||
$this->assertEquals(1, $count);
|
||||
// Delete data.
|
||||
provider::delete_data_for_all_users_in_context($context);
|
||||
// After deletion, the user_private_key entries should have been deleted.
|
||||
$count = $DB->count_records('user_private_key', ['script' => 'tool_mobile']);
|
||||
$this->assertEquals(0, $count);
|
||||
}
|
||||
/**
|
||||
* Test for provider::delete_data_for_user().
|
||||
*/
|
||||
public function test_delete_data_for_user() {
|
||||
global $DB;
|
||||
// Create user and Mobile user keys.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$context = \context_user::instance($user->id);
|
||||
$keyvalue = get_user_key('tool_mobile', $user->id);
|
||||
$key = $DB->get_record('user_private_key', ['value' => $keyvalue]);
|
||||
// Before deletion, we should have 1 user_private_key.
|
||||
$count = $DB->count_records('user_private_key', ['script' => 'tool_mobile']);
|
||||
$this->assertEquals(1, $count);
|
||||
// Delete data.
|
||||
$contextlist = provider::get_contexts_for_userid($user->id);
|
||||
$approvedcontextlist = new approved_contextlist($user, 'tool_mobile', $contextlist->get_contextids());
|
||||
provider::delete_data_for_user($approvedcontextlist);
|
||||
// After deletion, the user_private_key entries should have been deleted.
|
||||
$count = $DB->count_records('user_private_key', ['script' => 'tool_mobile']);
|
||||
$this->assertEquals(0, $count);
|
||||
}
|
||||
}
|
||||
@@ -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/>.
|
||||
/**
|
||||
* Privacy class for requesting user data.
|
||||
*
|
||||
* @package tool_monitor
|
||||
* @copyright 2018 Adrian Greeve <adrian@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_monitor\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;
|
||||
use \tool_monitor\subscription_manager;
|
||||
use \tool_monitor\rule_manager;
|
||||
|
||||
/**
|
||||
* Privacy provider for tool_monitor
|
||||
*
|
||||
* @package tool_monitor
|
||||
* @copyright 2018 Adrian Greeve <adrian@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 {
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
$toolmonitorrules = [
|
||||
'description' => 'privacy:metadata:description',
|
||||
'name' => 'privacy:metadata:name',
|
||||
'userid' => 'privacy:metadata:userid',
|
||||
'plugin' => 'privacy:metadata:plugin',
|
||||
'eventname' => 'privacy:metadata:eventname',
|
||||
'template' => 'privacy:metadata:template',
|
||||
'frequency' => 'privacy:metadata:frequency',
|
||||
'timewindow' => 'privacy:metadata:timewindow',
|
||||
'timemodified' => 'privacy:metadata:timemodifiedrule',
|
||||
'timecreated' => 'privacy:metadata:timecreatedrule'
|
||||
];
|
||||
$toolmonitorsubscriptions = [
|
||||
'userid' => 'privacy:metadata:useridsub',
|
||||
'timecreated' => 'privacy:metadata:timecreatedsub',
|
||||
'lastnotificationsent' => 'privacy:metadata:lastnotificationsent',
|
||||
'inactivedate' => 'privacy:metadata:inactivedate'
|
||||
];
|
||||
// Tool monitor history doesn't look like it is used at all.
|
||||
$toolmonitorhistory = [
|
||||
'userid' => 'privacy:metadata:useridhistory',
|
||||
'timesent' => 'privacy:metadata:timesent'
|
||||
];
|
||||
$collection->add_database_table('tool_monitor_rules', $toolmonitorrules, 'privacy:metadata:rulessummary');
|
||||
$collection->add_database_table('tool_monitor_subscriptions', $toolmonitorsubscriptions,
|
||||
'privacy:metadata:subscriptionssummary');
|
||||
$collection->add_database_table('tool_monitor_history', $toolmonitorhistory, 'privacy:metadata:historysummary');
|
||||
$collection->link_subsystem('core_message', 'privacy:metadata:messagesummary');
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all contexts for this userid. In this situation the user context.
|
||||
*
|
||||
* @param int $userid The user ID.
|
||||
* @return contextlist The list of context IDs.
|
||||
*/
|
||||
public static function get_contexts_for_userid($userid) {
|
||||
$params = ['useridrules' => $userid, 'useridsubscriptions' => $userid, 'contextuserrule' => CONTEXT_USER,
|
||||
'contextusersub' => CONTEXT_USER];
|
||||
$sql = "SELECT DISTINCT ctx.id
|
||||
FROM {context} ctx
|
||||
LEFT JOIN {tool_monitor_rules} mr ON ctx.instanceid = mr.userid AND ctx.contextlevel = :contextuserrule
|
||||
AND mr.userid = :useridsubscriptions
|
||||
LEFT JOIN {tool_monitor_subscriptions} ms ON ctx.instanceid = ms.userid AND ctx.contextlevel = :contextusersub
|
||||
AND ms.userid = :useridrules
|
||||
WHERE ms.id IS NOT NULL OR mr.id IS NOT NULL";
|
||||
|
||||
$contextlist = new contextlist();
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
return $contextlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export all event monitor information for the list of contexts and this user.
|
||||
*
|
||||
* @param approved_contextlist $contextlist The list of approved contexts for a user.
|
||||
*/
|
||||
public static function export_user_data(approved_contextlist $contextlist) {
|
||||
global $DB;
|
||||
// Export rules.
|
||||
$context = \context_user::instance($contextlist->get_user()->id);
|
||||
$rules = $DB->get_records('tool_monitor_rules', ['userid' => $contextlist->get_user()->id]);
|
||||
if ($rules) {
|
||||
static::export_monitor_rules($rules, $context);
|
||||
}
|
||||
// Export subscriptions.
|
||||
$subscriptions = subscription_manager::get_user_subscriptions(0, 0, $contextlist->get_user()->id);
|
||||
if ($subscriptions) {
|
||||
static::export_monitor_subscriptions($subscriptions, $context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// Only delete data for user contexts.
|
||||
if ($context->contextlevel == CONTEXT_USER) {
|
||||
static::delete_user_data($context->instanceid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
static::delete_user_data($contextlist->get_user()->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* This does the deletion of user data for the event monitor.
|
||||
*
|
||||
* @param int $userid The user ID
|
||||
*/
|
||||
protected static function delete_user_data($userid) {
|
||||
global $DB;
|
||||
// Delete this user's subscriptions first.
|
||||
subscription_manager::delete_user_subscriptions($userid);
|
||||
// Because we only use user contexts the instance ID is the user ID.
|
||||
// Get the rules and check if this user has the capability to delete them.
|
||||
$rules = $DB->get_records('tool_monitor_rules', ['userid' => $userid]);
|
||||
foreach ($rules as $ruledata) {
|
||||
$rule = rule_manager::get_rule($ruledata);
|
||||
// If no-one is suscribed to the rule then it is safe to delete.
|
||||
if ($rule->can_manage_rule($userid) && subscription_manager::count_rule_subscriptions($rule->id) == 0) {
|
||||
$rule->delete_rule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This formats and then exports the monitor rules.
|
||||
*
|
||||
* @param array $rules The monitor rules.
|
||||
* @param context_user $context The user context
|
||||
*/
|
||||
protected static function export_monitor_rules(array $rules, \context_user $context) {
|
||||
foreach ($rules as $rule) {
|
||||
$rule = rule_manager::get_rule($rule);
|
||||
$ruledata = new \stdClass();
|
||||
$ruledata->name = $rule->name;
|
||||
$ruledata->eventname = $rule->get_event_name();
|
||||
$ruledata->description = $rule->get_description($context);
|
||||
$ruledata->plugin = $rule->get_plugin_name();
|
||||
$ruledata->template = $rule->template;
|
||||
$ruledata->frequency = $rule->get_filters_description();
|
||||
$ruledata->course = $rule->get_course_name($context);
|
||||
$ruledata->timecreated = transform::datetime($rule->timecreated);
|
||||
$ruledata->timemodified = transform::datetime($rule->timemodified);
|
||||
writer::with_context($context)->export_data([get_string('privacy:createdrules', 'tool_monitor'),
|
||||
$rule->name . '_' . $rule->id], $ruledata);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This formats and then exports the event monitor subscriptions.
|
||||
*
|
||||
* @param array $subscriptions Subscriptions
|
||||
* @param \context_user $context The user context
|
||||
*/
|
||||
protected static function export_monitor_subscriptions(array $subscriptions, \context_user $context) {
|
||||
foreach ($subscriptions as $subscription) {
|
||||
$subscriptiondata = new \stdClass();
|
||||
$subscriptiondata->instancename = $subscription->get_instance_name();
|
||||
$subscriptiondata->eventname = $subscription->get_event_name();
|
||||
$subscriptiondata->frequency = $subscription->get_filters_description();
|
||||
$subscriptiondata->name = $subscription->get_name($context);
|
||||
$subscriptiondata->description = $subscription->get_description($context);
|
||||
$subscriptiondata->pluginname = $subscription->get_plugin_name();
|
||||
$subscriptiondata->course = $subscription->get_course_name($context);
|
||||
$subscriptiondata->timecreated = transform::datetime($subscription->timecreated);
|
||||
$subscriptiondata->lastnotificationsent = transform::datetime($subscription->lastnotificationsent);
|
||||
writer::with_context($context)->export_data([get_string('privacy:subscriptions', 'tool_monitor'),
|
||||
$subscriptiondata->name . '_' . $subscription->id, $subscriptiondata->course, $subscriptiondata->instancename],
|
||||
$subscriptiondata);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,14 +51,15 @@ class rule {
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the current user manage this rule?
|
||||
* Can the user manage this rule? Defaults to $USER.
|
||||
*
|
||||
* @param int $userid Check against this userid.
|
||||
* @return bool true if the current user can manage this rule, else false.
|
||||
*/
|
||||
public function can_manage_rule() {
|
||||
public function can_manage_rule($userid = null) {
|
||||
$courseid = $this->courseid;
|
||||
$context = empty($courseid) ? \context_system::instance() : \context_course::instance($this->courseid);
|
||||
return has_capability('tool/monitor:managerules', $context);
|
||||
return has_capability('tool/monitor:managerules', $context, $userid);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<XMLDB PATH="tool/monitor/db" VERSION="20141103" COMMENT="XMLDB file for Moodle tool/monitor"
|
||||
<XMLDB PATH="admin/tool/monitor/db" VERSION="20141103" COMMENT="XMLDB file for Moodle tool/monitor"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
|
||||
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
|
||||
>
|
||||
<TABLES>
|
||||
<TABLE NAME="tool_monitor_rules" COMMENT="Table to store rules">
|
||||
@@ -79,4 +79,4 @@
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
</TABLES>
|
||||
</XMLDB>
|
||||
</XMLDB>
|
||||
|
||||
@@ -79,6 +79,28 @@ $string['monitor:managetool'] = 'Enable/disable event monitoring';
|
||||
$string['monitor:subscribe'] = 'Subscribe to event monitor rules';
|
||||
$string['norules'] = 'There are no event monitoring rules.';
|
||||
$string['pluginname'] = 'Event monitor';
|
||||
$string['privacy:createdrules'] = 'Event monitor rules I created';
|
||||
$string['privacy:metadata:description'] = 'Description of the rule';
|
||||
$string['privacy:metadata:eventname'] = 'Fully qualified name of the event';
|
||||
$string['privacy:metadata:frequency'] = 'Frequency of notifications';
|
||||
$string['privacy:metadata:historysummary'] = 'Stores the history of the message notifications sent';
|
||||
$string['privacy:metadata:inactivedate'] = 'Period of time, in days, after which an inactive subscription will be removed completely';
|
||||
$string['privacy:metadata:lastnotificationsent'] = 'When a notification was last sent for this subscription.';
|
||||
$string['privacy:metadata:messagesummary'] = 'Notifications are sent to the message system.';
|
||||
$string['privacy:metadata:name'] = 'Name of the rule';
|
||||
$string['privacy:metadata:plugin'] = 'Frankenstlye name of the plugin';
|
||||
$string['privacy:metadata:rulessummary'] = 'This stores monitor rules.';
|
||||
$string['privacy:metadata:subscriptionssummary'] = 'Stores user subscriptions to various rules';
|
||||
$string['privacy:metadata:template'] = 'Message template';
|
||||
$string['privacy:metadata:timecreatedrule'] = 'When this rule was created';
|
||||
$string['privacy:metadata:timecreatedsub'] = 'When this subscription was created';
|
||||
$string['privacy:metadata:timemodifiedrule'] = 'When this rule was last modified';
|
||||
$string['privacy:metadata:timesent'] = 'When the message was sent';
|
||||
$string['privacy:metadata:timewindow'] = 'Time window in seconds';
|
||||
$string['privacy:metadata:userid'] = 'The ID of user who created the rule.';
|
||||
$string['privacy:metadata:useridhistory'] = 'The ID of the user to whom this notification was sent';
|
||||
$string['privacy:metadata:useridsub'] = 'The ID of the subscriber.';
|
||||
$string['privacy:subscriptions'] = 'My event monitor subscriptions';
|
||||
$string['processevents'] = 'Process events';
|
||||
$string['rulename'] = 'Rule name';
|
||||
$string['ruleareyousure'] = 'Are you sure you want to delete the rule "{$a}"?';
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
<?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 the event monitor
|
||||
*
|
||||
* @package tool_monitor
|
||||
* @category test
|
||||
* @copyright 2018 Adrian Greeve <adriangreeve.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use \tool_monitor\privacy\provider;
|
||||
use \core_privacy\local\request\approved_contextlist;
|
||||
|
||||
/**
|
||||
* Privacy test for the event monitor
|
||||
*
|
||||
* @package tool_monitor
|
||||
* @category test
|
||||
* @copyright 2018 Adrian Greeve <adriangreeve.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_monitor_privacy_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Set up method.
|
||||
*/
|
||||
public function setUp() {
|
||||
$this->resetAfterTest();
|
||||
// Enable monitor.
|
||||
set_config('enablemonitor', 1, 'tool_monitor');
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a capability to $USER
|
||||
* The function creates a student $USER if $USER->id is empty
|
||||
*
|
||||
* @param string $capability capability name
|
||||
* @param int $contextid
|
||||
* @param int $roleid
|
||||
* @return int the role id - mainly returned for creation, so calling function can reuse it
|
||||
*/
|
||||
public static function assign_user_capability($capability, $contextid, $roleid = null) {
|
||||
global $USER;
|
||||
|
||||
// Create a new student $USER if $USER doesn't exist.
|
||||
if (empty($USER->id)) {
|
||||
$user = self::getDataGenerator()->create_user();
|
||||
self::setUser($user);
|
||||
}
|
||||
|
||||
if (empty($roleid)) {
|
||||
$roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
|
||||
}
|
||||
|
||||
assign_capability($capability, CAP_ALLOW, $roleid, $contextid);
|
||||
|
||||
role_assign($roleid, $USER->id, $contextid);
|
||||
|
||||
accesslib_clear_all_caches_for_unit_testing();
|
||||
|
||||
return $roleid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a collection with data is returned when calling this function.
|
||||
*/
|
||||
public function test_get_metadata() {
|
||||
$collection = new \core_privacy\local\metadata\collection('tool_monitor');
|
||||
$collection = provider::get_metadata($collection);
|
||||
$this->assertNotEmpty($collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a user context is returned if there is any user data for this user.
|
||||
*/
|
||||
public function test_get_contexts_for_userid() {
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$usercontext = \context_user::instance($user->id);
|
||||
$usercontext2 = \context_user::instance($user2->id);
|
||||
$this->assertEmpty(provider::get_contexts_for_userid($user->id));
|
||||
$this->assertEmpty(provider::get_contexts_for_userid($user2->id));
|
||||
|
||||
$monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
|
||||
|
||||
// Create a rule with this user.
|
||||
$this->setUser($user);
|
||||
$rule = $monitorgenerator->create_rule();
|
||||
$contextlist = provider::get_contexts_for_userid($user->id);
|
||||
|
||||
// Check that we only get back one context.
|
||||
$this->assertCount(1, $contextlist);
|
||||
|
||||
// Check that a context is returned for just creating a rule.
|
||||
$this->assertEquals($usercontext->id, $contextlist->get_contextids()[0]);
|
||||
|
||||
$this->setUser($user2);
|
||||
|
||||
$record = new stdClass();
|
||||
$record->courseid = 0;
|
||||
$record->userid = $user2->id;
|
||||
$record->ruleid = $rule->id;
|
||||
|
||||
$subscription = $monitorgenerator->create_subscription($record);
|
||||
$contextlist = provider::get_contexts_for_userid($user2->id);
|
||||
|
||||
// Check that we only get back one context.
|
||||
$this->assertCount(1, $contextlist);
|
||||
|
||||
// Check that a context is returned for just subscribing to a rule.
|
||||
$this->assertEquals($usercontext2->id, $contextlist->get_contextids()[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that user data is exported correctly.
|
||||
*/
|
||||
public function test_export_user_data() {
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$usercontext = \context_user::instance($user->id);
|
||||
$monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
|
||||
|
||||
$this->setUser($user);
|
||||
$rulerecord = (object)['name' => 'privacy rule'];
|
||||
$rule = $monitorgenerator->create_rule($rulerecord);
|
||||
|
||||
$secondrulerecord = (object)['name' => 'privacy rule2'];
|
||||
$rule2 = $monitorgenerator->create_rule($secondrulerecord);
|
||||
|
||||
$subscription = (object)['ruleid' => $rule->id, 'userid' => $user->id];
|
||||
$subscription = $monitorgenerator->create_subscription($subscription);
|
||||
|
||||
$writer = \core_privacy\local\request\writer::with_context($usercontext);
|
||||
$this->assertFalse($writer->has_any_data());
|
||||
|
||||
$approvedlist = new approved_contextlist($user, 'tool_monitor', [$usercontext->id]);
|
||||
provider::export_user_data($approvedlist);
|
||||
|
||||
// Check that the rules created by this user are exported.
|
||||
$this->assertEquals($rulerecord->name, $writer->get_data([get_string('privacy:createdrules', 'tool_monitor'),
|
||||
$rulerecord->name . '_' . $rule->id])->name);
|
||||
$this->assertEquals($secondrulerecord->name, $writer->get_data([get_string('privacy:createdrules', 'tool_monitor'),
|
||||
$secondrulerecord->name . '_' . $rule2->id])->name);
|
||||
|
||||
// Check that the subscriptions for this user are also exported.
|
||||
$this->assertEquals($rulerecord->name, $writer->get_data([get_string('privacy:subscriptions', 'tool_monitor'),
|
||||
$rulerecord->name . '_' . $subscription->id, 'Site' , 'All events'])->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test deleting all user data for a specific context.
|
||||
*/
|
||||
public function test_delete_data_for_all_users_in_context() {
|
||||
global $DB;
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$usercontext = \context_user::instance($user->id);
|
||||
$usercontext2 = \context_user::instance($user2->id);
|
||||
$monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
|
||||
|
||||
$this->setUser($user);
|
||||
// Need to give user one the ability to manage rules.
|
||||
$this->assign_user_capability('tool/monitor:managerules', \context_system::instance());
|
||||
|
||||
$rulerecord = (object)['name' => 'privacy rule'];
|
||||
$rule = $monitorgenerator->create_rule($rulerecord);
|
||||
|
||||
$secondrulerecord = (object)['name' => 'privacy rule2'];
|
||||
$rule2 = $monitorgenerator->create_rule($secondrulerecord);
|
||||
|
||||
$subscription = (object)['ruleid' => $rule->id, 'userid' => $user->id];
|
||||
$subscription = $monitorgenerator->create_subscription($subscription);
|
||||
|
||||
// Have user 2 subscribe to the second rule created by user 1.
|
||||
$subscription2 = (object)['ruleid' => $rule2->id, 'userid' => $user2->id];
|
||||
$subscription2 = $monitorgenerator->create_subscription($subscription2);
|
||||
|
||||
$this->setUser($user2);
|
||||
$thirdrulerecord = (object)['name' => 'privacy rule for second user'];
|
||||
$rule3 = $monitorgenerator->create_rule($thirdrulerecord);
|
||||
|
||||
$subscription3 = (object)['ruleid' => $rule3->id, 'userid' => $user2->id];
|
||||
$subscription3 = $monitorgenerator->create_subscription($subscription3);
|
||||
|
||||
// Try a different context first.
|
||||
provider::delete_data_for_all_users_in_context(context_system::instance());
|
||||
|
||||
// Get all of the monitor rules.
|
||||
$dbrules = $DB->get_records('tool_monitor_rules');
|
||||
|
||||
// All of the rules should still be present.
|
||||
$this->assertCount(3, $dbrules);
|
||||
$this->assertEquals($user->id, $dbrules[$rule->id]->userid);
|
||||
$this->assertEquals($user->id, $dbrules[$rule2->id]->userid);
|
||||
$this->assertEquals($user2->id, $dbrules[$rule3->id]->userid);
|
||||
|
||||
// Delete everything for the first user context.
|
||||
provider::delete_data_for_all_users_in_context($usercontext);
|
||||
|
||||
// Get all of the monitor rules.
|
||||
$dbrules = $DB->get_records('tool_monitor_rules');
|
||||
|
||||
// Only the rules for user 1 that does not have any more subscriptions should be deleted (the first rule).
|
||||
$this->assertCount(2, $dbrules);
|
||||
$this->assertEquals($user->id, $dbrules[$rule2->id]->userid);
|
||||
$this->assertEquals($user2->id, $dbrules[$rule3->id]->userid);
|
||||
|
||||
// Get all of the monitor subscriptions.
|
||||
$dbsubs = $DB->get_records('tool_monitor_subscriptions');
|
||||
// There should be two subscriptions left, both for user 2.
|
||||
$this->assertCount(2, $dbsubs);
|
||||
$this->assertEquals($user2->id, $dbsubs[$subscription2->id]->userid);
|
||||
$this->assertEquals($user2->id, $dbsubs[$subscription3->id]->userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* This should work identical to the above test.
|
||||
*/
|
||||
public function test_delete_data_for_user() {
|
||||
global $DB;
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$usercontext = \context_user::instance($user->id);
|
||||
$usercontext2 = \context_user::instance($user2->id);
|
||||
$monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
|
||||
|
||||
$this->setUser($user);
|
||||
// Need to give user one the ability to manage rules.
|
||||
$this->assign_user_capability('tool/monitor:managerules', \context_system::instance());
|
||||
|
||||
$rulerecord = (object)['name' => 'privacy rule'];
|
||||
$rule = $monitorgenerator->create_rule($rulerecord);
|
||||
|
||||
$secondrulerecord = (object)['name' => 'privacy rule2'];
|
||||
$rule2 = $monitorgenerator->create_rule($secondrulerecord);
|
||||
|
||||
$subscription = (object)['ruleid' => $rule->id, 'userid' => $user->id];
|
||||
$subscription = $monitorgenerator->create_subscription($subscription);
|
||||
|
||||
// Have user 2 subscribe to the second rule created by user 1.
|
||||
$subscription2 = (object)['ruleid' => $rule2->id, 'userid' => $user2->id];
|
||||
$subscription2 = $monitorgenerator->create_subscription($subscription2);
|
||||
|
||||
$this->setUser($user2);
|
||||
$thirdrulerecord = (object)['name' => 'privacy rule for second user'];
|
||||
$rule3 = $monitorgenerator->create_rule($thirdrulerecord);
|
||||
|
||||
$subscription3 = (object)['ruleid' => $rule3->id, 'userid' => $user2->id];
|
||||
$subscription3 = $monitorgenerator->create_subscription($subscription3);
|
||||
|
||||
$approvedlist = new approved_contextlist($user, 'tool_monitor', [$usercontext->id]);
|
||||
|
||||
// Delete everything for the first user.
|
||||
provider::delete_data_for_user($approvedlist);
|
||||
|
||||
// Get all of the monitor rules.
|
||||
$dbrules = $DB->get_records('tool_monitor_rules');
|
||||
|
||||
// Only the rules for user 1 that does not have any more subscriptions should be deleted (the first rule).
|
||||
$this->assertCount(2, $dbrules);
|
||||
$this->assertEquals($user->id, $dbrules[$rule2->id]->userid);
|
||||
$this->assertEquals($user2->id, $dbrules[$rule3->id]->userid);
|
||||
|
||||
// Get all of the monitor subscriptions.
|
||||
$dbsubs = $DB->get_records('tool_monitor_subscriptions');
|
||||
// There should be two subscriptions left, both for user 2.
|
||||
$this->assertCount(2, $dbsubs);
|
||||
$this->assertEquals($user2->id, $dbsubs[$subscription2->id]->userid);
|
||||
$this->assertEquals($user2->id, $dbsubs[$subscription3->id]->userid);
|
||||
}
|
||||
}
|
||||
@@ -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_multilangupgrade.
|
||||
*
|
||||
* @package tool_multilangupgrade
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_multilangupgrade\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_multilangupgrade 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';
|
||||
}
|
||||
}
|
||||
@@ -27,3 +27,4 @@ $string['multilangupgradeinfo'] = '<p>The multilang filter syntax was changed in
|
||||
<p>Example: <span lang="en" class="multilang">Hello!</span><span lang="es" class="multilang">Hola!</span></p>
|
||||
<p>Do you want to upgrade the syntax in all existing texts now?</p>';
|
||||
$string['pluginname'] = 'Multilang upgrade';
|
||||
$string['privacy:metadata'] = 'The Multilang upgrade 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_oauth2.
|
||||
*
|
||||
* @package tool_oauth2
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_oauth2\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_oauth2 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';
|
||||
}
|
||||
}
|
||||
@@ -96,8 +96,10 @@ $string['systemauthstatus'] = 'System account connected';
|
||||
$string['usebasicauth'] = 'Authenticate token requests via HTTP headers';
|
||||
$string['usebasicauth_help'] = 'Utilise the HTTP Basic authentication scheme when sending client ID and password with a refresh token request. Recommended by the OAuth 2 standard, but may not be available with some issuers.';
|
||||
$string['userfieldexternalfield'] = 'External field name';
|
||||
$string['userfieldexternalfield_error'] = 'This field cannot contain HTML.';
|
||||
$string['userfieldexternalfield_help'] = 'Name of the field provided by the external OAuth system.';
|
||||
$string['userfieldinternalfield_help'] = 'Name of the Moodle user field that should be mapped from the external field.';
|
||||
$string['userfieldinternalfield'] = 'Internal field name';
|
||||
$string['userfieldmappingdeleted'] = 'User field mapping deleted';
|
||||
$string['userfieldmappingsforissuer'] = 'User field mappings for issuer: {$a}';
|
||||
$string['privacy:metadata'] = 'The OAuth 2 services 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_phpunit.
|
||||
*
|
||||
* @package tool_phpunit
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_phpunit\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_phpunit 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';
|
||||
}
|
||||
}
|
||||
@@ -23,3 +23,4 @@
|
||||
*/
|
||||
|
||||
$string['pluginname'] = 'PHPUnit tests';
|
||||
$string['privacy:metadata'] = 'The PHPUnit tests 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_profiling.
|
||||
*
|
||||
* @package tool_profiling
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_profiling\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_profiling 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';
|
||||
}
|
||||
}
|
||||
@@ -53,3 +53,4 @@ $string['summaryof'] = 'Summary of {$a}';
|
||||
$string['viewdetails'] = 'View profiling details';
|
||||
$string['viewdiff'] = 'View profiling differences with:';
|
||||
$string['viewdiffdetails'] = 'View profiling diff details';
|
||||
$string['privacy:metadata'] = 'The Profiling runs plugin does not store any personal data.';
|
||||
|
||||
@@ -25,8 +25,12 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
// profiling tool, added to development
|
||||
if ((extension_loaded('xhprof') || extension_loaded('tideways')) && (!empty($CFG->profilingenabled) || !empty($CFG->earlyprofilingenabled))) {
|
||||
// Profiling tool, added to development.
|
||||
$hasextension = extension_loaded('tideways_xhprof');
|
||||
$hasextension = $hasextension || extension_loaded('tideways');
|
||||
$hasextension = $hasextension || extension_loaded('xhprof');
|
||||
$isenabled = !empty($CFG->profilingenabled) || !empty($CFG->earlyprofilingenabled);
|
||||
if ($hasextension && $isenabled) {
|
||||
$ADMIN->add('development', new admin_externalpage('toolprofiling', get_string('pluginname', 'tool_profiling'),
|
||||
"$CFG->wwwroot/$CFG->admin/tool/profiling/index.php", 'moodle/site:config'));
|
||||
}
|
||||
|
||||
@@ -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_recyclebin.
|
||||
*
|
||||
* @package tool_recyclebin
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_recyclebin\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_recyclebin 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';
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<XMLDB PATH="tool/recyclebin/db" VERSION="20160315" COMMENT="XMLDB file for Moodle tool/recyclebin"
|
||||
<XMLDB PATH="admin/tool/recyclebin/db" VERSION="20160315" COMMENT="XMLDB file for Moodle tool/recyclebin"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
|
||||
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
|
||||
>
|
||||
<TABLES>
|
||||
<TABLE NAME="tool_recyclebin_course" COMMENT="A list of items in the course recycle bin">
|
||||
@@ -38,4 +38,4 @@
|
||||
</INDEXES>
|
||||
</TABLE>
|
||||
</TABLES>
|
||||
</XMLDB>
|
||||
</XMLDB>
|
||||
|
||||
@@ -55,3 +55,4 @@ $string['taskcleanupcoursebin'] = 'Cleanup course recycle bin';
|
||||
$string['recyclebin:deleteitems'] = 'Delete recycle bin items';
|
||||
$string['recyclebin:restoreitems'] = 'Restore recycle bin items';
|
||||
$string['recyclebin:viewitems'] = 'View recycle bin items';
|
||||
$string['privacy:metadata'] = 'The Recycle bin 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_replace.
|
||||
*
|
||||
* @package tool_replace
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_replace\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_replace 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';
|
||||
}
|
||||
}
|
||||
@@ -39,3 +39,4 @@ $string['replacewithhelp'] = 'usually new server URL';
|
||||
$string['searchwholedb'] = 'Search whole database for';
|
||||
$string['searchwholedbhelp'] = 'usually previous server URL';
|
||||
$string['shortenoversized'] = 'Shorten result if necessary';
|
||||
$string['privacy:metadata'] = 'The DB search and replace 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_spamcleaner.
|
||||
*
|
||||
* @package tool_spamcleaner
|
||||
* @copyright 2018 Zig Tan <zig@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_spamcleaner\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for tool_spamcleaner 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';
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user