Compare commits
749 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c1d466b911 | |||
| 13561f5881 | |||
| a31041ef7b | |||
| 098013a01d | |||
| ec63b8f289 | |||
| b3cea552a2 | |||
| 9777b29452 | |||
| 081dc58c6f | |||
| a650045e23 | |||
| 1396072aa3 | |||
| 8864cfdcb8 | |||
| 727cedf0be | |||
| d45bc875af | |||
| d09d4730d9 | |||
| 57fcc42a49 | |||
| be680afe3e | |||
| e1d69f2347 | |||
| c0a69e036e | |||
| aca6d79b91 | |||
| a2c405ba5f | |||
| f200548361 | |||
| 1ff829a4f0 | |||
| 607cf7fff1 | |||
| f714dbee87 | |||
| 72ba0759fc | |||
| 0ada05c99f | |||
| a5c0090669 | |||
| 31d7689f7f | |||
| 86a5027502 | |||
| 36002cf5e7 | |||
| 778a1c3546 | |||
| 19666984de | |||
| 268b9dadfd | |||
| e3b1909b2b | |||
| da1346752d | |||
| 50bbd580ac | |||
| 6367ed9300 | |||
| 8684982cfe | |||
| 59a80f598b | |||
| 181f00f36e | |||
| 38aaac7af6 | |||
| fb7ef63350 | |||
| 2d51edb610 | |||
| 24e196b63d | |||
| 944e9d5fdc | |||
| b28424638c | |||
| d580889967 | |||
| d559cde424 | |||
| fafeeda7e1 | |||
| ef70a58aed | |||
| d82ea0f211 | |||
| 96cf22c819 | |||
| 833b48fc75 | |||
| a1b390bd4f | |||
| cc9b202deb | |||
| f2e657fc42 | |||
| d94e170007 | |||
| d430695693 | |||
| 36030e92d7 | |||
| 0d0cdaeecf | |||
| dc9b44f531 | |||
| 2243d1b395 | |||
| c0a2c4bfd8 | |||
| 49a95e4cef | |||
| d1d01d2cdb | |||
| cef371bcac | |||
| 60dafdf7c1 | |||
| 9e351777b1 | |||
| 0620ca406c | |||
| cc2edf3b8c | |||
| 7376b6deca | |||
| 7052d36f7c | |||
| 04479a6a3c | |||
| 3521b24e51 | |||
| bdfa441f00 | |||
| 6728d1ccc0 | |||
| 40d1da51bb | |||
| c52869afbe | |||
| 18d4531ad4 | |||
| 62aab40860 | |||
| b4525ba984 | |||
| 7b16b52ffe | |||
| ff93557b40 | |||
| 48e8e6f434 | |||
| ecc5f367ef | |||
| 850b4a5f56 | |||
| 91eae783fc | |||
| 912817f097 | |||
| 36412a5fea | |||
| a7f19c9072 | |||
| 4bc773ad03 | |||
| cb05767ce1 | |||
| 4973da2768 | |||
| aa145e2669 | |||
| bdc4367460 | |||
| fc467246b4 | |||
| ee502bf6b5 | |||
| ee6e605770 | |||
| 2d953eb291 | |||
| 6e7faf5854 | |||
| 50569e6f4a | |||
| 23a61db0f0 | |||
| d3d580671a | |||
| 8304649791 | |||
| c8c3b17cd2 | |||
| 3e5569a61a | |||
| 80782f8907 | |||
| 51af50ade5 | |||
| b683a38423 | |||
| 54dc1112cb | |||
| 421c0608e8 | |||
| 7678c3f41c | |||
| a3573087d1 | |||
| f809e209e3 | |||
| 59a377abbe | |||
| ac7cecfb37 | |||
| 83a24b0e9f | |||
| 7ffbd04d35 | |||
| 19284518d1 | |||
| cb8578e93f | |||
| 217f5d8f8d | |||
| dec2143d73 | |||
| abd4203547 | |||
| 28922ba889 | |||
| b67fb190b9 | |||
| 1e0f46a125 | |||
| 1abeac9ba4 | |||
| 84c134f7c0 | |||
| aa8f7a6a39 | |||
| 2937e90bc7 | |||
| 01971bc9a4 | |||
| 09fae09cd5 | |||
| e0381615aa | |||
| 349916ae5f | |||
| 5168d9c97b | |||
| fc38c5c7e7 | |||
| 3ab8986eb0 | |||
| be4d15a856 | |||
| 495f38ff3f | |||
| 0fe19ee1dc | |||
| ef3d7a9d18 | |||
| 1970d836c6 | |||
| 96f76208d1 | |||
| 2fb409a999 | |||
| 86afac09e9 | |||
| 45fbccf625 | |||
| 2c2cda1660 | |||
| e6176d6dba | |||
| 0be064b328 | |||
| 5b68c82eb2 | |||
| 94bcaa0d4f | |||
| 971b7b6eb7 | |||
| ad698059ae | |||
| 1114ab3822 | |||
| adf336cbf2 | |||
| b150085bb1 | |||
| f0dd2ae1f5 | |||
| fd63451ecc | |||
| 3d40c9bc47 | |||
| b540ab944c | |||
| d34d87f45a | |||
| abd665d545 | |||
| 5bcdefb516 | |||
| d69212b895 | |||
| 0223b0fefb | |||
| 3452312c8c | |||
| e884c04350 | |||
| a1dcb85347 | |||
| ed179a8d7f | |||
| 30bae49637 | |||
| 0ae247b51f | |||
| d8a1a67fd7 | |||
| acbfa5e1a3 | |||
| 408154a1b6 | |||
| f91d479407 | |||
| 09b9c2a5b9 | |||
| 8d12c2a15e | |||
| a358040701 | |||
| a03674de6f | |||
| b3dbeeecf7 | |||
| e18333d842 | |||
| 67b21bbc99 | |||
| d81770cdbe | |||
| 688ee9e9d1 | |||
| 42cc612a5e | |||
| fa8b74b5eb | |||
| ffc32f80a5 | |||
| 3da6a04461 | |||
| eac19ef151 | |||
| 37a95f12e7 | |||
| 727c4c5a6e | |||
| a52f92a82c | |||
| 067818c2e6 | |||
| d7d19fe136 | |||
| 28a7d39b2a | |||
| da95f8d083 | |||
| b1297a80df | |||
| cbafc55078 | |||
| b7bd55bc82 | |||
| aafb937adf | |||
| 95d011dcf1 | |||
| c930111e17 | |||
| f0c5d668e9 | |||
| a061f1b4c2 | |||
| 2299b73307 | |||
| 85c7dcaf05 | |||
| 8d30ff7575 | |||
| 2e8bf841c6 | |||
| d5b719f6a0 | |||
| d7cb2a66cd | |||
| e96db7beec | |||
| 44801c448c | |||
| 904222b6f2 | |||
| f1bc96a8ab | |||
| d0ce8e8d62 | |||
| c2f3e98cea | |||
| 266c8b73b0 | |||
| 00ce502c67 | |||
| 38ab94e04b | |||
| dc6db54e99 | |||
| d45422ccbf | |||
| c1b4d24095 | |||
| e05fa63684 | |||
| 03f840b4f2 | |||
| fbd674e81b | |||
| 1291503846 | |||
| 21fc6a3016 | |||
| 23f0ec2766 | |||
| b99a81af8e | |||
| 908488e472 | |||
| 7fdb644bd1 | |||
| 70204fd73c | |||
| 389e4fc011 | |||
| ed4a47302e | |||
| 0f0710a249 | |||
| 42511349ae | |||
| 32f1542b5e | |||
| 78bcf355c6 | |||
| 31e0fd81a7 | |||
| 7a21be49f9 | |||
| 43b3bda115 | |||
| 088c523df0 | |||
| 4af5185587 | |||
| b2018b2fbc | |||
| 25f4874743 | |||
| 3ff165b4c5 | |||
| 1d51af0f8e | |||
| aa550ddea6 | |||
| 978ada1047 | |||
| 8b7270f9b1 | |||
| 8741181a5b | |||
| e7065ad471 | |||
| d36cf65ef3 | |||
| 39d46917bb | |||
| 6b3d8fd889 | |||
| b15b73848a | |||
| 6cd08cc382 | |||
| b1a6791aa2 | |||
| 384300011c | |||
| 57fc94747d | |||
| 9956e9ff31 | |||
| 8601c56964 | |||
| 9f1ab61160 | |||
| 4197e50fec | |||
| 23ba4d1792 | |||
| 17e5aaa5be | |||
| dd998e8d88 | |||
| 1bfd61c0fd | |||
| 95274afe55 | |||
| 8d1bf9d92d | |||
| 799e5c9ecc | |||
| 1d20be4c3d | |||
| ed5fde9c1f | |||
| 45d839a7c8 | |||
| 0ebbf88990 | |||
| 2196813e1e | |||
| 562b9775b2 | |||
| cbe78bb3a6 | |||
| 8cfc9387f2 | |||
| 0ea950e710 | |||
| 077f535912 | |||
| e1816e9af1 | |||
| 5ca387bef3 | |||
| 27cd5596e7 | |||
| a10c074f54 | |||
| ba80f884f7 | |||
| 54ec6416b4 | |||
| 83bec3bdb5 | |||
| 8a325684d0 | |||
| 8704a69aed | |||
| d6fbf5661a | |||
| 6a33cb3e08 | |||
| cd2d47dafc | |||
| 1be7820208 | |||
| f297094295 | |||
| 0aeff04c0a | |||
| 959963e1e4 | |||
| 7acefc469c | |||
| 3ae349b86e | |||
| 3413c90bd2 | |||
| 41a5833327 | |||
| 9557a52569 | |||
| f41181c894 | |||
| ff3fb00a96 | |||
| 7f45da44ed | |||
| d9a761f6ee | |||
| 771694018e | |||
| eafd1bcb17 | |||
| 7065a3dca0 | |||
| 5bc2479793 | |||
| 70d257fc5b | |||
| 3423dd2245 | |||
| a0a2015027 | |||
| 96f2686d47 | |||
| 5bbd628f49 | |||
| aad75c1072 | |||
| c35610502f | |||
| f35b8e6651 | |||
| 5be8cf7ba8 | |||
| b3b8f520e9 | |||
| a25eb4cb95 | |||
| f4f7f83ce5 | |||
| f312621f0d | |||
| 6ad723885c | |||
| 2119c703d2 | |||
| 42938d2dd4 | |||
| 462357ebf0 | |||
| a79853672e | |||
| 75b3607fc3 | |||
| f142770c37 | |||
| cf92d64a57 | |||
| 6771eb805b | |||
| 6c7121254d | |||
| f523a01673 | |||
| 75791eabea | |||
| 2c804c3ab2 | |||
| 044e924d8a | |||
| 6b01e331e6 | |||
| 87d7ac1f81 | |||
| 09c7a41d7f | |||
| 219d5bfb44 | |||
| 32d08ee47f | |||
| ef45ffc444 | |||
| 52cbcdbca7 | |||
| 97c21add9e | |||
| 15d331c058 | |||
| 2ac781b76d | |||
| 6d4ea3f3c2 | |||
| b0b17142c7 | |||
| 306b5289a7 | |||
| 99ae088704 | |||
| 7605966155 | |||
| 0f7d291b9c | |||
| ff2164fd3d | |||
| b1072fc937 | |||
| 81900583fe | |||
| 9dde27f0bb | |||
| 40ffccc33b | |||
| 1975be2909 | |||
| 160617d2d5 | |||
| 2b42e986e3 | |||
| e9683d416f | |||
| 85c8395e52 | |||
| fa80a0b8a5 | |||
| 10eead2473 | |||
| f341c774de | |||
| 731bf1cb78 | |||
| 18bf9704e7 | |||
| cd781ab1b7 | |||
| 7ec026173e | |||
| ce6b2f77c9 | |||
| d3078a3f53 | |||
| d5be1b502f | |||
| a74b702a23 | |||
| 4246ca2fbf | |||
| 931f11899a | |||
| f3ade2b67a | |||
| a1334d14d7 | |||
| c2cd21be15 | |||
| 3fb33d314e | |||
| e075cb4c1d | |||
| 7cb072b11e | |||
| d84efee3e2 | |||
| 5fbdb37d60 | |||
| f8eecf37d9 | |||
| 4bdf820437 | |||
| d59f7a04f0 | |||
| 1d753e37af | |||
| 77c9660195 | |||
| 877e585704 | |||
| f64d94dd62 | |||
| f6f6475b8e | |||
| e877329be1 | |||
| 26824c239e | |||
| 6aa18b9c05 | |||
| d348237022 | |||
| 114ddf489e | |||
| f78c93e325 | |||
| 941c866216 | |||
| d9ba8ea3f3 | |||
| 6e41020e44 | |||
| 2be9aaf311 | |||
| ac65869391 | |||
| f7184f748f | |||
| 40995c1030 | |||
| 202c58888a | |||
| 6d35be2af2 | |||
| 754768bad3 | |||
| fbed8034eb | |||
| 73713bd675 | |||
| 347ade6e68 | |||
| ece31e87a6 | |||
| 7b3d547f4a | |||
| 89d93d2632 | |||
| d1f4ca4505 | |||
| f5a2ddef9f | |||
| 533deb42af | |||
| 15b422f3a7 | |||
| 17f080683d | |||
| 9874249ad1 | |||
| 7fae6efbd8 | |||
| e4a8fd37f0 | |||
| c8bdae260b | |||
| 200eb30225 | |||
| d3890caed1 | |||
| 3f1f6192ea | |||
| bf51502049 | |||
| 0d1cbfb3b7 | |||
| 6d717d9c8a | |||
| 4d4efaa72c | |||
| b2ce12db1d | |||
| 96c159f032 | |||
| c260c622b9 | |||
| 71f5eee793 | |||
| 2a86638a15 | |||
| 091b458c52 | |||
| 1093256560 | |||
| 7b6070003a | |||
| d376d8cdbf | |||
| 5541cd1af9 | |||
| dd345ab8ee | |||
| 90c5adee7c | |||
| d5395c4f47 | |||
| 84a3e73aa0 | |||
| c0a966d944 | |||
| 844247aecf | |||
| 90f138fced | |||
| 6a46e9524b | |||
| 21b4aac782 | |||
| 83b9f0e29d | |||
| 6d4a0cbcb2 | |||
| 06b9cbdefd | |||
| 2b4769bb73 | |||
| e858c2e747 | |||
| 222fb603c0 | |||
| f2dbbfe66e | |||
| 821f2a390c | |||
| ac692ebbf6 | |||
| 40960e49bc | |||
| db07c09afc | |||
| 70c8bfa038 | |||
| ccca8adbc1 | |||
| 7e01d53de3 | |||
| 3317a05ec9 | |||
| b0dddbb02a | |||
| 322dae8b9c | |||
| f6be7e2a3e | |||
| aca5d1d5c6 | |||
| 07367f22f1 | |||
| 0c47aa6a92 | |||
| e23f603c41 | |||
| 9af9711c0a | |||
| 30fadc3686 | |||
| 756090ed79 | |||
| 2630591338 | |||
| e00e4487a4 | |||
| 64f1fd7d7b | |||
| 09b06bdb28 | |||
| 9e1723f4b8 | |||
| 3b4e2ae357 | |||
| d35b9dc7bb | |||
| 500cec5757 | |||
| 891e9994b4 | |||
| 1bff691a64 | |||
| 1913cdb088 | |||
| 83b3ee6212 | |||
| 253c16b64d | |||
| 2ed6a57f36 | |||
| af96d4eebe | |||
| 4e6de24f31 | |||
| 7d1563333b | |||
| f7988538b2 | |||
| 6ba71820b1 | |||
| 61c5863b1a | |||
| 0091b1a153 | |||
| b12afc89e4 | |||
| 844aef124c | |||
| 476a8fcb72 | |||
| fe22fe1c83 | |||
| cb9527cd4f | |||
| e71fc4ff3b | |||
| 89fae608a6 | |||
| 2692097272 | |||
| 11ca67f853 | |||
| 34bcb7e70e | |||
| 608f7d6aa0 | |||
| c43d07d0cf | |||
| ad46a97f53 | |||
| da4e626ffa | |||
| cb4904ea25 | |||
| 4d5289e599 | |||
| 0efd3066a2 | |||
| 002b1aed6a | |||
| c73c1bafc1 | |||
| e56c732209 | |||
| cff22cd5ab | |||
| 2ec3b297d7 | |||
| 1e480e2851 | |||
| a22d64816d | |||
| 89d9032e4d | |||
| f540fab1dc | |||
| a49c099ae5 | |||
| 1291869f90 | |||
| 7a420b6a34 | |||
| ff963d9feb | |||
| ad693b830a | |||
| c2b0463539 | |||
| f0445d5cac | |||
| 2debc731ef | |||
| 56aaf7a998 | |||
| 86cb89c5fc | |||
| 9a75b24e1c | |||
| b19ce46723 | |||
| 370843c967 | |||
| da15787ed0 | |||
| 801a2b9497 | |||
| 7a5bdf76b6 | |||
| 63f7c6cc6a | |||
| b35b012e9e | |||
| 0204fbb810 | |||
| 5cf7efb761 | |||
| 2016d4c6dd | |||
| aae18f59aa | |||
| 4d23fe8a58 | |||
| fcfcdd6508 | |||
| 45808a99b4 | |||
| e4acde27c9 | |||
| 5182b60391 | |||
| 23ab7d4973 | |||
| af8f13bac5 | |||
| e5b2b5e0ba | |||
| 9a6b07f0de | |||
| 11e53bf4b0 | |||
| 176caab765 | |||
| bd03886512 | |||
| 6f6d2d9a0f | |||
| e6af0ea94e | |||
| 86b45212e8 | |||
| b2a485e244 | |||
| 80f521eed0 | |||
| f14fdf23e3 | |||
| 25724ebfbb | |||
| c73623506d | |||
| 7450f0e33f | |||
| 55163a6ba9 | |||
| 4e04339cbd | |||
| bf3562d13e | |||
| aca3ad2008 | |||
| f34e050e9b | |||
| c2eb42fc7b | |||
| 625e015ff8 | |||
| 9d74cebd74 | |||
| 64ad970e5e | |||
| f0fb54d907 | |||
| 5da8226f72 | |||
| 2fcd7f93b9 | |||
| 9b30118456 | |||
| dd87304fcf | |||
| b08fbf99db | |||
| b3f708d4d1 | |||
| 4572a114a9 | |||
| c889381be2 | |||
| 1e07a5eef6 | |||
| 2c40c8554b | |||
| 955f4f3bb7 | |||
| 1dca48371d | |||
| c0b9a52430 | |||
| 462b8f9861 | |||
| 242ab5d5aa | |||
| bf8a28b220 | |||
| 20d6ffb9ee | |||
| a76a1e3f5c | |||
| fbd18d8ead | |||
| 78b3369ca0 | |||
| 2f0513b91c | |||
| b25405fd56 | |||
| 5565f47e9b | |||
| 2848c7d6be | |||
| 4e151ddf30 | |||
| 4b9de301c3 | |||
| dbffeb334b | |||
| 165709ff7d | |||
| 0a75d57b13 | |||
| 3182c71093 | |||
| 56fb55683a | |||
| 78229db6a1 | |||
| d04044bb0a | |||
| c1b4ccc4c6 | |||
| 5db3c95e29 | |||
| bcae2164ac | |||
| 207eed1190 | |||
| c948d9e40e | |||
| 9ccdee7687 | |||
| 1a3e68a956 | |||
| dfde819c8b | |||
| ffe67fc6df | |||
| 8a60437bea | |||
| c62143a5df | |||
| 691f2c8950 | |||
| 6ae2c78e9f | |||
| 7a4ea4dd65 | |||
| 217a7af933 | |||
| df176b292a | |||
| fa5a564a17 | |||
| dd1d91486d | |||
| f9ab045794 | |||
| 32bf836fec | |||
| e205d5accc | |||
| 9febdf9b0f | |||
| ce8695ff1f | |||
| 3e80d6ee79 | |||
| 513bafbcfd | |||
| 7fc818ebb8 | |||
| 0219831af4 | |||
| 477aa7c750 | |||
| 47e844d0f4 | |||
| c44c1b5345 | |||
| 28e9e58577 | |||
| 6a628bcd3a | |||
| 10e44e40d9 | |||
| a625583b11 | |||
| 810ab99d7d | |||
| 82a76a1b69 | |||
| ddca055e0d | |||
| b0a11d12ca | |||
| 996564f803 | |||
| 2512e6b09e | |||
| d5d6b0f7f9 | |||
| 52b4f3bb1d | |||
| ad9d4bb4af | |||
| 569d9dfe39 | |||
| 1c2f5dc1df | |||
| 75d95bc4b8 | |||
| a6c17af4c7 | |||
| 7ac2d7f598 | |||
| c4533967d1 | |||
| 5bd4a81e09 | |||
| 72f0a11f0d | |||
| 7b873db958 | |||
| b83cd14d25 | |||
| 4b0ca64617 | |||
| 7741ae3094 | |||
| aa1dc46bc8 | |||
| 27dd2e751f | |||
| f88dbfcafc | |||
| aaf45097b7 | |||
| adb0352ef4 | |||
| da4486b202 | |||
| 778470818a | |||
| 4625eeb998 | |||
| 4e7d555c18 | |||
| 8b7f262aca | |||
| d33dd13fd5 | |||
| a548bccf18 | |||
| 9977c07a69 | |||
| eedb5fde7a | |||
| eabc15f983 | |||
| 18ac43934a | |||
| 35914200b5 | |||
| 48c064505e | |||
| 417fbe1534 | |||
| c70ceb5f7b | |||
| c4735904e5 | |||
| 0792e7bdd9 | |||
| eaf09ebbc4 | |||
| d620ee8cdf | |||
| f7124a4a95 | |||
| 34f1f22b00 | |||
| a95ac513bf | |||
| a829242d47 | |||
| 2479bc2042 | |||
| 67376511ac | |||
| b65b4892ae | |||
| be159037d4 | |||
| eccadb67b7 | |||
| 94d8b7f040 | |||
| c6bf1efac6 | |||
| 50c7842d73 | |||
| 48597285cd | |||
| e13fe01c7b | |||
| 1542a9f9e1 | |||
| 310b932482 | |||
| edf923e38e | |||
| 95a5cb8038 | |||
| 771786ad4d | |||
| 5f6b32c4cb | |||
| 8ee732f391 | |||
| 21e6eea20e | |||
| 1dbc78253c | |||
| c9d48f6f25 | |||
| 894ee17f53 | |||
| cbdfc83b69 | |||
| 6c484f9fc8 | |||
| 6de35b2623 | |||
| f0c1a3789d | |||
| 8f0f474882 | |||
| 452cf5c72d | |||
| dd88b540c2 | |||
| 6a04c121af | |||
| 83eb2a624c | |||
| 1d6bee488d | |||
| 14574fdb56 | |||
| aa213563e7 | |||
| 1d01c74390 | |||
| 0e70a88d03 | |||
| 49b666b670 | |||
| 4c1beb921f | |||
| dcb43cf3b0 | |||
| aa71fcfb7f | |||
| 53557c3be1 | |||
| 16f7008c68 | |||
| 291a5449eb | |||
| 42d52002ee | |||
| c9b7b67fad | |||
| 8a2929c1a9 | |||
| 8f53f30e02 | |||
| 9ea7607613 | |||
| 26a607e8a3 | |||
| 4378d1dadc | |||
| 582eb88404 | |||
| ec0e1d74a0 | |||
| 82eacb2e48 | |||
| 8b976e77ce | |||
| cab8184fcb | |||
| 7b18faed92 | |||
| 4adb2a7695 | |||
| 410b333e47 | |||
| a8908c1a50 | |||
| b0fec7d82b |
@@ -1,6 +1,3 @@
|
||||
**/yui/build/** -diff
|
||||
**/amd/build/** -diff
|
||||
lib/dml/oci_native_moodle_package.sql text eol=lf
|
||||
**.js.map -diff
|
||||
**-min.js -diff
|
||||
**.min.js -diff
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
# Primary donations pages.
|
||||
custom: ["https://moodle.com/donations/", "https://fund.moodle.org"]
|
||||
custom: ["https://moodle.com/donations/", moodle.org]
|
||||
|
||||
@@ -19,7 +19,7 @@ env:
|
||||
|
||||
jobs:
|
||||
Grunt:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checking out code
|
||||
@@ -61,12 +61,12 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
# MySQL builds always run with the lowest PHP supported version.
|
||||
- os: ubuntu-24.04
|
||||
- os: ubuntu-22.04
|
||||
php: 8.1
|
||||
extensions:
|
||||
db: mysqli
|
||||
# PostgreSQL builds always run with the highest PHP supported version.
|
||||
- os: ubuntu-24.04
|
||||
- os: ubuntu-22.04
|
||||
php: 8.3
|
||||
db: pgsql
|
||||
|
||||
@@ -122,6 +122,3 @@ jobs:
|
||||
dbtype: ${{ matrix.db }}
|
||||
phpunit_options: ${{ secrets.phpunit_options }}
|
||||
run: vendor/bin/phpunit $phpunit_options ${{ inputs.phpunit_extra_options }}
|
||||
|
||||
- name: Git status
|
||||
run: git status
|
||||
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
|
||||
- name: Setting up redis service
|
||||
run: |
|
||||
choco install redis --version 5.0.7 --no-progress
|
||||
choco install redis-64 --version 3.0.503 --no-progress
|
||||
nssm install redis redis-server
|
||||
nssm start redis
|
||||
|
||||
|
||||
@@ -58,4 +58,3 @@ moodle-plugin-ci.phar
|
||||
.hugo_build.lock
|
||||
phpcs.xml
|
||||
jsconfig.json
|
||||
UPGRADING-CURRENT.md
|
||||
|
||||
+2
-27
@@ -40,20 +40,9 @@ const fetchComponentData = () => {
|
||||
if (!Object.entries(componentData).length) {
|
||||
componentData.subsystems = {};
|
||||
componentData.pathList = [];
|
||||
componentData.components = {};
|
||||
componentData.standardComponents = {};
|
||||
|
||||
// Fetch the component definiitions from the distributed JSON file.
|
||||
const components = JSON.parse(fs.readFileSync(`${gruntFilePath}/lib/components.json`));
|
||||
const pluginData = JSON.parse(fs.readFileSync(`${gruntFilePath}/lib/plugins.json`));
|
||||
|
||||
componentData.pluginTypes = components.plugintypes;
|
||||
|
||||
const standardPlugins = Object.entries(pluginData.standard).map(
|
||||
([pluginType, pluginNames]) => {
|
||||
return pluginNames.map(pluginName => `${pluginType}_${pluginName}`);
|
||||
}
|
||||
).reduce((acc, val) => acc.concat(val), []);
|
||||
|
||||
// Build the list of moodle subsystems.
|
||||
componentData.subsystems.lib = 'core';
|
||||
@@ -66,8 +55,8 @@ const fetchComponentData = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// The list of components includes the list of subsystems.
|
||||
componentData.components = {...componentData.subsystems};
|
||||
// The list of components incldues the list of subsystems.
|
||||
componentData.components = componentData.subsystems;
|
||||
|
||||
// Go through each of the plugintypes.
|
||||
Object.entries(components.plugintypes).forEach(([pluginType, pluginTypePath]) => {
|
||||
@@ -98,20 +87,6 @@ const fetchComponentData = () => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Create a list of the standard subsystem and plugins.
|
||||
componentData.standardComponents = Object.fromEntries(
|
||||
Object.entries(componentData.components).filter(([, name]) => {
|
||||
if (name === 'core' || name.startsWith('core_')) {
|
||||
return true;
|
||||
}
|
||||
return standardPlugins.indexOf(name) !== -1;
|
||||
})
|
||||
);
|
||||
|
||||
componentData.componentMapping = Object.fromEntries(
|
||||
Object.entries(componentData.components).map(([path, name]) => [name, path])
|
||||
);
|
||||
}
|
||||
|
||||
return componentData;
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import * as Components from '../../components.js';
|
||||
|
||||
const componentData = Components.fetchComponentData();
|
||||
|
||||
/**
|
||||
* The standard components shipped with core Moodle.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
export const standardComponents = componentData.standardComponents;
|
||||
|
||||
/**
|
||||
* All components of the current Moodle instance.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
export const allComponents = componentData.components;
|
||||
|
||||
/**
|
||||
* Get all components of the current Moodle instance.
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
export const getAllComponents = () => {
|
||||
let components = new Map(Object.entries(componentData.pluginTypes).map(([value, path]) => ([path,{
|
||||
path,
|
||||
value,
|
||||
name: `${value} (plugin type)`,
|
||||
}])));
|
||||
|
||||
Object
|
||||
.entries(componentData.components)
|
||||
.filter(([path, value]) => Object.values(componentData.standardComponents).includes(value))
|
||||
.forEach(([path, value]) => {
|
||||
const entry = {
|
||||
path,
|
||||
value,
|
||||
name: value,
|
||||
};
|
||||
if (Object.values(componentData.subsystems).includes(value)) {
|
||||
if (components.has(path)) {
|
||||
entry.name = `${value} (subsystem / plugintype)`;
|
||||
} else {
|
||||
entry.name = `${value} (subsystem)`;
|
||||
}
|
||||
}
|
||||
|
||||
components.set(path, entry);
|
||||
});
|
||||
|
||||
return Array.from(components.values());
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether the specified component is a standard component shipped with core Moodle.
|
||||
*
|
||||
* @param {string} componentName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isStandardComponent = (componentName) => {
|
||||
if (Object.values(componentData.standardComponents).includes(componentName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Object.keys(componentData.pluginTypes).includes(componentName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const rewritePlugintypeAsSubsystem = (componentName) => {
|
||||
if (Object.keys(componentData.pluginTypes).includes(componentName)) {
|
||||
const pluginTypePath = componentData.pluginTypes[componentName];
|
||||
if (Object.keys(componentData.subsystems).includes(pluginTypePath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether the specified component is a community component.
|
||||
*
|
||||
* @param {string} componentName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isCommunityComponent = (componentName) => {
|
||||
if (isStandardComponent(componentName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Object.values(componentData.components).indexOf(componentName) !== -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort method for components.
|
||||
*
|
||||
* This method sorts components putting `core` first, followed by core subsystems, then everything else.
|
||||
*
|
||||
* @param {String} a
|
||||
* @param {String} b
|
||||
* @returns {Number}
|
||||
*/
|
||||
export const sortComponents = (a, b) => {
|
||||
// Always put 'core' first.
|
||||
if (a === 'core') {
|
||||
return -1;
|
||||
} else if (b === 'core') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Put core subsystems next.
|
||||
if (a.startsWith('core_') && !b.startsWith('core_')) {
|
||||
return -1;
|
||||
} else if (b.startsWith('core_') && !a.startsWith('core_')) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Sort alphabetically for everything else.
|
||||
return a.localeCompare(b);
|
||||
};
|
||||
@@ -1,85 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
// 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/>.
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { createNote } from './note.mjs';
|
||||
import { getInitialValues } from './helpers.mjs';
|
||||
import * as Prompts from './prompts.mjs';
|
||||
import logger from './logger.mjs';
|
||||
|
||||
export default async (options) => {
|
||||
// Processs the initial values.
|
||||
const initialValues = getInitialValues(options);
|
||||
|
||||
// Fetch information.
|
||||
const messages = [];
|
||||
const { issueNumber } = await inquirer.prompt([
|
||||
Prompts.getIssuePrompt(),
|
||||
], initialValues);
|
||||
|
||||
let selection = {};
|
||||
let notePath;
|
||||
do {
|
||||
selection = {};
|
||||
selection = await inquirer.prompt([
|
||||
Prompts.getComponentsPrompt(),
|
||||
Prompts.getTypePrompt(),
|
||||
Prompts.getMessagePromptInput(),
|
||||
], initialValues);
|
||||
if (selection.message === '') {
|
||||
selection = Object.assign(
|
||||
selection,
|
||||
await inquirer.prompt([
|
||||
Prompts.getMessagePromptEditor(),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
logger.info(`
|
||||
Creating upgrade note with the following options:
|
||||
|
||||
- Issue: ${chalk.bold(issueNumber)}
|
||||
- Component: ${chalk.bold(selection.components)}
|
||||
- Type: ${chalk.bold(selection.type)}
|
||||
- Message:
|
||||
${chalk.bold(selection.message)}
|
||||
`);
|
||||
|
||||
messages.push({
|
||||
components: [selection.components],
|
||||
type: selection.type,
|
||||
message: selection.message,
|
||||
});
|
||||
|
||||
// Save the note so far.
|
||||
if (notePath) {
|
||||
await createNote(issueNumber, messages, notePath);
|
||||
logger.info(`Updated note at: ${chalk.underline(chalk.bold(notePath))}`);
|
||||
} else {
|
||||
notePath = await createNote(issueNumber, messages);
|
||||
logger.info(`Note created at: ${chalk.underline(chalk.bold(notePath))}`);
|
||||
}
|
||||
|
||||
selection = Object.assign(
|
||||
selection,
|
||||
await inquirer.prompt([
|
||||
Prompts.getAddAnotherPrompt(),
|
||||
], initialValues),
|
||||
);
|
||||
} while (selection.addAnother);
|
||||
};
|
||||
@@ -1,357 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
// 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/>.
|
||||
|
||||
import chalk from 'chalk';
|
||||
import { getAllComponents } from './components.mjs';
|
||||
import { getCombinedNotesByComponent, deleteAllNotes } from './note.mjs';
|
||||
import { getNoteName } from './noteTypes.mjs';
|
||||
import { writeFile, readFile, unlink } from 'fs/promises';
|
||||
import { join as joinPath } from 'path';
|
||||
import logger from './logger.mjs';
|
||||
import { getCurrentVersion } from './helpers.mjs';
|
||||
|
||||
/**
|
||||
* Helper to fetch the current notes from a file.
|
||||
*
|
||||
* @param {string} file
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
const getCurrentNotes = async (file) => {
|
||||
try {
|
||||
return await readFile(file, 'utf8');
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the UPGRADING.md file.
|
||||
*
|
||||
* @param {string} upgradeNotes
|
||||
* @param {Object} options
|
||||
* @param {boolean} options.deleteNotes
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const updateUpgradeNotes = async (upgradeNotes, options) => {
|
||||
const fileName = 'UPGRADING.md';
|
||||
// Write the notes to a file.
|
||||
logger.info(`Writing notes to ${chalk.underline(chalk.bold(fileName))}`);
|
||||
// Prepend to the existing file.
|
||||
const existingContent = await getCurrentNotes(fileName);
|
||||
if (existingContent) {
|
||||
await writeFile(fileName, getUpdatedNotes(existingContent, upgradeNotes));
|
||||
} else {
|
||||
// This should not normally happen.
|
||||
await writeFile(fileName, upgradeNotes);
|
||||
}
|
||||
|
||||
if (options.deleteNotes) {
|
||||
logger.warn(`>>> Deleting all notes <<<`)
|
||||
// Delete the notes.
|
||||
deleteAllNotes();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the current summary notes.
|
||||
*
|
||||
* @param {string} upgradeNotes
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const createCurrentSummary = async (upgradeNotes) => {
|
||||
const fileName = 'UPGRADING-CURRENT.md';
|
||||
const notes = `# Moodle upgrade notes\n\n${upgradeNotes}`;
|
||||
await writeFile(fileName, notes);
|
||||
|
||||
logger.info(`Running upgrade notes written to ${chalk.underline(chalk.bold(fileName))}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the indexes of the lines that contain the version headings.
|
||||
*
|
||||
* @param {array<string>} lines
|
||||
* @returns {array<object>}
|
||||
*/
|
||||
const getVersionLineIndexes = (lines) => {
|
||||
const h2Indexes = [];
|
||||
lines.forEach((line, index) => {
|
||||
const matches = line.match(/^##\s(?<version>.*)$/);
|
||||
if (matches) {
|
||||
h2Indexes.push({
|
||||
index,
|
||||
line,
|
||||
version: matches.groups.version,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return h2Indexes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the index of the Unreleased heading.
|
||||
*
|
||||
* @param {array<object>} versionHeadings
|
||||
* @returns {number}
|
||||
*/
|
||||
const findUnreleasedHeadingIndex = (versionHeadings) => versionHeadings.findIndex((heading) => {
|
||||
if (heading.version === 'Unreleased') {
|
||||
// Used if version cannot be guessed.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (heading.version.endsWith('+')) {
|
||||
// Weekly release for a stable branch.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (heading.version.match(/beta|rc\d/)) {
|
||||
// Beta and RC rolls are treated as weeklies.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (heading.version.endsWith('dev')) {
|
||||
// Development version.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the before and after content, to facilitate replacing any existing Unreleased notes.
|
||||
*
|
||||
* @param {array<string>} lines
|
||||
* @returns {Object} {beforeContent: string, afterContent: string}
|
||||
*/
|
||||
const getBeforeAndAfterContent = (lines) => {
|
||||
const existingLines = lines.split('\n');
|
||||
const versionHeadings = getVersionLineIndexes(existingLines);
|
||||
|
||||
if (versionHeadings.length > 0) {
|
||||
const unreleasedHeadingIndex = findUnreleasedHeadingIndex(versionHeadings);
|
||||
if (unreleasedHeadingIndex !== -1) {
|
||||
const beforeContent = existingLines.slice(0, versionHeadings[unreleasedHeadingIndex].index).join('\n');
|
||||
if (versionHeadings.length > unreleasedHeadingIndex + 1) {
|
||||
const afterContent = existingLines.slice(versionHeadings[unreleasedHeadingIndex + 1].index).join('\n');
|
||||
return {
|
||||
beforeContent,
|
||||
afterContent,
|
||||
};
|
||||
}
|
||||
return {
|
||||
beforeContent,
|
||||
afterContent: '',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
beforeContent: existingLines.slice(0, versionHeadings[0].index).join('\n'),
|
||||
afterContent: existingLines.slice(versionHeadings[0].index).join('\n'),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
beforeContent: existingLines.join('\n'),
|
||||
afterContent: '',
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the notes for the component.
|
||||
*
|
||||
* @param {string} types
|
||||
* @param {Number} headingLevel
|
||||
* @returns {string}
|
||||
*/
|
||||
const getNotesForComponent = (types, headingLevel) => {
|
||||
let upgradeNotes = '';
|
||||
Object.entries(types).forEach(([type, notes]) => {
|
||||
upgradeNotes += '#'.repeat(headingLevel);
|
||||
upgradeNotes += ` ${getNoteName(type)}\n\n`;
|
||||
notes.forEach(({ message, issueNumber }) => {
|
||||
// Split the message into lines, removing empty lines.
|
||||
const messageLines = message
|
||||
.split('\n')
|
||||
// Remove empty lines between tables, and list entries, but not after lists.
|
||||
.filter((line, index, lines) => {
|
||||
if (line.trim().length === 0) {
|
||||
// This line is empty.
|
||||
|
||||
// If it's the first line in the file, remove it.
|
||||
if (index === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is the last line in the file, remove it.
|
||||
if (index === lines.length - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the previous line relates to a table, remove this line.
|
||||
if (lines[index - 1].match(/^\s*\|/)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the next line is also empty, do not remove this line.
|
||||
if (lines[index + 1].trim().length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do not remove the line if the previous line was a list item.
|
||||
if (lines[index - 1].match(/^\s*[-*]\s/)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lines[index - 1].match(/^\s*\d+\.\s/)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Preserve all other empty lines by default.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Keep any line which has content.
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
const firstLine = messageLines.shift().trim();
|
||||
upgradeNotes += `- ${firstLine}\n`;
|
||||
|
||||
messageLines
|
||||
.forEach((line) => {
|
||||
upgradeNotes += ` ${line}`.trimEnd() + `\n`;
|
||||
});
|
||||
upgradeNotes += `\n For more information see [${issueNumber}](https://tracker.moodle.org/browse/${issueNumber})\n`;
|
||||
});
|
||||
upgradeNotes += '\n';
|
||||
});
|
||||
|
||||
return upgradeNotes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the updated notes mixed with existing content.
|
||||
*
|
||||
* @param {string} existingContent
|
||||
* @param {string} upgradeNotes
|
||||
*/
|
||||
const getUpdatedNotes = (existingContent, upgradeNotes) => {
|
||||
const { beforeContent, afterContent } = getBeforeAndAfterContent(existingContent);
|
||||
const newContent = `${beforeContent}\n${upgradeNotes}\n${afterContent}`
|
||||
.split('\n')
|
||||
.filter((line, index, lines) => {
|
||||
if (line === '' && lines[index - 1] === '') {
|
||||
// Remove multiple consecutive empty lines.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
return newContent;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the notes for each component.
|
||||
*/
|
||||
const updateComponentNotes = (
|
||||
notes,
|
||||
version,
|
||||
notesFileName = 'UPGRADING.md',
|
||||
removeEmpty = false,
|
||||
) => {
|
||||
return getAllComponents().map(async (component) => {
|
||||
logger.verbose(`Updating notes for ${component.name} into ${component.path}`);
|
||||
const fileName = joinPath(component.path, notesFileName);
|
||||
|
||||
const existingContent = await getCurrentNotes(fileName);
|
||||
|
||||
if (!existingContent) {
|
||||
if (!notes[component.value]) {
|
||||
// No existing notes, and no new notes to add.
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!notes[component.value]) {
|
||||
// There is existing content, but nothing to add.
|
||||
if (removeEmpty) {
|
||||
logger.verbose(`Removing empty notes file ${fileName}`);
|
||||
await unlink(fileName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const componentNotes = notes[component.value];
|
||||
let upgradeNotes = `## ${version}\n\n`;
|
||||
upgradeNotes += getNotesForComponent(componentNotes, 3);
|
||||
|
||||
if (existingContent) {
|
||||
await writeFile(fileName, getUpdatedNotes(existingContent, upgradeNotes));
|
||||
} else {
|
||||
await writeFile(
|
||||
fileName,
|
||||
`# ${component.name} Upgrade notes\n\n${upgradeNotes}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the upgrade notes for a new release.
|
||||
*
|
||||
* @param {string|undefined} version
|
||||
* @param {Object} options
|
||||
* @param {boolean} options.generateUpgradeNotes
|
||||
* @param {boolean} options.deleteNotes
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export default async (version, options = {}) => {
|
||||
const notes = await getCombinedNotesByComponent();
|
||||
|
||||
if (Object.keys(notes).length === 0) {
|
||||
logger.warn('No notes to generate');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!version) {
|
||||
version = await getCurrentVersion();
|
||||
}
|
||||
|
||||
// Generate the upgrade notes for this release.
|
||||
// We have
|
||||
// - a title with the release name
|
||||
// - the change types
|
||||
// - which contain the components
|
||||
// - which document each change
|
||||
let upgradeNotes = `## ${version}\n\n`;
|
||||
|
||||
Object.entries(notes).forEach(([component, types]) => {
|
||||
upgradeNotes += `### ${component}\n\n`;
|
||||
upgradeNotes += getNotesForComponent(types, 4);
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
createCurrentSummary(upgradeNotes),
|
||||
...updateComponentNotes(notes, version, 'UPGRADING-CURRENT.md', true),
|
||||
]);
|
||||
if (options.generateUpgradeNotes) {
|
||||
await Promise.all(updateComponentNotes(notes, version));
|
||||
await updateUpgradeNotes(upgradeNotes, options);
|
||||
}
|
||||
};
|
||||
@@ -1,230 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import chalk from 'chalk';
|
||||
import { isStandardComponent, isCommunityComponent, rewritePlugintypeAsSubsystem } from './components.mjs';
|
||||
import { isValidNoteName } from './noteTypes.mjs';
|
||||
import logger from './logger.mjs';
|
||||
import { readFile } from 'fs/promises';
|
||||
|
||||
/**
|
||||
* Validate an issue number input
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {string|boolean}
|
||||
*/
|
||||
export const validateIssueNumber = (input) => {
|
||||
if (!input) {
|
||||
return 'You must provide a tracker issue number';
|
||||
}
|
||||
|
||||
if (input.match(/^[a-zA-Z]*-\d+$/)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (input.match(/^\d+$/)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'The issue number was not recognised as a valid issue number';
|
||||
};
|
||||
|
||||
/**
|
||||
* Format an issue number input.
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {string}
|
||||
*/
|
||||
export const formatIssueNumber = (input) => {
|
||||
if (input.match(/^[a-zA-Z]*-\d+$/)) {
|
||||
return input;
|
||||
}
|
||||
|
||||
if (input.match(/^\d+$/)) {
|
||||
return `MDL-${input}`;
|
||||
}
|
||||
|
||||
return input;
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate a component.
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {string|boolean}
|
||||
*/
|
||||
export const validateComponent = (input) => {
|
||||
if (isStandardComponent(input)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isCommunityComponent(input)) {
|
||||
return 'Currently only core plugins are supported.';
|
||||
}
|
||||
|
||||
return 'The component was not recognised as a standard component';
|
||||
};
|
||||
|
||||
export const formatComponent = (input) => {
|
||||
if (rewritePlugintypeAsSubsystem(input)) {
|
||||
return `core_${input}`;
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the initial values from the options.
|
||||
*
|
||||
* @param {object} options
|
||||
* @returns {object}
|
||||
*/
|
||||
export const getInitialValues = (options) => {
|
||||
const initialValues = {};
|
||||
|
||||
const type = getInitialTypeValue(options);
|
||||
if (type) {
|
||||
initialValues.type = type;
|
||||
}
|
||||
|
||||
const issueNumber = getInitialIssueValue(options);
|
||||
if (issueNumber) {
|
||||
initialValues.issueNumber = issueNumber;
|
||||
}
|
||||
|
||||
const component = getInitialComponentValue(options);
|
||||
if (component) {
|
||||
initialValues.components = component;
|
||||
}
|
||||
|
||||
const message = getInitialMessageValue(options);
|
||||
if (message) {
|
||||
initialValues.message = message
|
||||
initialValues.addAnother = false;
|
||||
}
|
||||
|
||||
return initialValues;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the initial type value.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @returns {string|undefined}
|
||||
*/
|
||||
const getInitialTypeValue = (options) => {
|
||||
if (!options.type) {
|
||||
return;
|
||||
}
|
||||
|
||||
options.type = options.type.trim().toLowerCase();
|
||||
|
||||
if (isValidNoteName(options.type)) {
|
||||
return options.type;
|
||||
}
|
||||
|
||||
logger.warn(`Note type "${chalk.underline(chalk.red(options.type))}" is not valid.`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the initial issue number value.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @returns {string|undefined}
|
||||
*/
|
||||
|
||||
const getInitialIssueValue = (options) => {
|
||||
if (!options.issue) {
|
||||
return;
|
||||
}
|
||||
options.issue = options.issue.trim().toUpperCase();
|
||||
|
||||
const issueNumberValidated = validateIssueNumber(options.issue);
|
||||
if (issueNumberValidated === true) {
|
||||
const issueNumber = formatIssueNumber(options.issue);
|
||||
if (issueNumber !== options.issue) {
|
||||
logger.warn(
|
||||
`Issue number "${chalk.underline(chalk.red(options.issue))}" was updated to ` +
|
||||
`"${chalk.underline(chalk.green(issueNumber))}"`
|
||||
);
|
||||
}
|
||||
|
||||
return issueNumber;
|
||||
} else {
|
||||
logger.warn(`Issue number "${chalk.underline(chalk.red(options.issue))}" is not valid: ${issueNumberValidated}`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the initial component value.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @returns {string|undefined}
|
||||
*/
|
||||
const getInitialComponentValue = (options) => {
|
||||
if (!options.component) {
|
||||
return;
|
||||
}
|
||||
|
||||
options.component = options.component.trim().toLowerCase();
|
||||
const componentValidated = validateComponent(options.component);
|
||||
if (componentValidated === true) {
|
||||
const component = formatComponent(options.component);
|
||||
if (component !== options.component) {
|
||||
logger.warn(
|
||||
`Component "${chalk.underline(chalk.red(options.component))}" was updated to ` +
|
||||
`"${chalk.underline(chalk.green(component))}"`
|
||||
);
|
||||
}
|
||||
|
||||
return component;
|
||||
} else {
|
||||
logger.warn(`Component "${chalk.underline(chalk.red(options.component))}" is not valid: ${componentValidated}`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the initial message value.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @returns {string|undefined}
|
||||
*/
|
||||
|
||||
const getInitialMessageValue = (options) => {
|
||||
if (!options.message) {
|
||||
return;
|
||||
}
|
||||
|
||||
return options.message.trim();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current version from the project /version.php file.
|
||||
*
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
export const getCurrentVersion = async () => {
|
||||
const versionRegex = new RegExp(/^ *\$release *= *['\"](?<release>[^ \+]+\+?) *\(Build:.*/m);
|
||||
try {
|
||||
const versionFile = await readFile('version.php', 'utf8');
|
||||
const match = versionFile.match(versionRegex);
|
||||
if (match) {
|
||||
return match.groups.release;
|
||||
}
|
||||
} catch(error) {
|
||||
logger.error('Unable to read the version file');
|
||||
}
|
||||
|
||||
return "Unreleased";
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
// 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/>.
|
||||
|
||||
import winston from 'winston';
|
||||
|
||||
const logger = winston.createLogger({
|
||||
level: 'info',
|
||||
format: winston.format.combine(
|
||||
winston.format.colorize(),
|
||||
winston.format.simple()
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
],
|
||||
});
|
||||
|
||||
export default logger;
|
||||
@@ -1,194 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import yaml from 'js-yaml';
|
||||
import path from 'path';
|
||||
import { writeFile, mkdir, readdir, readFile, unlink } from 'fs/promises';
|
||||
import { isValidNoteName, sortNoteTypes } from './noteTypes.mjs';
|
||||
import { sortComponents } from './components.mjs';
|
||||
|
||||
const unreleasedPath = path.resolve('.upgradenotes');
|
||||
|
||||
/**
|
||||
* Get the filename for the note.
|
||||
*
|
||||
* @param {string} issueNumnber The issue number
|
||||
* @returns {string}
|
||||
*/
|
||||
const getFilename = (issueNumber) => {
|
||||
const dateTimeFormat = new Intl.DateTimeFormat(undefined, {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
fractionalSecondDigits: 2,
|
||||
timeZone: 'UTC',
|
||||
});
|
||||
|
||||
const date = Object.fromEntries(
|
||||
dateTimeFormat.formatToParts(new Date())
|
||||
.filter((p) => p.type !== 'literal')
|
||||
.map((p) => ([p.type, p.value]))
|
||||
);
|
||||
|
||||
const dateString = [
|
||||
date.year,
|
||||
date.month,
|
||||
date.day,
|
||||
date.hour,
|
||||
date.minute,
|
||||
date.second,
|
||||
date.fractionalSecond,
|
||||
].join('');
|
||||
|
||||
return `${issueNumber}-${dateString}.yml`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new note.
|
||||
*
|
||||
* @param {string} issueNumber
|
||||
* @param {[type: string]: {message: string}} messages
|
||||
* @returns {string} The path to the note on disk
|
||||
*/
|
||||
export const createNote = async (
|
||||
issueNumber,
|
||||
messages,
|
||||
notePath,
|
||||
) => {
|
||||
const note = {
|
||||
issueNumber,
|
||||
notes: {},
|
||||
};
|
||||
|
||||
messages.forEach(({components, type, message}) => {
|
||||
note.notes[components] = note.notes[components] || [];
|
||||
note.notes[components].push({message, type});
|
||||
});
|
||||
|
||||
if (!notePath) {
|
||||
notePath = path.resolve(unreleasedPath, getFilename(issueNumber));
|
||||
}
|
||||
const noteContent = yaml.dump(note);
|
||||
|
||||
await mkdir(unreleasedPath, {recursive: true});
|
||||
await writeFile(notePath, noteContent);
|
||||
|
||||
return notePath;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all unreleased notes.
|
||||
*
|
||||
* @returns {Promise<{issueNumber: string, components: string[], types: {[type: string]: {message: string}[]}}[]>
|
||||
*/
|
||||
export const getAllNotes = async () => {
|
||||
const files = await readdir(unreleasedPath);
|
||||
const notes = files
|
||||
.filter((file) => file.endsWith('.yml'))
|
||||
.map(async (file) => {
|
||||
const filePath = path.resolve(unreleasedPath, file);
|
||||
const fileContent = await readFile(filePath, 'utf8');
|
||||
|
||||
return {
|
||||
...yaml.load(fileContent),
|
||||
filePath,
|
||||
};
|
||||
});
|
||||
|
||||
return Promise.all(notes);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the list of notes, grouped by note type, then component.
|
||||
*
|
||||
* @returns {Promise<{[type: string]: {[components: string]: {message: string, issueNumber: string}[]}}>}
|
||||
*/
|
||||
export const getCombinedNotes = async () => {
|
||||
const notes = await getAllNotes();
|
||||
const combinedNotes = {};
|
||||
|
||||
notes.forEach((note) => {
|
||||
Object.entries(note.notes).forEach(([components, data]) => {
|
||||
data.forEach((entry) => {
|
||||
if (!isValidNoteName(entry.type)) {
|
||||
throw new Error(`Invalid note type: "${entry.type}" in file ${note.filePath}`);
|
||||
}
|
||||
combinedNotes[entry.type] = combinedNotes[entry.type] || {};
|
||||
combinedNotes[entry.type][components] = combinedNotes[entry.type][components] || [];
|
||||
combinedNotes[entry.type][components].push({message: entry.message, issueNumber: note.issueNumber});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return combinedNotes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the list of notes, grouped by component, then by note type.
|
||||
*
|
||||
* @returns {Promise<{[component: string]: {[type: string]: {message: string, issueNumber: string}[]}}>}
|
||||
*/
|
||||
export const getCombinedNotesByComponent = async () => {
|
||||
const notes = await getAllNotes();
|
||||
const combinedNotes = {};
|
||||
|
||||
notes.forEach((note) => {
|
||||
Object.entries(note.notes).forEach(([component, data]) => {
|
||||
combinedNotes[component] = combinedNotes[component] || {};
|
||||
data.forEach((entry) => {
|
||||
if (!isValidNoteName(entry.type)) {
|
||||
throw new Error(`Invalid note type: "${entry.type}" in file ${note.filePath}`);
|
||||
}
|
||||
combinedNotes[component][entry.type] = combinedNotes[component][entry.type] || [];
|
||||
combinedNotes[component][entry.type].push({
|
||||
component,
|
||||
message: entry.message,
|
||||
issueNumber: note.issueNumber,
|
||||
type: entry.type,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Sort notes by note type.
|
||||
Object.entries(combinedNotes).forEach(([component, types]) => {
|
||||
combinedNotes[component] = Object.fromEntries(
|
||||
Object.entries(types).sort(([a], [b]) => sortNoteTypes(a, b))
|
||||
);
|
||||
});
|
||||
|
||||
// Sort components.
|
||||
return Object.fromEntries(
|
||||
Object.entries(combinedNotes).sort(([a], [b]) => sortComponents(a, b))
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete all unreleased notes.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const deleteAllNotes = async () => {
|
||||
const files = await readdir(unreleasedPath);
|
||||
return Promise.all(
|
||||
files
|
||||
.filter((item, index) => files.indexOf(item) === index)
|
||||
.filter((file) => file.endsWith('.yml'))
|
||||
.map((file) => unlink(`${unreleasedPath}/${file}`))
|
||||
);
|
||||
};
|
||||
@@ -1,80 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The note types.
|
||||
*
|
||||
* Matches the suggested order in https://keepachangelog.com/en/1.1.0/.
|
||||
* @type {Object<string, string>}
|
||||
*/
|
||||
const noteTypes = {
|
||||
'improved': 'Added',
|
||||
'changed': 'Changed',
|
||||
'deprecated': 'Deprecated',
|
||||
'removed': 'Removed',
|
||||
'fixed': 'Fixed',
|
||||
};
|
||||
|
||||
/**
|
||||
* The preferred order of note types.
|
||||
*
|
||||
* @type {string[]}
|
||||
*/
|
||||
const preferredOrder = Object.keys(noteTypes);
|
||||
|
||||
/**
|
||||
* Comparison method to sort note types.
|
||||
*
|
||||
* @param {String} a
|
||||
* @param {String} b
|
||||
* @returns {Number}
|
||||
*/
|
||||
export const sortNoteTypes = (a, b) => {
|
||||
const aIndex = preferredOrder.indexOf(a);
|
||||
const bIndex = preferredOrder.indexOf(b);
|
||||
|
||||
if (aIndex === -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (bIndex === -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return aIndex - bIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the note names.
|
||||
*
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export const getNoteNames = () => Object.keys(noteTypes);
|
||||
|
||||
/**
|
||||
* Get the human-readable note name.
|
||||
*
|
||||
* @param {string} type
|
||||
* @returns {string}
|
||||
*/
|
||||
export const getNoteName = (type) => noteTypes[type];
|
||||
|
||||
/**
|
||||
* Whether the note name is valid.
|
||||
*
|
||||
* @param {string} type
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isValidNoteName = (type) => noteTypes[type] !== undefined;
|
||||
@@ -1,153 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import inquirer from 'inquirer';
|
||||
import SearchList from 'inquirer-search-list';
|
||||
|
||||
import { getNoteNames } from './noteTypes.mjs';
|
||||
import { getAllComponents } from './components.mjs';
|
||||
import {
|
||||
formatComponent,
|
||||
formatIssueNumber,
|
||||
validateComponent,
|
||||
validateIssueNumber,
|
||||
} from './helpers.mjs';
|
||||
|
||||
/**
|
||||
* A Search List which accepts an initial value.
|
||||
*/
|
||||
class SearchListWithInitialValue extends SearchList {
|
||||
constructor(options, ...args) {
|
||||
super(options, ...args);
|
||||
|
||||
if (options.default) {
|
||||
const pointer = this.filterList.findIndex((item) => {
|
||||
return item.value === options.default;
|
||||
});
|
||||
if (pointer > -1) {
|
||||
this.pointer = pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inquirer.registerPrompt('search-list', SearchListWithInitialValue);
|
||||
|
||||
/**
|
||||
* Get the issue type prompt.
|
||||
*
|
||||
* @param {string} defaultData The initially selected value
|
||||
* @returns {Object}
|
||||
*/
|
||||
export const getTypePrompt = (defaultData) => ({
|
||||
default: defaultData,
|
||||
type: 'search-list',
|
||||
message: 'Type of change',
|
||||
name: 'type',
|
||||
choices: getNoteNames(),
|
||||
validate: (selection) => {
|
||||
if (selection.length < 1) {
|
||||
return 'You must select at least one type of change';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the component prompt.
|
||||
*
|
||||
* @param {string} [defaultValue='core'] The initally selected value.
|
||||
* @returns
|
||||
*/
|
||||
export const getComponentsPrompt = (defaultValue) => {
|
||||
if (!defaultValue ) {
|
||||
defaultValue = 'core';
|
||||
}
|
||||
|
||||
return {
|
||||
choices: getAllComponents(),
|
||||
default: defaultValue,
|
||||
type: 'search-list',
|
||||
message: 'Component',
|
||||
name: 'components',
|
||||
validate: validateComponent,
|
||||
filter: formatComponent,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the issue number prompt as an inline input.
|
||||
*
|
||||
* @param {string} defaultData
|
||||
* @returns {object}
|
||||
*/
|
||||
export const getIssuePrompt = (defaultData) => ({
|
||||
default: defaultData,
|
||||
type: 'input',
|
||||
message: 'Tracker issue number',
|
||||
name: 'issueNumber',
|
||||
validate: validateIssueNumber,
|
||||
filter: formatIssueNumber,
|
||||
});
|
||||
|
||||
/**
|
||||
* Get a message prompt.
|
||||
*
|
||||
* @param {string} defaultData
|
||||
* @returns
|
||||
*/
|
||||
export const getMessagePromptEditor = (defaultData) => ({
|
||||
default: defaultData,
|
||||
type: process.stdin.isTTY ? 'editor' : 'input',
|
||||
postfix: '.md',
|
||||
message: 'Message',
|
||||
name: 'message',
|
||||
waitUserInput: false,
|
||||
validate: (input) => {
|
||||
if (!input) {
|
||||
return 'You must provide a message';
|
||||
}
|
||||
return true;
|
||||
},
|
||||
// Remove any trailing whitespace.
|
||||
filter: (input) => input.split('\n').map((line) => line.trimEnd()).join('\n'),
|
||||
});
|
||||
|
||||
/**
|
||||
* Get a message prompt.
|
||||
*
|
||||
* @param {string} defaultData
|
||||
* @returns
|
||||
*/
|
||||
export const getMessagePromptInput = (defaultData) => ({
|
||||
default: defaultData,
|
||||
type: 'input',
|
||||
message: 'Message (leave empty to use editor)',
|
||||
name: 'message',
|
||||
filter: (input) => input.trim(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Get a prompt to ask the user if they wish to add another entry.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
export const getAddAnotherPrompt = () => ({
|
||||
type: 'confirm',
|
||||
message: 'Do you want to add another note?',
|
||||
default: false,
|
||||
name: 'addAnother',
|
||||
});
|
||||
@@ -1,103 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
// 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/>.
|
||||
|
||||
import { Argument, Option, program } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { getNoteNames } from './notes/src/noteTypes.mjs';
|
||||
import createAction from './notes/src/create.mjs';
|
||||
import generateAction from './notes/src/generate.mjs';
|
||||
import logger from './notes/src/logger.mjs';
|
||||
|
||||
console.log(`
|
||||
${chalk.bold(chalk.underline(chalk.green('Moodle Upgrade Notes Generator')))}
|
||||
|
||||
This tool is used to generate the upgrade notes for changes you make in Moodle.
|
||||
|
||||
Please remember that the intended audience of these changes is
|
||||
${chalk.italic('plugin developers')} who need to know how to update their plugins
|
||||
for a new Moodle version.
|
||||
|
||||
Upgrade notes should not be used to document changes for site administrators, or
|
||||
for internal API changes which are not expected to be used outside of the
|
||||
relevant component.
|
||||
`)
|
||||
|
||||
program.configureHelp({
|
||||
helpWidth: 100,
|
||||
});
|
||||
|
||||
program.on('option:verbose', () => {
|
||||
logger.level = 'verbose';
|
||||
});
|
||||
|
||||
program.addOption(
|
||||
new Option(
|
||||
'-v, --verbose',
|
||||
'Output more information during the generation process',
|
||||
)
|
||||
.default(false)
|
||||
);
|
||||
|
||||
// Define the command line options.
|
||||
program
|
||||
.command('create')
|
||||
.summary('Generate a new upgrade note')
|
||||
.addOption(
|
||||
new Option('-t, --type <type>', `The type of change to document. Valid types are: ${getNoteNames().join(', ')}`)
|
||||
)
|
||||
.addOption(new Option('-i, --issue <issue>', 'The tracker issue number'))
|
||||
.addOption(new Option('-c, --component <component>', 'The component to write a note for'))
|
||||
.addOption(new Option(
|
||||
'-m, --message <message>',
|
||||
'The message to use for the upgrade note',
|
||||
))
|
||||
.action((options) => createAction(options));
|
||||
|
||||
program
|
||||
.command('summary')
|
||||
.summary('Generate a local copy of the upgrade notes summary')
|
||||
.addArgument(
|
||||
new Argument('[version]', 'The Moodle version to create the summary notes for')
|
||||
)
|
||||
.action((version) => generateAction(version));
|
||||
|
||||
program
|
||||
.command('release')
|
||||
.summary('Generate the markdown copies of the upgrade notes for a Moodle release')
|
||||
.addArgument(
|
||||
new Argument('[version]', 'The Moodle version to create the release notes for')
|
||||
)
|
||||
.addOption(
|
||||
new Option(
|
||||
'--generate-upgrade-notes',
|
||||
'Generate the UPGRADING.md notes for the release. ' +
|
||||
'Note: This option is intended for use by the release manager when generating the upgrade notes.',
|
||||
)
|
||||
.default(true)
|
||||
)
|
||||
.addOption(
|
||||
new Option(
|
||||
'-d, --delete-notes',
|
||||
'Delete the notes after generating the UPGRADING.md notes for the release. ' +
|
||||
'Note: This option is intended for use by the release manager when generating the upgrade notes.' +
|
||||
'This option has no effect unless --generate-upgrade-notes is also set.'
|
||||
)
|
||||
.default(false)
|
||||
)
|
||||
.action((version, options) => generateAction(version, options));
|
||||
|
||||
program.parse();
|
||||
-1513
File diff suppressed because it is too large
Load Diff
@@ -1,9 +0,0 @@
|
||||
# core_admin (subsystem) Upgrade notes
|
||||
|
||||
## 4.5rc1
|
||||
|
||||
### Removed
|
||||
|
||||
- The HTTP Server setting "Use slash arguments" (slasharguments) configuration setting and related to it has been removed. Calling the option with $CFG->slasharguments or get_config('slasharguments') is no longer available.
|
||||
|
||||
For more information see [MDL-62640](https://tracker.moodle.org/browse/MDL-62640)
|
||||
@@ -1,65 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Manage AI subsystem plugins settings.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2024 Matt Porritt <matt.porritt@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once('../config.php');
|
||||
require_once($CFG->libdir . '/adminlib.php');
|
||||
|
||||
$action = required_param('action', PARAM_ALPHANUMEXT);
|
||||
$type = required_param('type', PARAM_PLUGIN); // Must be provider or placement.
|
||||
$name = required_param('name', PARAM_PLUGIN); // Plugin name, e.g openai.
|
||||
|
||||
$syscontext = context_system::instance();
|
||||
$PAGE->set_url('/admin/ai.php');
|
||||
$PAGE->set_context($syscontext);
|
||||
|
||||
require_admin();
|
||||
require_sesskey();
|
||||
|
||||
$return = new moodle_url('/admin/settings.php', ['section' => "aisettings$type"]);
|
||||
|
||||
$plugins = core_plugin_manager::instance()->get_plugins_of_type("ai$type");
|
||||
$sortorder = array_flip(array_keys($plugins));
|
||||
|
||||
if (!isset($plugins[$name])) {
|
||||
throw new moodle_exception('aipluginnotfound', 'core_ai', $return, $name);
|
||||
}
|
||||
|
||||
$plugintypename = $plugins[$name]->type . '_' . $plugins[$name]->name;
|
||||
|
||||
switch ($action) {
|
||||
case 'disable':
|
||||
if ($plugins[$name]->is_enabled()) {
|
||||
$class = core_plugin_manager::resolve_plugininfo_class("aisettings$type");
|
||||
$class::enable_plugin($name, false);
|
||||
}
|
||||
break;
|
||||
case 'enable':
|
||||
if (!$plugins[$name]->is_enabled()) {
|
||||
$class = core_plugin_manager::resolve_plugininfo_class("aisettings$type");
|
||||
$class::enable_plugin($name, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
redirect($return);
|
||||
Vendored
-12
@@ -1,12 +0,0 @@
|
||||
define("core_admin/expand_hash",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=_exports.expandSection=void 0;
|
||||
/**
|
||||
* Expand the collapse section element.
|
||||
*
|
||||
* @module core_admin/expand_hash
|
||||
* @copyright Meirza <meirza.arson@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since 4.5
|
||||
*/
|
||||
const SELECTORS_COLLAPSE_ELEMENTS='[data-toggle="collapse"]',SELECTORS_FOCUSTHENEXPAND_ELEMENTS=".focus-expand";_exports.init=()=>{const focusexpand=document.querySelector(SELECTORS_FOCUSTHENEXPAND_ELEMENTS);null==focusexpand||focusexpand.addEventListener("click",(()=>{expandSection("".concat(focusexpand.getAttribute("href")))}))};const expandSection=hash=>{const container=document.querySelector(hash),targetContainer=null==container?void 0:container.querySelector(SELECTORS_COLLAPSE_ELEMENTS);if("false"===(null==targetContainer?void 0:targetContainer.getAttribute("aria-expanded"))){const collapseId=targetContainer.getAttribute("aria-controls"),collapseContainer=document.getElementById(collapseId);collapseContainer.classList.remove("collapse"),collapseContainer.classList.add("show"),targetContainer.setAttribute("aria-expanded","true"),targetContainer.classList.remove("collapsed");const collapseElement=document.querySelector(".collapseexpand.collapsemenu");collapseElement.setAttribute("aria-expanded","true"),collapseElement.classList.remove("collapsed")}};_exports.expandSection=expandSection}));
|
||||
|
||||
//# sourceMappingURL=expand_hash.min.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"expand_hash.min.js","sources":["../src/expand_hash.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Expand the collapse section element.\n *\n * @module core_admin/expand_hash\n * @copyright Meirza <meirza.arson@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.5\n */\n\nconst SELECTORS = {\n COLLAPSE_ELEMENTS: '[data-toggle=\"collapse\"]',\n FOCUSTHENEXPAND_ELEMENTS: '.focus-expand',\n};\n\n/**\n * Initializes the focus and expand functionality.\n */\nexport const init = () => {\n // Select all collapsible elements only.\n const focusexpand = document.querySelector(SELECTORS.FOCUSTHENEXPAND_ELEMENTS);\n\n // Add click event listener to the anchor element\n focusexpand?.addEventListener('click', () => {\n expandSection(`${focusexpand.getAttribute('href')}`);\n });\n};\n\n/**\n * Expands a section based on the provided URL hash.\n *\n * This function takes a hash string, finds the corresponding element in the DOM,\n * and expands it if it is currently collapsed. It updates the necessary ARIA\n * attributes and classes to reflect the expanded state.\n *\n * @param {string} hash - The hash (e.g., '#elementId') of the element to expand.\n */\nexport const expandSection = (hash) => {\n const container = document.querySelector(hash);\n const targetContainer = container?.querySelector(SELECTORS.COLLAPSE_ELEMENTS);\n\n if (targetContainer?.getAttribute('aria-expanded') === 'false') {\n const collapseId = targetContainer.getAttribute('aria-controls');\n const collapseContainer = document.getElementById(collapseId);\n\n // Show the content.\n collapseContainer.classList.remove('collapse');\n collapseContainer.classList.add('show');\n\n // Update aria-expanded attribute to reflect the new state.\n targetContainer.setAttribute('aria-expanded', 'true');\n targetContainer.classList.remove('collapsed');\n\n // Get collapse expand menu element.\n const collapseElement = document.querySelector('.collapseexpand.collapsemenu');\n // Ensure it gets noticed to make it work.\n collapseElement.setAttribute('aria-expanded', 'true');\n collapseElement.classList.remove('collapsed');\n }\n};\n"],"names":["SELECTORS","focusexpand","document","querySelector","addEventListener","expandSection","getAttribute","hash","container","targetContainer","collapseId","collapseContainer","getElementById","classList","remove","add","setAttribute","collapseElement"],"mappings":";;;;;;;;;MAwBMA,4BACiB,2BADjBA,mCAEwB,8BAMV,WAEVC,YAAcC,SAASC,cAAcH,oCAG3CC,MAAAA,aAAAA,YAAaG,iBAAiB,SAAS,KACnCC,wBAAiBJ,YAAYK,aAAa,oBAarCD,cAAiBE,aACpBC,UAAYN,SAASC,cAAcI,MACnCE,gBAAkBD,MAAAA,iBAAAA,UAAWL,cAAcH,gCAEM,WAAnDS,MAAAA,uBAAAA,gBAAiBH,aAAa,kBAA8B,OACtDI,WAAaD,gBAAgBH,aAAa,iBAC1CK,kBAAoBT,SAASU,eAAeF,YAGlDC,kBAAkBE,UAAUC,OAAO,YACnCH,kBAAkBE,UAAUE,IAAI,QAGhCN,gBAAgBO,aAAa,gBAAiB,QAC9CP,gBAAgBI,UAAUC,OAAO,mBAG3BG,gBAAkBf,SAASC,cAAc,gCAE/Cc,gBAAgBD,aAAa,gBAAiB,QAC9CC,gBAAgBJ,UAAUC,OAAO"}
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
define("core_admin/plugin_management_table",["exports","core_table/dynamic","core_table/local/dynamic/selectors","core/ajax","core/pending","core/notification"],(function(_exports,_dynamic,Selectors,_ajax,_pending,_notification){var obj;function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Selectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Selectors),_pending=(obj=_pending)&&obj.__esModule?obj:{default:obj};let watching=!1;return _exports.default=class{constructor(){!function(obj,key,value){key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}(this,"clickHandlers",[]),this.addClickHandler(this.handleStateToggle),this.addClickHandler(this.handleMoveUpDown),this.registerEventListeners()}static init(){watching||(watching=!0,new this)}addClickHandler(handler){this.clickHandlers.push(handler.bind(this))}registerEventListeners(){document.addEventListener("click",function(e){const tableRoot=this.getTableRoot(e);tableRoot&&this.clickHandlers.forEach((handler=>handler(tableRoot,e)))}.bind(this))}getTableRoot(e){const tableRoot=e.target.closest(Selectors.main.region);return tableRoot||!1}setPluginState(methodname,plugin,state){return(0,_ajax.call)([{methodname:methodname,args:{plugin:plugin,state:state}}])[0]}setPluginOrder(methodname,plugin,direction){return(0,_ajax.call)([{methodname:methodname,args:{plugin:plugin,direction:direction}}])[0]}async handleStateToggle(tableRoot,e){const stateToggle=e.target.closest('[data-action="togglestate"][data-toggle-method]');if(stateToggle){e.preventDefault();const pendingPromise=new _pending.default("core_table/dynamic:togglestate");await this.setPluginState(stateToggle.dataset.toggleMethod,stateToggle.dataset.plugin,"1"===stateToggle.dataset.state?0:1);const[updatedRoot]=await Promise.all([(0,_dynamic.refreshTableContent)(tableRoot),(0,_notification.fetchNotifications)()]);updatedRoot.querySelector('[data-action="togglestate"][data-plugin="'.concat(stateToggle.dataset.plugin,'"]')).focus(),this.removeTooltips(),pendingPromise.resolve()}}async handleMoveUpDown(tableRoot,e){const actionLink=e.target.closest('[data-action="move"][data-method][data-direction]');if(!actionLink)return;e.preventDefault();const pendingPromise=new _pending.default("core_table/dynamic:processAction");await this.setPluginOrder(actionLink.dataset.method,actionLink.dataset.plugin,"up"===actionLink.dataset.direction?-1:1);const[updatedRoot]=await Promise.all([(0,_dynamic.refreshTableContent)(tableRoot),(0,_notification.fetchNotifications)()]),exactMatch=updatedRoot.querySelector('[data-action="move"][data-plugin="'.concat(actionLink.dataset.plugin,'"][data-direction="').concat(actionLink.dataset.direction,'"]'));var _updatedRoot$querySel;exactMatch?exactMatch.focus():null===(_updatedRoot$querySel=updatedRoot.querySelector('[data-action="move"][data-plugin="'.concat(actionLink.dataset.plugin,'"]')))||void 0===_updatedRoot$querySel||_updatedRoot$querySel.focus();pendingPromise.resolve()}removeTooltips(){const tooltips=document.querySelectorAll('[id*="tooltip"]');tooltips.length>0&&tooltips.forEach((tooltip=>{tooltip.remove()}))}},_exports.default}));
|
||||
define("core_admin/plugin_management_table",["exports","core_table/dynamic","core_table/local/dynamic/selectors","core/ajax","core/pending","core/notification"],(function(_exports,_dynamic,Selectors,_ajax,_pending,_notification){var obj;function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Selectors=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Selectors),_pending=(obj=_pending)&&obj.__esModule?obj:{default:obj};let watching=!1;return _exports.default=class{constructor(){!function(obj,key,value){key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value}(this,"clickHandlers",[]),this.addClickHandler(this.handleStateToggle),this.addClickHandler(this.handleMoveUpDown),this.registerEventListeners()}static init(){watching||(watching=!0,new this)}addClickHandler(handler){this.clickHandlers.push(handler.bind(this))}registerEventListeners(){document.addEventListener("click",function(e){const tableRoot=this.getTableRoot(e);tableRoot&&this.clickHandlers.forEach((handler=>handler(tableRoot,e)))}.bind(this))}getTableRoot(e){const tableRoot=e.target.closest(Selectors.main.region);return tableRoot||!1}setPluginState(methodname,plugin,state){return(0,_ajax.call)([{methodname:methodname,args:{plugin:plugin,state:state}}])[0]}setPluginOrder(methodname,plugin,direction){return(0,_ajax.call)([{methodname:methodname,args:{plugin:plugin,direction:direction}}])[0]}async handleStateToggle(tableRoot,e){const stateToggle=e.target.closest('[data-action="togglestate"][data-toggle-method]');if(stateToggle){e.preventDefault();const pendingPromise=new _pending.default("core_table/dynamic:togglestate");await this.setPluginState(stateToggle.dataset.toggleMethod,stateToggle.dataset.plugin,"1"===stateToggle.dataset.state?0:1);const[updatedRoot]=await Promise.all([(0,_dynamic.refreshTableContent)(tableRoot),(0,_notification.fetchNotifications)()]);updatedRoot.querySelector('[data-action="togglestate"][data-plugin="'.concat(stateToggle.dataset.plugin,'"]')).focus(),pendingPromise.resolve()}}async handleMoveUpDown(tableRoot,e){const actionLink=e.target.closest('[data-action="move"][data-method][data-direction]');if(!actionLink)return;e.preventDefault();const pendingPromise=new _pending.default("core_table/dynamic:processAction");await this.setPluginOrder(actionLink.dataset.method,actionLink.dataset.plugin,"up"===actionLink.dataset.direction?-1:1);const[updatedRoot]=await Promise.all([(0,_dynamic.refreshTableContent)(tableRoot),(0,_notification.fetchNotifications)()]),exactMatch=updatedRoot.querySelector('[data-action="move"][data-plugin="'.concat(actionLink.dataset.plugin,'"][data-direction="').concat(actionLink.dataset.direction,'"]'));var _updatedRoot$querySel;exactMatch?exactMatch.focus():null===(_updatedRoot$querySel=updatedRoot.querySelector('[data-action="move"][data-plugin="'.concat(actionLink.dataset.plugin,'"]')))||void 0===_updatedRoot$querySel||_updatedRoot$querySel.focus();pendingPromise.resolve()}},_exports.default}));
|
||||
|
||||
//# sourceMappingURL=plugin_management_table.min.js.map
|
||||
File diff suppressed because one or more lines are too long
-11
@@ -1,11 +0,0 @@
|
||||
define("core_admin/plugins_overview",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(){const filters=document.querySelectorAll(SELECTORS_PLUGIN_FILTERS),pluginRows=document.querySelectorAll(SELECTORS_PLUGIN_ROWS),pluginTypeRows=document.querySelectorAll(SELECTORS_PLUGIN_TYPE_ROWS),filterPlugins=target=>{const filterBy=target.getAttribute("data-filterby"),headerVisibility={};for(const row of pluginRows){const type=[...row.classList].find((s=>s.startsWith("type-"))),visible="all"===filterBy||row.classList.contains(filterBy);row.style.display=visible?null:"none",visible&&type&&(headerVisibility[type]=!0)}for(const row of pluginTypeRows){const type=[...row.classList].find((s=>s.startsWith("type-")));if(type){const visible="all"===filterBy||headerVisibility[type];row.style.display=visible?null:"none"}}filters.forEach((el=>el.classList.remove("active"))),target.classList.add("active")};if(filters.forEach((target=>target.addEventListener("click",(e=>{e.preventDefault(),window.history.replaceState({},null,e.target.href),filterPlugins(target)})))),window.location.hash.length>1){const anchor=window.location.hash.substring(1),target=[...filters].find((t=>t.getAttribute("data-filterby")===anchor));target&&filterPlugins(target)}};
|
||||
/**
|
||||
* Allows to filter the plugin list on plugins overview page
|
||||
*
|
||||
* @module core_admin/plugins_overview
|
||||
* @copyright 2024 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
const SELECTORS_PLUGIN_FILTERS="#plugins-overview-panel [data-filterby]",SELECTORS_PLUGIN_ROWS="table#plugins-control-panel tbody tr:not(.plugintypeheader)",SELECTORS_PLUGIN_TYPE_ROWS="table#plugins-control-panel tbody tr.plugintypeheader"}));
|
||||
|
||||
//# sourceMappingURL=plugins_overview.min.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"plugins_overview.min.js","sources":["../src/plugins_overview.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Allows to filter the plugin list on plugins overview page\n *\n * @module core_admin/plugins_overview\n * @copyright 2024 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst SELECTORS = {\n PLUGIN_FILTERS: '#plugins-overview-panel [data-filterby]',\n PLUGIN_ROWS: 'table#plugins-control-panel tbody tr:not(.plugintypeheader)',\n PLUGIN_TYPE_ROWS: 'table#plugins-control-panel tbody tr.plugintypeheader',\n};\n\n/**\n * Initialise filters for the \"Plugins overview\" page\n */\nexport function init() {\n const filters = document.querySelectorAll(SELECTORS.PLUGIN_FILTERS);\n const pluginRows = document.querySelectorAll(SELECTORS.PLUGIN_ROWS);\n const pluginTypeRows = document.querySelectorAll(SELECTORS.PLUGIN_TYPE_ROWS);\n\n const filterPlugins = (target) => {\n const filterBy = target.getAttribute('data-filterby');\n const headerVisibility = {};\n\n // Hide all plugin rows in the plugin table that do not match the filter and show all others.\n for (const row of pluginRows) {\n const type = [...row.classList].find(s => s.startsWith('type-'));\n const visible = filterBy === 'all' ? true : row.classList.contains(filterBy);\n row.style.display = visible ? null : 'none';\n if (visible && type) {\n headerVisibility[type] = true;\n }\n }\n\n // Hide all the plugin type headers that do not have any visible plugins and show all others.\n for (const row of pluginTypeRows) {\n const type = [...row.classList].find(s => s.startsWith('type-'));\n if (type) {\n const visible = filterBy === 'all' || headerVisibility[type];\n row.style.display = visible ? null : 'none';\n }\n }\n\n // Toggle 'active' class for the selected filter.\n filters.forEach(el => el.classList.remove('active'));\n target.classList.add('active');\n };\n\n // Add event listeners for the links changing plugins filters.\n filters\n .forEach(target => target.addEventListener('click', (e) => {\n e.preventDefault();\n window.history.replaceState({}, null, e.target.href);\n filterPlugins(target);\n }));\n\n // Pre-filter plugins based on the current url anchor.\n if (window.location.hash.length > 1) {\n const anchor = window.location.hash.substring(1);\n const target = [...filters].find(t => t.getAttribute('data-filterby') === anchor);\n if (target) {\n filterPlugins(target);\n }\n }\n}\n"],"names":["filters","document","querySelectorAll","SELECTORS","pluginRows","pluginTypeRows","filterPlugins","target","filterBy","getAttribute","headerVisibility","row","type","classList","find","s","startsWith","visible","contains","style","display","forEach","el","remove","add","addEventListener","e","preventDefault","window","history","replaceState","href","location","hash","length","anchor","substring","t"],"mappings":"4JAiCUA,QAAUC,SAASC,iBAAiBC,0BACpCC,WAAaH,SAASC,iBAAiBC,uBACvCE,eAAiBJ,SAASC,iBAAiBC,4BAE3CG,cAAiBC,eACbC,SAAWD,OAAOE,aAAa,iBAC/BC,iBAAmB,OAGpB,MAAMC,OAAOP,WAAY,OACpBQ,KAAO,IAAID,IAAIE,WAAWC,MAAKC,GAAKA,EAAEC,WAAW,WACjDC,QAAuB,QAAbT,UAA4BG,IAAIE,UAAUK,SAASV,UACnEG,IAAIQ,MAAMC,QAAUH,QAAU,KAAO,OACjCA,SAAWL,OACXF,iBAAiBE,OAAQ,OAK5B,MAAMD,OAAON,eAAgB,OACxBO,KAAO,IAAID,IAAIE,WAAWC,MAAKC,GAAKA,EAAEC,WAAW,cACnDJ,KAAM,OACAK,QAAuB,QAAbT,UAAsBE,iBAAiBE,MACvDD,IAAIQ,MAAMC,QAAUH,QAAU,KAAO,QAK7CjB,QAAQqB,SAAQC,IAAMA,GAAGT,UAAUU,OAAO,YAC1ChB,OAAOM,UAAUW,IAAI,cAIzBxB,QACCqB,SAAQd,QAAUA,OAAOkB,iBAAiB,SAAUC,IACjDA,EAAEC,iBACFC,OAAOC,QAAQC,aAAa,GAAI,KAAMJ,EAAEnB,OAAOwB,MAC/CzB,cAAcC,aAIdqB,OAAOI,SAASC,KAAKC,OAAS,EAAG,OAC3BC,OAASP,OAAOI,SAASC,KAAKG,UAAU,GACxC7B,OAAS,IAAIP,SAASc,MAAKuB,GAAKA,EAAE5B,aAAa,mBAAqB0B,SACtE5B,QACAD,cAAcC;;;;;;;;MAvDpBJ,yBACc,0CADdA,sBAEW,8DAFXA,2BAGgB"}
|
||||
@@ -1,74 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Expand the collapse section element.
|
||||
*
|
||||
* @module core_admin/expand_hash
|
||||
* @copyright Meirza <meirza.arson@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since 4.5
|
||||
*/
|
||||
|
||||
const SELECTORS = {
|
||||
COLLAPSE_ELEMENTS: '[data-toggle="collapse"]',
|
||||
FOCUSTHENEXPAND_ELEMENTS: '.focus-expand',
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the focus and expand functionality.
|
||||
*/
|
||||
export const init = () => {
|
||||
// Select all collapsible elements only.
|
||||
const focusexpand = document.querySelector(SELECTORS.FOCUSTHENEXPAND_ELEMENTS);
|
||||
|
||||
// Add click event listener to the anchor element
|
||||
focusexpand?.addEventListener('click', () => {
|
||||
expandSection(`${focusexpand.getAttribute('href')}`);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Expands a section based on the provided URL hash.
|
||||
*
|
||||
* This function takes a hash string, finds the corresponding element in the DOM,
|
||||
* and expands it if it is currently collapsed. It updates the necessary ARIA
|
||||
* attributes and classes to reflect the expanded state.
|
||||
*
|
||||
* @param {string} hash - The hash (e.g., '#elementId') of the element to expand.
|
||||
*/
|
||||
export const expandSection = (hash) => {
|
||||
const container = document.querySelector(hash);
|
||||
const targetContainer = container?.querySelector(SELECTORS.COLLAPSE_ELEMENTS);
|
||||
|
||||
if (targetContainer?.getAttribute('aria-expanded') === 'false') {
|
||||
const collapseId = targetContainer.getAttribute('aria-controls');
|
||||
const collapseContainer = document.getElementById(collapseId);
|
||||
|
||||
// Show the content.
|
||||
collapseContainer.classList.remove('collapse');
|
||||
collapseContainer.classList.add('show');
|
||||
|
||||
// Update aria-expanded attribute to reflect the new state.
|
||||
targetContainer.setAttribute('aria-expanded', 'true');
|
||||
targetContainer.classList.remove('collapsed');
|
||||
|
||||
// Get collapse expand menu element.
|
||||
const collapseElement = document.querySelector('.collapseexpand.collapsemenu');
|
||||
// Ensure it gets noticed to make it work.
|
||||
collapseElement.setAttribute('aria-expanded', 'true');
|
||||
collapseElement.classList.remove('collapsed');
|
||||
}
|
||||
};
|
||||
@@ -138,10 +138,6 @@ export default class {
|
||||
|
||||
// Refocus on the link that as pressed in the first place.
|
||||
updatedRoot.querySelector(`[data-action="togglestate"][data-plugin="${stateToggle.dataset.plugin}"]`).focus();
|
||||
|
||||
// When clicking the toggle and it remains focused, a new tooltip will be generated.
|
||||
// Therefore, the old tooltip should be removed.
|
||||
this.removeTooltips();
|
||||
pendingPromise.resolve();
|
||||
}
|
||||
}
|
||||
@@ -180,16 +176,4 @@ export default class {
|
||||
|
||||
pendingPromise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove tooltips.
|
||||
*/
|
||||
removeTooltips() {
|
||||
const tooltips = document.querySelectorAll('[id*="tooltip"]');
|
||||
if (tooltips.length > 0) {
|
||||
tooltips.forEach(tooltip => {
|
||||
tooltip.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Allows to filter the plugin list on plugins overview page
|
||||
*
|
||||
* @module core_admin/plugins_overview
|
||||
* @copyright 2024 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
const SELECTORS = {
|
||||
PLUGIN_FILTERS: '#plugins-overview-panel [data-filterby]',
|
||||
PLUGIN_ROWS: 'table#plugins-control-panel tbody tr:not(.plugintypeheader)',
|
||||
PLUGIN_TYPE_ROWS: 'table#plugins-control-panel tbody tr.plugintypeheader',
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise filters for the "Plugins overview" page
|
||||
*/
|
||||
export function init() {
|
||||
const filters = document.querySelectorAll(SELECTORS.PLUGIN_FILTERS);
|
||||
const pluginRows = document.querySelectorAll(SELECTORS.PLUGIN_ROWS);
|
||||
const pluginTypeRows = document.querySelectorAll(SELECTORS.PLUGIN_TYPE_ROWS);
|
||||
|
||||
const filterPlugins = (target) => {
|
||||
const filterBy = target.getAttribute('data-filterby');
|
||||
const headerVisibility = {};
|
||||
|
||||
// Hide all plugin rows in the plugin table that do not match the filter and show all others.
|
||||
for (const row of pluginRows) {
|
||||
const type = [...row.classList].find(s => s.startsWith('type-'));
|
||||
const visible = filterBy === 'all' ? true : row.classList.contains(filterBy);
|
||||
row.style.display = visible ? null : 'none';
|
||||
if (visible && type) {
|
||||
headerVisibility[type] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Hide all the plugin type headers that do not have any visible plugins and show all others.
|
||||
for (const row of pluginTypeRows) {
|
||||
const type = [...row.classList].find(s => s.startsWith('type-'));
|
||||
if (type) {
|
||||
const visible = filterBy === 'all' || headerVisibility[type];
|
||||
row.style.display = visible ? null : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle 'active' class for the selected filter.
|
||||
filters.forEach(el => el.classList.remove('active'));
|
||||
target.classList.add('active');
|
||||
};
|
||||
|
||||
// Add event listeners for the links changing plugins filters.
|
||||
filters
|
||||
.forEach(target => target.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
window.history.replaceState({}, null, e.target.href);
|
||||
filterPlugins(target);
|
||||
}));
|
||||
|
||||
// Pre-filter plugins based on the current url anchor.
|
||||
if (window.location.hash.length > 1) {
|
||||
const anchor = window.location.hash.substring(1);
|
||||
const target = [...filters].find(t => t.getAttribute('data-filterby') === anchor);
|
||||
if (target) {
|
||||
filterPlugins(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,10 +118,6 @@ foreach ($settingspage->children as $childpage) {
|
||||
}
|
||||
$outputhtml .= html_writer::tag('div', '<!-- -->', array('class' => 'clearer'));
|
||||
$outputhtml .= $setting->output_html($data);
|
||||
if ($childpage->has_dependencies()) {
|
||||
$opts = ['dependencies' => $childpage->get_dependencies_for_javascript()];
|
||||
$PAGE->requires->js_call_amd('core/showhidesettings', 'init', [$opts]);
|
||||
}
|
||||
}
|
||||
$outputhtml .= html_writer::end_tag('fieldset');
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ class filesize extends \admin_setting {
|
||||
* @param int|null $defaultunit GB, MB, etc. (in bytes)
|
||||
*/
|
||||
public function __construct(string $name, string $visiblename, string $description,
|
||||
?int $defaultvalue = null, ?int $defaultunit = null) {
|
||||
int $defaultvalue = null, int $defaultunit = null) {
|
||||
|
||||
$defaultsetting = self::parse_bytes($defaultvalue);
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ class task_log extends base {
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_field("{$tablealias}.classname")
|
||||
->add_field("$tablealias.classname")
|
||||
->set_is_sortable(true)
|
||||
->add_callback(static function(string $classname): string {
|
||||
$output = '';
|
||||
@@ -183,7 +183,15 @@ class task_log extends base {
|
||||
->set_type(column::TYPE_FLOAT)
|
||||
->add_field("{$tablealias}.timeend - {$tablealias}.timestart", 'duration')
|
||||
->set_is_sortable(true)
|
||||
->add_callback([format::class, 'format_time'], 2);
|
||||
->add_callback(static function(float $value): string {
|
||||
$duration = round($value, 2);
|
||||
if (empty($duration)) {
|
||||
// The format_time function returns 'now' when the difference is exactly 0.
|
||||
// Note: format_time performs concatenation in exactly this fashion so we should do this for consistency.
|
||||
return '0 ' . get_string('secs', 'moodle');
|
||||
}
|
||||
return format_time($duration);
|
||||
});
|
||||
|
||||
// Hostname column.
|
||||
$columns[] = (new column(
|
||||
@@ -203,8 +211,11 @@ class task_log extends base {
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_INTEGER)
|
||||
->add_field("{$tablealias}.pid")
|
||||
->set_is_sortable(true);
|
||||
->set_is_sortable(true)
|
||||
// Although this is an integer column, it doesn't make sense to perform numeric aggregation on it.
|
||||
->set_disabled_aggregation(['avg', 'count', 'countdistinct', 'max', 'min', 'sum']);
|
||||
|
||||
// Database column.
|
||||
$columns[] = (new column(
|
||||
@@ -213,14 +224,17 @@ class task_log extends base {
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_INTEGER)
|
||||
->add_fields("{$tablealias}.dbreads, {$tablealias}.dbwrites")
|
||||
->set_is_sortable(true, ["{$tablealias}.dbreads", "{$tablealias}.dbwrites"])
|
||||
->add_callback(static function($value, stdClass $row): string {
|
||||
->add_callback(static function(int $value, stdClass $row): string {
|
||||
$output = '';
|
||||
$output .= \html_writer::div(get_string('task_stats:dbreads', 'admin', $row->dbreads));
|
||||
$output .= \html_writer::div(get_string('task_stats:dbwrites', 'admin', $row->dbwrites));
|
||||
return $output;
|
||||
});
|
||||
})
|
||||
// Although this is an integer column, it doesn't make sense to perform numeric aggregation on it.
|
||||
->set_disabled_aggregation(['avg', 'count', 'countdistinct', 'max', 'min', 'sum']);
|
||||
|
||||
// Database reads column.
|
||||
$columns[] = (new column(
|
||||
|
||||
@@ -20,12 +20,10 @@ use context_system;
|
||||
use core_admin\reportbuilder\local\entities\task_log;
|
||||
use core_reportbuilder\local\entities\user;
|
||||
use core_reportbuilder\local\report\action;
|
||||
use core_reportbuilder\system_report;
|
||||
use html_writer;
|
||||
use lang_string;
|
||||
use moodle_url;
|
||||
use pix_icon;
|
||||
use stdClass;
|
||||
use core_reportbuilder\system_report;
|
||||
|
||||
/**
|
||||
* Task logs system report class implementation
|
||||
@@ -91,9 +89,7 @@ class task_logs extends system_report {
|
||||
* unique identifier
|
||||
*/
|
||||
public function add_columns(): void {
|
||||
$entitymainalias = $this->get_entity('task_log')->get_table_alias('task_log');
|
||||
|
||||
$this->add_columns_from_entities([
|
||||
$columns = [
|
||||
'task_log:name',
|
||||
'task_log:type',
|
||||
'user:fullname',
|
||||
@@ -103,18 +99,14 @@ class task_logs extends system_report {
|
||||
'task_log:pid',
|
||||
'task_log:database',
|
||||
'task_log:result',
|
||||
]);
|
||||
];
|
||||
|
||||
// Wrap the task name in a link.
|
||||
$this->get_column('task_log:name')
|
||||
->add_field("{$entitymainalias}.id")
|
||||
->add_callback(static function(string $output, stdClass $row): string {
|
||||
return html_writer::link(new moodle_url('/admin/tasklogs.php', ['logid' => $row->id]), $output);
|
||||
});
|
||||
$this->add_columns_from_entities($columns);
|
||||
|
||||
// Rename the user fullname column.
|
||||
$this->get_column('user:fullname')
|
||||
->set_title(new lang_string('user', 'admin'));
|
||||
// It's possible to override the display name of a column, if you don't want to use the value provided by the entity.
|
||||
if ($column = $this->get_column('user:fullname')) {
|
||||
$column->set_title(new lang_string('user', 'admin'));
|
||||
}
|
||||
|
||||
// It's possible to set a default initial sort direction for one column.
|
||||
$this->set_initial_sort_column('task_log:starttime', SORT_DESC);
|
||||
@@ -127,14 +119,16 @@ class task_logs extends system_report {
|
||||
* unique identifier
|
||||
*/
|
||||
protected function add_filters(): void {
|
||||
$this->add_filters_from_entities([
|
||||
$filters = [
|
||||
'task_log:name',
|
||||
'task_log:type',
|
||||
'task_log:output',
|
||||
'task_log:result',
|
||||
'task_log:timestart',
|
||||
'task_log:duration',
|
||||
]);
|
||||
];
|
||||
|
||||
$this->add_filters_from_entities($filters);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,7 +143,7 @@ class task_logs extends system_report {
|
||||
new moodle_url('/admin/tasklogs.php', ['logid' => ':id']),
|
||||
new pix_icon('e/search', ''),
|
||||
[],
|
||||
false,
|
||||
true,
|
||||
new lang_string('view'),
|
||||
)));
|
||||
|
||||
|
||||
@@ -159,11 +159,11 @@ class users extends system_report {
|
||||
->add_callback(static function(string $fullname, \stdClass $row): string {
|
||||
if ($row->suspended) {
|
||||
$fullname .= ' ' . \html_writer::tag('span', get_string('suspended', 'moodle'),
|
||||
['class' => 'badge badge-secondary ms-1']);
|
||||
['class' => 'badge badge-secondary ml-1']);
|
||||
}
|
||||
if (!$row->confirmed) {
|
||||
$fullname .= ' ' . \html_writer::tag('span', get_string('confirmationpending', 'admin'),
|
||||
['class' => 'badge badge-danger ms-1']);
|
||||
['class' => 'badge badge-danger ml-1']);
|
||||
}
|
||||
return $fullname;
|
||||
});
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core_admin\table;
|
||||
|
||||
use moodle_url;
|
||||
use stdClass;
|
||||
use html_writer;
|
||||
use get_string_manager;
|
||||
|
||||
/**
|
||||
* Availability admin settings.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2024 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class availability_management_table extends plugin_management_table {
|
||||
|
||||
protected function get_table_id(): string {
|
||||
return 'availabilityconditions_administration_table';
|
||||
}
|
||||
|
||||
protected function get_plugintype(): string {
|
||||
return 'availability';
|
||||
}
|
||||
|
||||
protected function get_action_url(array $params = []): moodle_url {
|
||||
return new moodle_url('/admin/tool/availabilityconditions/', $params);
|
||||
}
|
||||
|
||||
public function guess_base_url(): void {
|
||||
$this->define_baseurl(
|
||||
new moodle_url('/admin/tool/availabilityconditions/')
|
||||
);
|
||||
}
|
||||
|
||||
protected function get_column_list(): array {
|
||||
return [
|
||||
'name' => get_string('plugin'),
|
||||
'version' => get_string('version'),
|
||||
'enabled' => get_string('enabled', 'admin'),
|
||||
'defaultdisplaymode' => get_string('defaultdisplaymode', 'tool_availabilityconditions'),
|
||||
];
|
||||
}
|
||||
|
||||
public function setup(): void {
|
||||
$this->set_attribute('id', 'availabilityconditions_administration_table');
|
||||
$this->set_attribute('class', 'admintable generaltable');
|
||||
parent::setup();
|
||||
}
|
||||
|
||||
protected function col_name(stdClass $row): string {
|
||||
return html_writer::span(
|
||||
get_string('pluginname', 'availability_' . $row->plugininfo->name)
|
||||
);
|
||||
}
|
||||
|
||||
protected function col_defaultdisplaymode(stdClass $row): string {
|
||||
global $OUTPUT, $CFG;
|
||||
$displaymode = get_config('availability_' . $row->plugininfo->name, 'defaultdisplaymode') ? 'show' : 'hide';
|
||||
$paramsdisplaymode = [
|
||||
'sesskey' => sesskey(),
|
||||
'plugin' => $row->plugininfo->name,
|
||||
'displaymode' => $displaymode,
|
||||
];
|
||||
$urldisplaymode = new moodle_url('/' . $CFG->admin . '/tool/availabilityconditions/', $paramsdisplaymode);
|
||||
|
||||
return html_writer::link($urldisplaymode, $OUTPUT->pix_icon('t/' . $displaymode,
|
||||
get_string($displaymode)), ['class' => 'display-mode-' . $row->plugininfo->name]);
|
||||
}
|
||||
}
|
||||
@@ -303,29 +303,28 @@ abstract class plugin_management_table extends flexible_table implements dynamic
|
||||
global $OUTPUT;
|
||||
|
||||
$enabled = $row->plugininfo->is_enabled();
|
||||
if ($enabled) {
|
||||
$labelstr = get_string('disableplugin', 'core_admin', $row->plugininfo->displayname);
|
||||
} else {
|
||||
$labelstr = get_string('enableplugin', 'core_admin', $row->plugininfo->displayname);
|
||||
}
|
||||
|
||||
$params = [
|
||||
'id' => 'admin-toggle-' . $row->plugininfo->name,
|
||||
'checked' => $enabled,
|
||||
'dataattributes' => [
|
||||
'name' => 'id',
|
||||
'value' => $row->plugininfo->name,
|
||||
'toggle-method' => $this->get_toggle_service(),
|
||||
'action' => 'togglestate',
|
||||
'plugin' => $row->plugin,
|
||||
'state' => $enabled ? 1 : 0,
|
||||
],
|
||||
'title' => $labelstr,
|
||||
'label' => $labelstr,
|
||||
'labelclasses' => 'sr-only',
|
||||
'sesskey' => sesskey(),
|
||||
'plugin' => $row->plugininfo->name,
|
||||
'action' => $enabled ? 'disable' : 'enable',
|
||||
];
|
||||
|
||||
return $OUTPUT->render_from_template('core_admin/setting_configtoggle', $params);
|
||||
if ($enabled) {
|
||||
$icon = $OUTPUT->pix_icon('t/hide', get_string('disableplugin', 'core_admin', $row->plugininfo->displayname));
|
||||
} else {
|
||||
$icon = $OUTPUT->pix_icon('t/show', get_string('enableplugin', 'core_admin', $row->plugininfo->displayname));
|
||||
}
|
||||
|
||||
return html_writer::link(
|
||||
$this->get_action_url($params),
|
||||
$icon,
|
||||
[
|
||||
'data-toggle-method' => $this->get_toggle_service(),
|
||||
'data-action' => 'togglestate',
|
||||
'data-plugin' => $row->plugin,
|
||||
'data-state' => $enabled ? 1 : 0,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
protected function col_order(stdClass $row): string {
|
||||
|
||||
@@ -172,16 +172,12 @@ $CFG->debugdeveloper = true;
|
||||
$parts = explode('/', str_replace('\\', '/', dirname(__DIR__)));
|
||||
$CFG->admin = array_pop($parts);
|
||||
|
||||
// Point pear include path to moodles lib/pear so that includes and requires will search there for files before anywhere else
|
||||
// the problem is that we need specific version of quickforms and hacked excel files :-(.
|
||||
//point pear include path to moodles lib/pear so that includes and requires will search there for files before anywhere else
|
||||
//the problem is that we need specific version of quickforms and hacked excel files :-(
|
||||
ini_set('include_path', $CFG->libdir.'/pear' . PATH_SEPARATOR . ini_get('include_path'));
|
||||
|
||||
// The core_component class can be used in any scripts, it does not need anything else.
|
||||
require_once($CFG->libdir.'/classes/component.php');
|
||||
|
||||
// Register our classloader.
|
||||
\core\component::register_autoloader();
|
||||
|
||||
require_once($CFG->libdir.'/classes/text.php');
|
||||
require_once($CFG->libdir.'/classes/string_manager.php');
|
||||
require_once($CFG->libdir.'/classes/string_manager_install.php');
|
||||
@@ -195,6 +191,10 @@ require_once($CFG->libdir.'/moodlelib.php');
|
||||
require_once($CFG->libdir.'/deprecatedlib.php');
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
require_once($CFG->libdir.'/componentlib.class.php');
|
||||
require_once($CFG->dirroot.'/cache/lib.php');
|
||||
|
||||
// Register our classloader.
|
||||
\core_component::register_autoloader();
|
||||
|
||||
require($CFG->dirroot.'/version.php');
|
||||
$CFG->target_release = $release;
|
||||
|
||||
@@ -50,6 +50,6 @@ Example:
|
||||
exit(0);
|
||||
}
|
||||
|
||||
\core\session\manager::destroy_all();
|
||||
\core\session\manager::kill_all_sessions();
|
||||
|
||||
exit(0);
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
define('IGNORE_COMPONENT_CACHE', true);
|
||||
|
||||
require(__DIR__.'/../../config.php');
|
||||
require_once($CFG->libdir.'/clilib.php');
|
||||
|
||||
@@ -55,14 +55,13 @@ If courseid is set, course module/s will be added into the course.
|
||||
|
||||
Options:
|
||||
-f, --file=STRING Path to the backup file.
|
||||
-c, --categoryid=INT ID of the course category to restore to. This option is ignored when restoring an activity and courseid is set.
|
||||
-C, --courseid=INT ID of the course to restore to. This option is ignored when restoring a course and the categoryid is set.
|
||||
-c, --categoryid=INT ID of the course category to restore to.
|
||||
-C, --courseid=INT ID of the course to restore to. This option is ignored if categoryid is set.
|
||||
-s, --showdebugging Show developer level debugging information
|
||||
-h, --help Print out this help.
|
||||
|
||||
Example:
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/restore_backup.php --file=/path/to/backup/coursebackup.mbz --categoryid=1\n
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/restore_backup.php --file=/path/to/backup/activitybackup.mbz --courseid=1\n
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/restore_backup.php --file=/path/to/backup/file.mbz --categoryid=1\n
|
||||
EOL;
|
||||
|
||||
echo $help;
|
||||
@@ -85,15 +84,11 @@ if ($options['categoryid']) {
|
||||
if (!$category = $DB->get_record('course_categories', ['id' => $options['categoryid']], 'id')) {
|
||||
throw new \moodle_exception('invalidcategoryid');
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['courseid']) {
|
||||
} else if ($options['courseid']) {
|
||||
if (!$course = $DB->get_record('course', ['id' => $options['courseid']], 'id')) {
|
||||
throw new \moodle_exception('invalidcourseid');
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($category) && empty($course)) {
|
||||
} else {
|
||||
throw new \moodle_exception('invalidoption');
|
||||
}
|
||||
|
||||
@@ -105,46 +100,28 @@ $fp = get_file_packer('application/vnd.moodle.backup');
|
||||
$fp->extract_to_pathname($options['file'], $path);
|
||||
|
||||
cli_heading(get_string('preprocessingbackupfile'));
|
||||
|
||||
try {
|
||||
// Create a temporary restore controller to determine the restore type.
|
||||
$tmprc = new restore_controller($backupdir, SITEID, backup::INTERACTIVE_NO,
|
||||
backup::MODE_GENERAL, $admin->id, backup::TARGET_EXISTING_ADDING);
|
||||
// Restore the backup into a new course if:
|
||||
// - It is a course backup and the category is set.
|
||||
// - It is an activity backup and the course is not set.
|
||||
$restoreasnewcourse = ($tmprc->get_type() === backup::TYPE_1COURSE && !empty($category)) ||
|
||||
($tmprc->get_type() !== backup::TYPE_1COURSE && empty($course));
|
||||
// Make sure to clean up the temporary restore controller.
|
||||
$tmprc->destroy();
|
||||
list($fullname, $shortname) = restore_dbops::calculate_course_names(0, get_string('restoringcourse', 'backup'),
|
||||
get_string('restoringcourseshortname', 'backup'));
|
||||
|
||||
if ($restoreasnewcourse) {
|
||||
list($fullname, $shortname) = restore_dbops::calculate_course_names(0, get_string('restoringcourse', 'backup'),
|
||||
get_string('restoringcourseshortname', 'backup'));
|
||||
$courseid = restore_dbops::create_new_course($fullname, $shortname, $category->id);
|
||||
$rc = new restore_controller($backupdir, $courseid, backup::INTERACTIVE_NO,
|
||||
backup::MODE_GENERAL, $admin->id, backup::TARGET_NEW_COURSE);
|
||||
} else {
|
||||
if (!empty($course)) {
|
||||
$courseid = $course->id;
|
||||
$rc = new restore_controller($backupdir, $courseid, backup::INTERACTIVE_NO,
|
||||
backup::MODE_GENERAL, $admin->id, backup::TARGET_EXISTING_ADDING);
|
||||
} else {
|
||||
$courseid = restore_dbops::create_new_course($fullname, $shortname, $category->id);
|
||||
$rc = new restore_controller($backupdir, $courseid, backup::INTERACTIVE_NO,
|
||||
backup::MODE_GENERAL, $admin->id, backup::TARGET_NEW_COURSE);
|
||||
}
|
||||
$rc->execute_precheck();
|
||||
$rc->execute_plan();
|
||||
$rc->destroy();
|
||||
|
||||
// Rename the course's full and short names with the backup file's original names if the backup file is an activity backup
|
||||
// that is restored to a new course.
|
||||
if ($restoreasnewcourse && $rc->get_type() !== backup::TYPE_1COURSE) {
|
||||
// Rename course name if the backup is from course module and restore to category.
|
||||
if (empty($course)) {
|
||||
$course = get_course($courseid);
|
||||
$backupinfo = $rc->get_info();
|
||||
$tmpfullname = $backupinfo->original_course_fullname ?? get_string('restoretonewcourse', 'backup');
|
||||
$tmpshortname = $backupinfo->original_course_shortname ?? get_string('newcourse');
|
||||
list($fullname, $shortname) = restore_dbops::calculate_course_names(
|
||||
courseid: 0,
|
||||
fullname: $tmpfullname,
|
||||
shortname: $tmpshortname,
|
||||
);
|
||||
list($fullname, $shortname) = restore_dbops::calculate_course_names(0, get_string('restoretonewcourse', 'backup'),
|
||||
get_string('newcourse'));
|
||||
$course->fullname = $fullname;
|
||||
$course->shortname = $shortname;
|
||||
$course->visible = 1;
|
||||
|
||||
@@ -4327,7 +4327,6 @@
|
||||
<VENDOR name="oracle" version="19" />
|
||||
</DATABASE>
|
||||
<PHP version="8.1.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_84" message="unsupportedphpversion84" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
@@ -4523,7 +4522,6 @@
|
||||
<VENDOR name="oracle" version="19" />
|
||||
</DATABASE>
|
||||
<PHP version="8.1.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_84" message="unsupportedphpversion84" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
@@ -4705,399 +4703,4 @@
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="5.0" requires="4.2.3">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="10.11.0" />
|
||||
<VENDOR name="mysql" version="8.4" />
|
||||
<VENDOR name="postgres" version="14" />
|
||||
<VENDOR name="mssql" version="14.0" />
|
||||
<VENDOR name="auroramysql" version="8.0" />
|
||||
</DATABASE>
|
||||
<PHP version="8.2.0" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="mbstringrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
<PHP_EXTENSION name="sodium" level="required"/>
|
||||
<PHP_EXTENSION name="exif" level="optional"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="sixtyfourbitsrequired" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_max_input_vars" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_admin_dir_usage" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="iscustomadminwarnings" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_xmlrpc_usage" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mod_assignment" level="required">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_db_prefix_length" level="required">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_async_backup" level="recommended">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_aurora_version" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="5.1" requires="4.2.3">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="10.11.0" />
|
||||
<VENDOR name="mysql" version="8.4" />
|
||||
<VENDOR name="postgres" version="15" />
|
||||
<VENDOR name="mssql" version="14.0" />
|
||||
<VENDOR name="auroramysql" version="8.0" />
|
||||
</DATABASE>
|
||||
<PHP version="8.2.0" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="mbstringrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
<PHP_EXTENSION name="sodium" level="required"/>
|
||||
<PHP_EXTENSION name="exif" level="optional"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="zend.exception_ignore_args" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingzendexceptionignoreargs" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="sixtyfourbitsrequired" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_max_input_vars" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_admin_dir_usage" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="iscustomadminwarnings" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_xmlrpc_usage" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mod_assignment" level="required">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_db_prefix_length" level="required">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_async_backup" level="recommended">
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_aurora_version" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
@@ -40,6 +40,13 @@ if (!function_exists('iconv')) {
|
||||
die();
|
||||
}
|
||||
|
||||
// Make sure php5-json is available.
|
||||
if (!function_exists('json_encode') || !function_exists('json_decode')) {
|
||||
// This also shouldn't happen.
|
||||
echo 'Moodle requires the json PHP extension. Please install or enable the json extension.';
|
||||
die();
|
||||
}
|
||||
|
||||
// Make sure xml extension is available.
|
||||
if (!extension_loaded('xml')) {
|
||||
echo 'Moodle requires the xml PHP extension. Please install or enable the xml extension.';
|
||||
|
||||
+7
-4
@@ -29,6 +29,8 @@ require_once($CFG->libdir . '/adminlib.php');
|
||||
require_once($CFG->libdir . '/filelib.php');
|
||||
|
||||
$fetchupdates = optional_param('fetchupdates', false, PARAM_BOOL); // Check for available plugins updates.
|
||||
$updatesonly = optional_param('updatesonly', false, PARAM_BOOL); // Show updateable plugins only.
|
||||
$contribonly = optional_param('contribonly', false, PARAM_BOOL); // Show additional plugins only.
|
||||
$uninstall = optional_param('uninstall', '', PARAM_COMPONENT); // Uninstall the plugin.
|
||||
$delete = optional_param('delete', '', PARAM_COMPONENT); // Delete the plugin folder after it is uninstalled.
|
||||
$confirmed = optional_param('confirm', false, PARAM_BOOL); // Confirm the uninstall/delete action.
|
||||
@@ -45,7 +47,8 @@ require_admin();
|
||||
$syscontext = context_system::instance();
|
||||
|
||||
// URL params we want to maintain on redirects.
|
||||
$pageurl = new moodle_url('/admin/plugins.php');
|
||||
$pageparams = array('updatesonly' => $updatesonly, 'contribonly' => $contribonly);
|
||||
$pageurl = new moodle_url('/admin/plugins.php', $pageparams);
|
||||
|
||||
$pluginman = core_plugin_manager::instance();
|
||||
|
||||
@@ -54,7 +57,7 @@ $PAGE->set_primary_active_tab('siteadminnode');
|
||||
if ($uninstall) {
|
||||
|
||||
if (!$confirmed) {
|
||||
admin_externalpage_setup('pluginsoverview');
|
||||
admin_externalpage_setup('pluginsoverview', '', $pageparams);
|
||||
} else {
|
||||
$PAGE->set_url($pageurl);
|
||||
$PAGE->set_context($syscontext);
|
||||
@@ -198,7 +201,7 @@ if ($installupdate and $installupdateversion) {
|
||||
}
|
||||
}
|
||||
|
||||
admin_externalpage_setup('pluginsoverview');
|
||||
admin_externalpage_setup('pluginsoverview', '', $pageparams);
|
||||
|
||||
/** @var core_admin_renderer $output */
|
||||
$output = $PAGE->get_renderer('core', 'admin');
|
||||
@@ -211,4 +214,4 @@ if ($fetchupdates) {
|
||||
redirect($PAGE->url);
|
||||
}
|
||||
|
||||
echo $output->plugin_management_page($pluginman, $checker);
|
||||
echo $output->plugin_management_page($pluginman, $checker, $pageparams);
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace core_adminpresets;
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass core_adminpresets_generator
|
||||
*/
|
||||
final class generator_test extends \advanced_testcase {
|
||||
class generator_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test the behaviour of create_preset() method.
|
||||
@@ -180,7 +180,7 @@ final class generator_test extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function create_preset_provider(): array {
|
||||
public function create_preset_provider(): array {
|
||||
return [
|
||||
'Default values' => [
|
||||
],
|
||||
|
||||
@@ -23,9 +23,9 @@ namespace core_adminpresets;
|
||||
* @category test
|
||||
* @copyright 2021 Sara Arjona (sara@moodle.com)
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \core_adminpresets\helper
|
||||
* @coversDefaultClass helper
|
||||
*/
|
||||
final class helper_test extends \advanced_testcase {
|
||||
class helper_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test the behaviour of create_preset() method.
|
||||
@@ -84,7 +84,7 @@ final class helper_test extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function create_preset_provider(): array {
|
||||
public function create_preset_provider(): array {
|
||||
return [
|
||||
'Default values' => [
|
||||
],
|
||||
@@ -177,7 +177,7 @@ final class helper_test extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function add_item_provider(): array {
|
||||
public function add_item_provider(): array {
|
||||
return [
|
||||
'Setting without plugin' => [
|
||||
'name' => 'settingname',
|
||||
@@ -239,7 +239,7 @@ final class helper_test extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function add_plugin_provider(): array {
|
||||
public function add_plugin_provider(): array {
|
||||
return [
|
||||
'Plugin: enabled (using int)' => [
|
||||
'type' => 'plugintype',
|
||||
@@ -321,7 +321,7 @@ final class helper_test extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function change_default_preset_provider(): array {
|
||||
public function change_default_preset_provider(): array {
|
||||
return [
|
||||
'Starter preset' => [
|
||||
'preset' => 'starter',
|
||||
@@ -364,7 +364,7 @@ final class helper_test extends \advanced_testcase {
|
||||
'preset' => 'unexisting',
|
||||
],
|
||||
'Valid XML file' => [
|
||||
'preset' => self::get_fixture_path(__NAMESPACE__, 'import_settings_plugins.xml'),
|
||||
'preset' => __DIR__ . '/fixtures/import_settings_plugins.xml',
|
||||
'settings' => [
|
||||
'allowemojipicker' => 1,
|
||||
'enableportfolios' => 1,
|
||||
@@ -377,7 +377,7 @@ final class helper_test extends \advanced_testcase {
|
||||
],
|
||||
],
|
||||
'Invalid XML file' => [
|
||||
'preset' => self::get_fixture_path(__NAMESPACE__, 'invalid_xml_file.xml'),
|
||||
'preset' => __DIR__ . '/fixtures/invalid_xml_file.xml',
|
||||
],
|
||||
'Unexisting XML file' => [
|
||||
'preset' => __DIR__ . '/fixtures/unexisting.xml',
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace core_adminpresets\local\setting;
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \core_adminpresets\local\setting\adminpresets_admin_setting_bloglevel
|
||||
*/
|
||||
final class adminpresets_admin_setting_bloglevel_test extends \advanced_testcase {
|
||||
class adminpresets_admin_setting_bloglevel_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test the behaviour of save_value() method.
|
||||
@@ -72,7 +72,7 @@ final class adminpresets_admin_setting_bloglevel_test extends \advanced_testcase
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function save_value_provider(): array {
|
||||
public function save_value_provider(): array {
|
||||
return [
|
||||
'Save the bloglevel and set blog_menu block visibility to true' => [
|
||||
'setttingvalue' => BLOG_USER_LEVEL,
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace core_adminpresets\local\setting;
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \core_adminpresets\local\setting\adminpresets_admin_setting_sitesettext
|
||||
*/
|
||||
final class adminpresets_admin_setting_sitesettext_test extends \advanced_testcase {
|
||||
class adminpresets_admin_setting_sitesettext_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test the behaviour of save_value() method.
|
||||
@@ -66,7 +66,7 @@ final class adminpresets_admin_setting_sitesettext_test extends \advanced_testca
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function save_value_provider(): array {
|
||||
public function save_value_provider(): array {
|
||||
return [
|
||||
'Fullname: different value' => [
|
||||
'settingname' => 'fullname',
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace core_adminpresets\local\setting;
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \core_adminpresets\local\setting\adminpresets_setting
|
||||
*/
|
||||
final class adminpresets_setting_test extends \advanced_testcase {
|
||||
class adminpresets_setting_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test the behaviour of save_value() method.
|
||||
@@ -77,7 +77,7 @@ final class adminpresets_setting_test extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function save_value_provider(): array {
|
||||
public function save_value_provider(): array {
|
||||
return [
|
||||
'Core setting with the same value is not saved' => [
|
||||
'category' => 'optionalsubsystems',
|
||||
@@ -167,7 +167,7 @@ final class adminpresets_setting_test extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function save_attributes_values_provider(): array {
|
||||
public function save_attributes_values_provider(): array {
|
||||
return [
|
||||
'Plugin setting with the same value is not saved' => [
|
||||
'category' => 'modsettinglesson',
|
||||
|
||||
@@ -28,14 +28,13 @@ use stdClass;
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \core_adminpresets\manager
|
||||
*/
|
||||
final class manager_test extends \advanced_testcase {
|
||||
class manager_test extends \advanced_testcase {
|
||||
/**
|
||||
* Include required libraries.
|
||||
*/
|
||||
public static function setUpBeforeClass(): void {
|
||||
global $CFG;
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
parent::setUpBeforeClass();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -368,7 +367,7 @@ final class manager_test extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function export_preset_provider(): array {
|
||||
public function export_preset_provider(): array {
|
||||
return [
|
||||
'Export settings and plugins, excluding sensible' => [
|
||||
'includesensible' => false,
|
||||
@@ -424,7 +423,7 @@ final class manager_test extends \advanced_testcase {
|
||||
* @param string|null $expectedpresetname Expected preset name.
|
||||
*/
|
||||
public function test_import_preset(string $filecontents, bool $expectedpreset, bool $expectedsettings = false,
|
||||
bool $expectedplugins = false, bool $expecteddebugging = false, ?string $expectedexception = null,
|
||||
bool $expectedplugins = false, bool $expecteddebugging = false, string $expectedexception = null,
|
||||
string $expectedpresetname = 'Imported preset'): void {
|
||||
global $DB;
|
||||
|
||||
@@ -531,32 +530,26 @@ final class manager_test extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function import_preset_provider(): array {
|
||||
public function import_preset_provider(): array {
|
||||
return [
|
||||
'Import settings from an empty file' => [
|
||||
'filecontents' => '',
|
||||
'expectedpreset' => false,
|
||||
],
|
||||
'Import settings and plugins from a valid XML file' => [
|
||||
'filecontents' => file_get_contents(
|
||||
filename: self::get_fixture_path(__NAMESPACE__, 'import_settings_plugins.xml')
|
||||
),
|
||||
'filecontents' => file_get_contents(__DIR__ . '/fixtures/import_settings_plugins.xml'),
|
||||
'expectedpreset' => true,
|
||||
'expectedsettings' => true,
|
||||
'expectedplugins' => true,
|
||||
],
|
||||
'Import only settings from a valid XML file' => [
|
||||
'filecontents' => file_get_contents(
|
||||
filename: self::get_fixture_path(__NAMESPACE__, 'import_settings.xml')
|
||||
),
|
||||
'filecontents' => file_get_contents(__DIR__ . '/fixtures/import_settings.xml'),
|
||||
'expectedpreset' => true,
|
||||
'expectedsettings' => true,
|
||||
'expectedplugins' => false,
|
||||
],
|
||||
'Import settings and plugins from a valid XML file with Starter name, which will be marked as non-core' => [
|
||||
'filecontents' => file_get_contents(
|
||||
filename: self::get_fixture_path(__NAMESPACE__, 'import_starter_name.xml')
|
||||
),
|
||||
'filecontents' => file_get_contents(__DIR__ . '/fixtures/import_starter_name.xml'),
|
||||
'expectedpreset' => true,
|
||||
'expectedsettings' => true,
|
||||
'expectedplugins' => true,
|
||||
@@ -565,9 +558,7 @@ final class manager_test extends \advanced_testcase {
|
||||
'expectedpresetname' => 'Starter',
|
||||
],
|
||||
'Import settings from an invalid XML file' => [
|
||||
'filecontents' => file_get_contents(
|
||||
filename: self::get_fixture_path(__NAMESPACE__, 'invalid_xml_file.xml')
|
||||
),
|
||||
'filecontents' => file_get_contents(__DIR__ . '/fixtures/invalid_xml_file.xml'),
|
||||
'expectedpreset' => false,
|
||||
'expectedsettings' => false,
|
||||
'expectedplugins' => false,
|
||||
@@ -575,26 +566,20 @@ final class manager_test extends \advanced_testcase {
|
||||
'expectedexception' => \Exception::class,
|
||||
],
|
||||
'Import unexisting settings category' => [
|
||||
'filecontents' => file_get_contents(
|
||||
filename: self::get_fixture_path(__NAMESPACE__, 'unexisting_category.xml')
|
||||
),
|
||||
'filecontents' => file_get_contents(__DIR__ . '/fixtures/unexisting_category.xml'),
|
||||
'expectedpreset' => false,
|
||||
'expectedsettings' => false,
|
||||
'expectedplugins' => false,
|
||||
],
|
||||
'Import unexisting setting' => [
|
||||
'filecontents' => file_get_contents(
|
||||
filename: self::get_fixture_path(__NAMESPACE__, 'unexisting_setting.xml')
|
||||
),
|
||||
'filecontents' => file_get_contents(__DIR__ . '/fixtures/unexisting_setting.xml'),
|
||||
'expectedpreset' => false,
|
||||
'expectedsettings' => false,
|
||||
'expectedplugins' => false,
|
||||
'expecteddebugging' => true,
|
||||
],
|
||||
'Import valid settings with one unexisting setting too' => [
|
||||
'filecontents' => file_get_contents(
|
||||
filename: self::get_fixture_path(__NAMESPACE__, 'import_settings_with_unexisting_setting.xml'),
|
||||
),
|
||||
'filecontents' => file_get_contents(__DIR__ . '/fixtures/import_settings_with_unexisting_setting.xml'),
|
||||
'expectedpreset' => true,
|
||||
'expectedsettings' => false,
|
||||
'expectedplugins' => false,
|
||||
@@ -603,6 +588,7 @@ final class manager_test extends \advanced_testcase {
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test the behaviour of delete_preset() method when the preset id doesn't exist.
|
||||
*
|
||||
|
||||
@@ -30,7 +30,7 @@ use core_privacy\tests\provider_testcase;
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \core_adminpresets\privacy\provider
|
||||
*/
|
||||
final class privacy_provider_test extends provider_testcase {
|
||||
class privacy_provider_test extends provider_testcase {
|
||||
|
||||
/**
|
||||
* Test for provider::get_metadata().
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('IGNORE_COMPONENT_CACHE', true);
|
||||
|
||||
require_once('../config.php');
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
|
||||
|
||||
@@ -34,10 +34,8 @@ admin_externalpage_setup('registrationmoodleorg');
|
||||
|
||||
$unregistration = optional_param('unregistration', false, PARAM_BOOL);
|
||||
$confirm = optional_param('confirm', false, PARAM_BOOL);
|
||||
// Consider the site 'registered' if records exist locally and at the hub.
|
||||
$siteisregistered = \core\hub\registration::is_registered() && core\hub\api::is_site_registered_in_hub();
|
||||
|
||||
if ($unregistration && $siteisregistered) {
|
||||
if ($unregistration && \core\hub\registration::is_registered()) {
|
||||
if ($confirm) {
|
||||
require_sesskey();
|
||||
\core\hub\registration::unregister(false, false);
|
||||
@@ -62,7 +60,7 @@ if (!$returnurl = optional_param('returnurl', null, PARAM_LOCALURL)) {
|
||||
$returnurl = $isinitialregistration ? '/admin/index.php' : '/admin/registration/index.php';
|
||||
}
|
||||
|
||||
$siteregistrationform = new \core\hub\site_registration_form(null, ['registered' => $siteisregistered]);
|
||||
$siteregistrationform = new \core\hub\site_registration_form();
|
||||
$siteregistrationform->set_data(['returnurl' => $returnurl]);
|
||||
if ($fromform = $siteregistrationform->get_data()) {
|
||||
|
||||
@@ -88,7 +86,7 @@ echo $OUTPUT->header();
|
||||
// Current status of registration.
|
||||
|
||||
$notificationtype = \core\output\notification::NOTIFY_ERROR;
|
||||
if ($siteisregistered) {
|
||||
if (\core\hub\registration::is_registered()) {
|
||||
$lastupdated = \core\hub\registration::get_last_updated();
|
||||
if ($lastupdated == 0) {
|
||||
$registrationmessage = get_string('pleaserefreshregistrationunknown', 'admin');
|
||||
@@ -106,7 +104,7 @@ if ($siteisregistered) {
|
||||
}
|
||||
|
||||
// Heading.
|
||||
if ($siteisregistered) {
|
||||
if (\core\hub\registration::is_registered()) {
|
||||
echo $OUTPUT->heading(get_string('registerwithmoodleorgupdate', 'core_hub'));
|
||||
} else if ($isinitialregistration) {
|
||||
echo $OUTPUT->heading(get_string('registerwithmoodleorgcomplete', 'core_hub'));
|
||||
@@ -119,7 +117,7 @@ echo $renderer->moodleorg_registration_message();
|
||||
|
||||
$siteregistrationform->display();
|
||||
|
||||
if ($siteisregistered) {
|
||||
if (\core\hub\registration::is_registered()) {
|
||||
// Unregister link.
|
||||
$unregisterhuburl = new moodle_url("/admin/registration/index.php", ['unregistration' => 1]);
|
||||
echo html_writer::div(html_writer::link($unregisterhuburl, get_string('unregister', 'hub')), 'unregister mt-2');
|
||||
@@ -127,7 +125,4 @@ if ($siteisregistered) {
|
||||
echo html_writer::div(html_writer::link(new moodle_url($returnurl), get_string('skipregistration', 'hub')),
|
||||
'skipregistration mt-2');
|
||||
}
|
||||
|
||||
$PAGE->requires->js_call_amd('core_admin/expand_hash', 'init');
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
+76
-82
@@ -295,7 +295,6 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
|
||||
$output .= $this->header();
|
||||
$output .= $this->output->heading(get_string('notifications', 'admin'));
|
||||
$output .= $this->upgrade_news_message();
|
||||
$output .= $this->maturity_info($maturity);
|
||||
$output .= empty($CFG->disableupdatenotifications) ? $this->available_updates($availableupdates, $availableupdatesfetch) : '';
|
||||
$output .= $this->insecure_dataroot_warning($insecuredataroot);
|
||||
@@ -755,22 +754,6 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
return $this->box($copyrighttext, 'copyright');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a transient notification for important upgrades messages for
|
||||
* specific releases.
|
||||
*
|
||||
* @return string HTML to output.
|
||||
*/
|
||||
protected function upgrade_news_message() {
|
||||
return $this->notification(
|
||||
get_string('importantupdates_content', 'admin'),
|
||||
'info',
|
||||
false,
|
||||
get_string('importantupdates_title', 'admin'),
|
||||
'i/circleinfo, core'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a warning about installing development code if necesary.
|
||||
* @param int $maturity
|
||||
@@ -860,14 +843,14 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
$registerbutton = $this->single_button(new moodle_url('/admin/registration/index.php'),
|
||||
get_string('register', 'admin'));
|
||||
$str = 'registrationwarning';
|
||||
$type = 'error alert alert-danger';
|
||||
} else {
|
||||
$registerbutton = '';
|
||||
$str = 'registrationwarningcontactadmin';
|
||||
$type = 'info';
|
||||
}
|
||||
|
||||
return $this->warning( get_string($str, 'admin') . ' ' . $registerbutton , $type);
|
||||
return $this->warning( get_string($str, 'admin')
|
||||
. ' ' . $this->help_icon('registration', 'admin') . $registerbutton ,
|
||||
'error alert alert-danger');
|
||||
}
|
||||
|
||||
return '';
|
||||
@@ -891,8 +874,8 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
protected function mobile_configuration_warning($mobileconfigured) {
|
||||
$output = '';
|
||||
if (!$mobileconfigured) {
|
||||
$settingslink = new moodle_url('/admin/search.php', ['query' => 'enablemobilewebservice']);
|
||||
$configurebutton = $this->single_button($settingslink, get_string('enablemobilewebservice', 'admin'), 'get');
|
||||
$settingslink = new moodle_url('/admin/settings.php', ['section' => 'mobilesettings']);
|
||||
$configurebutton = $this->single_button($settingslink, get_string('enablemobilewebservice', 'admin'));
|
||||
$output .= $this->warning(get_string('mobilenotconfiguredwarning', 'admin') . ' ' . $configurebutton);
|
||||
}
|
||||
|
||||
@@ -1021,7 +1004,7 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
function upgrade_reload($url) {
|
||||
return html_writer::empty_tag('br') .
|
||||
html_writer::tag('div',
|
||||
html_writer::link($url, $this->pix_icon('i/reload', '', '', ['class' => 'icon']) .
|
||||
html_writer::link($url, $this->pix_icon('i/reload', '', '', array('class' => 'icon icon-pre')) .
|
||||
get_string('reload'), array('title' => get_string('reload'))),
|
||||
array('class' => 'continuebutton')) . html_writer::empty_tag('br');
|
||||
}
|
||||
@@ -1258,7 +1241,7 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
new moodle_url($this->page->url, array('installupdatex' => 1)),
|
||||
get_string('updateavailableinstallall', 'core_admin', count($installableupdates)),
|
||||
'post',
|
||||
array('class' => 'singlebutton updateavailableinstallall me-1')
|
||||
array('class' => 'singlebutton updateavailableinstallall mr-1')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1267,7 +1250,7 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
new moodle_url($this->page->url, array('abortinstallx' => 1, 'confirmplugincheck' => 0)),
|
||||
get_string('cancelinstallall', 'core_plugin', count($installabortable)),
|
||||
'post',
|
||||
array('class' => 'singlebutton cancelinstallall me-1')
|
||||
array('class' => 'singlebutton cancelinstallall mr-1')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1276,17 +1259,17 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
new moodle_url($this->page->url, array('abortupgradex' => 1)),
|
||||
get_string('cancelupgradeall', 'core_plugin', count($upgradeabortable)),
|
||||
'post',
|
||||
array('class' => 'singlebutton cancelupgradeall me-1')
|
||||
array('class' => 'singlebutton cancelupgradeall mr-1')
|
||||
);
|
||||
}
|
||||
|
||||
$out .= html_writer::div(html_writer::link(new moodle_url($this->page->url, array('showallplugins' => 0)),
|
||||
get_string('plugincheckattention', 'core_plugin')).' '.html_writer::span($sumattention, 'badge bg-light text-dark'),
|
||||
'btn btn-link me-1');
|
||||
'btn btn-link mr-1');
|
||||
|
||||
$out .= html_writer::div(html_writer::link(new moodle_url($this->page->url, array('showallplugins' => 1)),
|
||||
get_string('plugincheckall', 'core_plugin')).' '.html_writer::span($sumtotal, 'badge bg-light text-dark'),
|
||||
'btn btn-link me-1');
|
||||
'btn btn-link mr-1');
|
||||
|
||||
$out .= $this->output->container_end(); // End of .actions container.
|
||||
$out .= $this->output->container_end(); // End of #plugins-check-info container.
|
||||
@@ -1305,7 +1288,7 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
* @param null|moodle_url $cancel URL for the cancel link, defaults to the current page
|
||||
* @return string HTML
|
||||
*/
|
||||
public function plugins_management_confirm_buttons(?moodle_url $continue=null, ?moodle_url $cancel=null) {
|
||||
public function plugins_management_confirm_buttons(moodle_url $continue=null, moodle_url $cancel=null) {
|
||||
|
||||
$out = html_writer::start_div('plugins-management-confirm-buttons');
|
||||
|
||||
@@ -1393,13 +1376,13 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
new moodle_url($this->page->url, array('installdepx' => 1)),
|
||||
get_string('dependencyinstallmissing', 'core_plugin', count($installable)),
|
||||
'post',
|
||||
array('class' => 'singlebutton dependencyinstallmissing d-inline-block me-1')
|
||||
array('class' => 'singlebutton dependencyinstallmissing d-inline-block mr-1')
|
||||
);
|
||||
}
|
||||
|
||||
$out .= html_writer::div(html_writer::link(new moodle_url('/admin/tool/installaddon/'),
|
||||
get_string('dependencyuploadmissing', 'core_plugin'), array('class' => 'btn btn-link')),
|
||||
'dependencyuploadmissing d-inline-block me-1');
|
||||
'dependencyuploadmissing d-inline-block mr-1');
|
||||
|
||||
$out .= $this->output->container_end(); // End of .plugins-check-dependencies-actions container.
|
||||
|
||||
@@ -1465,29 +1448,29 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
$info .= html_writer::div(
|
||||
html_writer::link('https://moodle.org/plugins/view.php?plugin='.$plugin->component,
|
||||
get_string('misdepinfoplugin', 'core_plugin')),
|
||||
'misdepinfoplugin d-inline-block me-3 mb-1'
|
||||
'misdepinfoplugin d-inline-block mr-3 mb-1'
|
||||
);
|
||||
|
||||
$info .= html_writer::div(
|
||||
html_writer::link('https://moodle.org/plugins/pluginversion.php?id='.$plugin->version->id,
|
||||
get_string('misdepinfoversion', 'core_plugin')),
|
||||
'misdepinfoversion d-inline-block me-3 mb-1'
|
||||
'misdepinfoversion d-inline-block mr-3 mb-1'
|
||||
);
|
||||
|
||||
$info .= html_writer::div(html_writer::link($plugin->version->downloadurl, get_string('download')),
|
||||
'misdepdownload d-inline-block me-3 mb-1');
|
||||
'misdepdownload d-inline-block mr-3 mb-1');
|
||||
|
||||
if ($pluginman->is_remote_plugin_installable($plugin->component, $plugin->version->version, $reason)) {
|
||||
$info .= $this->output->single_button(
|
||||
new moodle_url($this->page->url, array('installdep' => $plugin->component)),
|
||||
get_string('dependencyinstall', 'core_plugin'),
|
||||
'post',
|
||||
array('class' => 'singlebutton dependencyinstall me-3 mb-1')
|
||||
array('class' => 'singlebutton dependencyinstall mr-3 mb-1')
|
||||
);
|
||||
} else {
|
||||
$reasonhelp = $this->info_remote_plugin_not_installable($reason);
|
||||
if ($reasonhelp) {
|
||||
$info .= html_writer::div($reasonhelp, 'reasonhelp dependencyinstall d-inline-block me-3 mb-1');
|
||||
$info .= html_writer::div($reasonhelp, 'reasonhelp dependencyinstall d-inline-block mr-3 mb-1');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1673,9 +1656,8 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
public function plugins_overview_panel(core_plugin_manager $pluginman, array $options = array()) {
|
||||
|
||||
$plugininfo = $pluginman->get_plugins();
|
||||
$this->page->requires->js_call_amd('core_admin/plugins_overview', 'init');
|
||||
|
||||
$numtotal = $numextension = $numupdatable = $numinstallable = $nummissing = $numnew = 0;
|
||||
$numtotal = $numextension = $numupdatable = $numinstallable = 0;
|
||||
|
||||
foreach ($plugininfo as $type => $plugins) {
|
||||
foreach ($plugins as $name => $plugin) {
|
||||
@@ -1689,12 +1671,8 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
}
|
||||
}
|
||||
if ($plugin->get_status() === core_plugin_manager::PLUGIN_STATUS_MISSING) {
|
||||
$nummissing++;
|
||||
continue;
|
||||
}
|
||||
if ($plugin->get_status() === core_plugin_manager::PLUGIN_STATUS_NEW) {
|
||||
$numnew++;
|
||||
}
|
||||
$numtotal++;
|
||||
if (!$plugin->is_standard()) {
|
||||
$numextension++;
|
||||
@@ -1703,50 +1681,36 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
}
|
||||
|
||||
$infoall = html_writer::link(
|
||||
$this->page->url,
|
||||
new moodle_url($this->page->url, array('contribonly' => 0, 'updatesonly' => 0)),
|
||||
get_string('overviewall', 'core_plugin'),
|
||||
['title' => get_string('filterall', 'core_plugin'), 'data-filterby' => 'all', 'class' => 'active']
|
||||
array('title' => get_string('filterall', 'core_plugin'))
|
||||
).' '.html_writer::span($numtotal, 'badge number number-all');
|
||||
|
||||
$infoext = html_writer::link(
|
||||
new moodle_url($this->page->url, [], 'additional'),
|
||||
new moodle_url($this->page->url, array('contribonly' => 1, 'updatesonly' => 0)),
|
||||
get_string('overviewext', 'core_plugin'),
|
||||
['title' => get_string('filtercontribonly', 'core_plugin'), 'data-filterby' => 'additional']
|
||||
array('title' => get_string('filtercontribonly', 'core_plugin'))
|
||||
).' '.html_writer::span($numextension, 'badge number number-additional');
|
||||
|
||||
if ($numupdatable) {
|
||||
$infoupdatable = html_writer::link(
|
||||
new moodle_url($this->page->url, [], 'updatable'),
|
||||
new moodle_url($this->page->url, array('contribonly' => 0, 'updatesonly' => 1)),
|
||||
get_string('overviewupdatable', 'core_plugin'),
|
||||
['title' => get_string('filterupdatesonly', 'core_plugin'), 'data-filterby' => 'updatable']
|
||||
array('title' => get_string('filterupdatesonly', 'core_plugin'))
|
||||
).' '.html_writer::span($numupdatable, 'badge bg-info text-white number number-updatable');
|
||||
} else {
|
||||
// No updates, or the notifications disabled.
|
||||
$infoupdatable = '';
|
||||
}
|
||||
|
||||
if ($numnew) {
|
||||
$infonew = html_writer::link(
|
||||
new moodle_url($this->page->url, [], 'newplugin'),
|
||||
get_string('status_new', 'plugin'),
|
||||
['title' => get_string('filternewpluginsonly', 'core_plugin'), 'data-filterby' => 'newplugin']
|
||||
).' '.html_writer::span($numnew, 'badge bg-success text-white number number-newplugin');
|
||||
} else {
|
||||
$infonew = '';
|
||||
}
|
||||
$out = html_writer::start_div('', array('id' => 'plugins-overview-panel'));
|
||||
|
||||
if ($nummissing) {
|
||||
$infomissing = html_writer::link(
|
||||
new moodle_url($this->page->url, [], 'missing'),
|
||||
get_string('status_missing', 'plugin'),
|
||||
['title' => get_string('filtermissingonly', 'core_plugin'), 'data-filterby' => 'missing']
|
||||
).' '.html_writer::span($nummissing, 'badge bg-danger text-white number number-missing');
|
||||
} else {
|
||||
$infomissing = '';
|
||||
if (!empty($options['updatesonly'])) {
|
||||
$out .= $this->output->heading(get_string('overviewupdatable', 'core_plugin'), 3);
|
||||
} else if (!empty($options['contribonly'])) {
|
||||
$out .= $this->output->heading(get_string('overviewext', 'core_plugin'), 3);
|
||||
}
|
||||
|
||||
$out = html_writer::start_div('', ['id' => 'plugins-overview-panel']);
|
||||
|
||||
if ($numinstallable) {
|
||||
$out .= $this->output->single_button(
|
||||
new moodle_url($this->page->url, array('installupdatex' => 1)),
|
||||
@@ -1757,10 +1721,8 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
}
|
||||
|
||||
$out .= html_writer::div($infoall, 'info info-all').
|
||||
html_writer::div($infoext, 'info info-additional').
|
||||
html_writer::div($infoupdatable, 'info info-updatable').
|
||||
html_writer::div($infonew, 'info info-newplugin').
|
||||
html_writer::div($infomissing, 'info info-missing');
|
||||
html_writer::div($infoext, 'info info-ext').
|
||||
html_writer::div($infoupdatable, 'info info-updatable');
|
||||
|
||||
$out .= html_writer::end_div(); // End of #plugins-overview-panel block.
|
||||
|
||||
@@ -1780,6 +1742,38 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
|
||||
$plugininfo = $pluginman->get_plugins();
|
||||
|
||||
// Filter the list of plugins according the options.
|
||||
if (!empty($options['updatesonly'])) {
|
||||
$updateable = array();
|
||||
foreach ($plugininfo as $plugintype => $pluginnames) {
|
||||
foreach ($pluginnames as $pluginname => $pluginfo) {
|
||||
$pluginavailableupdates = $pluginfo->available_updates();
|
||||
if (!empty($pluginavailableupdates)) {
|
||||
foreach ($pluginavailableupdates as $pluginavailableupdate) {
|
||||
$updateable[$plugintype][$pluginname] = $pluginfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$plugininfo = $updateable;
|
||||
}
|
||||
|
||||
if (!empty($options['contribonly'])) {
|
||||
$contribs = array();
|
||||
foreach ($plugininfo as $plugintype => $pluginnames) {
|
||||
foreach ($pluginnames as $pluginname => $pluginfo) {
|
||||
if (!$pluginfo->is_standard()) {
|
||||
$contribs[$plugintype][$pluginname] = $pluginfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
$plugininfo = $contribs;
|
||||
}
|
||||
|
||||
if (empty($plugininfo)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$table = new html_table();
|
||||
$table->id = 'plugins-control-panel';
|
||||
$table->head = array(
|
||||
@@ -1877,15 +1871,13 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
$row->attributes['class'] .= ' standard';
|
||||
$source = '';
|
||||
} else {
|
||||
$row->attributes['class'] .= ' additional';
|
||||
$source = html_writer::div(get_string('sourceext', 'core_plugin'), 'source badge mr-1 bg-info text-white');
|
||||
$row->attributes['class'] .= ' extension';
|
||||
$source = html_writer::div(get_string('sourceext', 'core_plugin'), 'source badge bg-info text-white');
|
||||
}
|
||||
|
||||
if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
|
||||
$row->attributes['class'] .= ' missing';
|
||||
$msg = html_writer::div(get_string('status_missing', 'core_plugin'), 'statusmsg badge bg-danger text-white');
|
||||
} else if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
|
||||
$row->attributes['class'] .= ' newplugin';
|
||||
$msg = html_writer::div(get_string('status_new', 'core_plugin'), 'statusmsg badge bg-success text-white');
|
||||
} else {
|
||||
$msg = '';
|
||||
@@ -1901,7 +1893,6 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
|
||||
$updateinfo = '';
|
||||
if (is_array($plugin->available_updates())) {
|
||||
$row->attributes['class'] .= ' updatable';
|
||||
foreach ($plugin->available_updates() as $availableupdate) {
|
||||
$updateinfo .= $this->plugin_available_update_info($pluginman, $availableupdate);
|
||||
}
|
||||
@@ -2189,11 +2180,10 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
|
||||
$output = '';
|
||||
$output .= $this->header();
|
||||
$output .= $this->container_start('upgradekeyreq');
|
||||
$output .= $this->heading(get_string('upgradekeyreq', 'core_admin'));
|
||||
$output .= $this->container_start('upgradekeyreq w-25');
|
||||
$output .= html_writer::start_tag('form', array('method' => 'POST', 'action' => $url));
|
||||
$output .= html_writer::empty_tag('input', [
|
||||
'id' => 'upgradekey',
|
||||
'name' => 'upgradekey',
|
||||
'type' => 'password',
|
||||
'class' => 'form-control w-auto',
|
||||
@@ -2216,10 +2206,14 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
* @return string
|
||||
*/
|
||||
public function moodleorg_registration_message() {
|
||||
$a = new stdClass();
|
||||
$a->moreinformation = '#id_sitestats'; // More information anchor.
|
||||
$a->moodleapp = HUB_MOODLEORGHUBURL . '/solutions/moodle-app/';
|
||||
$out = format_text(get_string('registerwithmoodleorginfo', 'core_hub', $a), FORMAT_MARKDOWN);
|
||||
|
||||
$out = format_text(get_string('registerwithmoodleorginfo', 'core_hub'), FORMAT_MARKDOWN);
|
||||
|
||||
$out .= html_writer::link(
|
||||
MOODLE_PRODUCTURL.'/solutions/moodle-app/',
|
||||
$this->output->pix_icon('i/info', '').' '.get_string('registerwithmoodleorginfoapp', 'core_hub'),
|
||||
['class' => 'btn btn-link', 'role' => 'opener', 'target' => '_href']
|
||||
);
|
||||
|
||||
$out .= html_writer::link(
|
||||
HUB_MOODLEORGHUBURL,
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# core_role (subsystem) Upgrade notes
|
||||
|
||||
## 4.5
|
||||
|
||||
### Added
|
||||
|
||||
- All session management has been moved to the `\core\session\manager` class.
|
||||
This removes the dependancy to use the `sessions` table.
|
||||
|
||||
Session management plugins (like Redis) should now inherit
|
||||
the base `\core\session\handler` class, which implements
|
||||
`SessionHandlerInterface`, and override methods as required.
|
||||
|
||||
The following methods in `\core\session\manager` have been deprecated:
|
||||
| Old method name | New method name |
|
||||
| --- | --- |
|
||||
| `kill_all_sessions` | `destroy_all` |
|
||||
| `kill_session` | `destroy` |
|
||||
| `kill_sessions_for_auth_plugin` | `destroy_by_auth_plugin` |
|
||||
| `kill_user_sessions` | `destroy_user_sessions` |
|
||||
|
||||
For more information see [MDL-66151](https://tracker.moodle.org/browse/MDL-66151)
|
||||
@@ -655,7 +655,7 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
echo "</label>\n";
|
||||
}
|
||||
if ($helpicon) {
|
||||
echo '<span class="float-sm-end text-nowrap">'.$helpicon.'</span>';
|
||||
echo '<span class="float-sm-right text-nowrap">'.$helpicon.'</span>';
|
||||
}
|
||||
echo '</div>';
|
||||
if (isset($this->errors[$name])) {
|
||||
|
||||
@@ -18,12 +18,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace core_role\reportbuilder\local\entities;
|
||||
|
||||
use core\{context, context_helper};
|
||||
use core\lang_string;
|
||||
use context;
|
||||
use context_helper;
|
||||
use lang_string;
|
||||
use stdClass;
|
||||
use core_reportbuilder\local\entities\base;
|
||||
use core_reportbuilder\local\filters\select;
|
||||
use core_reportbuilder\local\report\{column, filter};
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Role entity
|
||||
@@ -95,6 +96,7 @@ class role extends base {
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_fields("{$rolealias}.name, {$rolealias}.shortname, {$rolealias}.id, {$contextalias}.id AS contextid")
|
||||
->add_fields(context_helper::get_preload_record_columns_sql($contextalias))
|
||||
// The sorting is on name, unless empty (determined by single space - thanks Oracle) then we use shortname.
|
||||
@@ -104,7 +106,7 @@ class role extends base {
|
||||
ELSE {$rolealias}.name
|
||||
END",
|
||||
])
|
||||
->add_callback(static function(?string $name, stdClass $role): string {
|
||||
->set_callback(static function($name, stdClass $role): string {
|
||||
if ($name === null) {
|
||||
return '';
|
||||
}
|
||||
@@ -122,6 +124,7 @@ class role extends base {
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_fields("{$rolealias}.name, {$rolealias}.shortname")
|
||||
// The sorting is on name, unless empty (determined by single space - thanks Oracle) then we use shortname.
|
||||
->set_is_sortable(true, [
|
||||
@@ -130,9 +133,12 @@ class role extends base {
|
||||
ELSE {$rolealias}.name
|
||||
END",
|
||||
])
|
||||
->add_callback(fn(?string $name, stdClass $role) => match ($name) {
|
||||
null => '',
|
||||
default => role_get_name($role, null, ROLENAME_ORIGINAL),
|
||||
->set_callback(static function($name, stdClass $role): string {
|
||||
if ($name === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return role_get_name($role, null, ROLENAME_ORIGINAL);
|
||||
});
|
||||
|
||||
// Short name column.
|
||||
@@ -142,22 +148,8 @@ class role extends base {
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->add_field("{$rolealias}.shortname")
|
||||
->set_is_sortable(true);
|
||||
|
||||
// Archetype column.
|
||||
$columns[] = (new column(
|
||||
'archetype',
|
||||
new lang_string('archetype', 'core_role'),
|
||||
$this->get_entity_name(),
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->add_field("{$rolealias}.archetype")
|
||||
->add_callback(fn(?string $archetype) => match ($archetype) {
|
||||
null => '',
|
||||
'' => get_string('none'),
|
||||
default => get_string("archetype{$archetype}", 'core_role'),
|
||||
})
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_fields("{$rolealias}.shortname")
|
||||
->set_is_sortable(true);
|
||||
|
||||
// Description column.
|
||||
@@ -174,9 +166,12 @@ class role extends base {
|
||||
->set_type(column::TYPE_LONGTEXT)
|
||||
->add_field($descriptionfieldsql, 'description')
|
||||
->add_field("{$rolealias}.shortname")
|
||||
->add_callback(fn(?string $description, stdClass $role) => match ($description) {
|
||||
null => '',
|
||||
default => role_get_description($role),
|
||||
->set_callback(static function($description, stdClass $role): string {
|
||||
if ($description === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return role_get_description($role);
|
||||
});
|
||||
|
||||
return $columns;
|
||||
@@ -203,22 +198,6 @@ class role extends base {
|
||||
return role_get_names(null, ROLENAME_ORIGINAL, true);
|
||||
});
|
||||
|
||||
// Archetype filter.
|
||||
$filters[] = (new filter(
|
||||
select::class,
|
||||
'archetype',
|
||||
new lang_string('archetype', 'core_role'),
|
||||
$this->get_entity_name(),
|
||||
"{$rolealias}.archetype",
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_options_callback(static function(): array {
|
||||
return array_map(
|
||||
fn(string $archetype) => get_string("archetype{$archetype}", 'core_role'),
|
||||
get_role_archetypes(),
|
||||
);
|
||||
});
|
||||
|
||||
return $filters;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ class role_assignment extends base {
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_field("{$raalias}.component")
|
||||
->set_is_sortable(true);
|
||||
|
||||
@@ -112,6 +113,7 @@ class role_assignment extends base {
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_INTEGER)
|
||||
->add_field("{$raalias}.itemid")
|
||||
->set_is_sortable(true);
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ if (!empty($capabilities)) {
|
||||
$attrs = array('type'=>'submit', 'name'=>'savechanges', 'value'=>get_string('savechanges'), 'class'=>'btn btn-primary');
|
||||
echo html_writer::empty_tag('input', $attrs);
|
||||
$attrs = array('type' => 'submit', 'name' => 'cancel', 'value' => get_string('cancel'),
|
||||
'class' => 'btn btn-secondary ms-1');
|
||||
'class' => 'btn btn-secondary ml-1');
|
||||
echo html_writer::empty_tag('input', $attrs);
|
||||
echo html_writer::end_tag('div');
|
||||
echo html_writer::end_tag('div');
|
||||
|
||||
@@ -26,7 +26,7 @@ use core_role_preset;
|
||||
* @copyright 2013 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
final class preset_test extends \advanced_testcase {
|
||||
class preset_test extends \advanced_testcase {
|
||||
public function test_xml(): void {
|
||||
global $DB;
|
||||
|
||||
@@ -84,7 +84,7 @@ final class preset_test extends \advanced_testcase {
|
||||
public function test_mixed_levels(): void {
|
||||
// The problem here is that we cannot guarantee plugin contexts
|
||||
// have unique short names, so we have to also support level numbers.
|
||||
$xml = file_get_contents(self::get_fixture_path(__NAMESPACE__, 'mixed_levels.xml'));
|
||||
$xml = file_get_contents(__DIR__ . '/fixtures/mixed_levels.xml');
|
||||
$this->assertTrue(\core_role_preset::is_valid_preset($xml));
|
||||
|
||||
$preset = \core_role_preset::parse_preset($xml);
|
||||
|
||||
@@ -40,7 +40,7 @@ use core_privacy\local\request\approved_userlist;
|
||||
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
final class provider_test extends provider_testcase {
|
||||
class provider_test extends provider_testcase {
|
||||
/**
|
||||
* Test to check export_user_preferences.
|
||||
* returns user preferences data.
|
||||
|
||||
@@ -20,14 +20,19 @@ namespace core_role\reportbuilder\datasource;
|
||||
|
||||
use core\context\course;
|
||||
use core_reportbuilder_generator;
|
||||
use core_reportbuilder_testcase;
|
||||
use core_reportbuilder\local\filters\{date, select, text};
|
||||
use core_reportbuilder\tests\core_reportbuilder_testcase;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php");
|
||||
|
||||
/**
|
||||
* Unit tests for roles datasource
|
||||
*
|
||||
* @package core_role
|
||||
* @covers \core_role\reportbuilder\datasource\roles
|
||||
* @covers \core_role\reportbuilder\datasource\roles;
|
||||
* @copyright 2024 Paul Holden <paulh@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@@ -99,7 +104,6 @@ final class roles_test extends core_reportbuilder_testcase {
|
||||
// Role.
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'role:name']);
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'role:shortname']);
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'role:archetype']);
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'role:description']);
|
||||
|
||||
// Role assignment.
|
||||
@@ -110,20 +114,11 @@ final class roles_test extends core_reportbuilder_testcase {
|
||||
$content = $this->get_custom_report_content($report->get('id'));
|
||||
$this->assertCount(1, $content);
|
||||
|
||||
[
|
||||
$rolename,
|
||||
$roleshortname,
|
||||
$rolearchetype,
|
||||
$roledescription,
|
||||
$timemodified,
|
||||
$component,
|
||||
$itemid,
|
||||
] = array_values($content[0]);
|
||||
[$rolename, $roleshortname, $roledescription, $timemodified, $component, $itemid] = array_values($content[0]);
|
||||
|
||||
// Role.
|
||||
$this->assertEquals('Moocher (Manager)', $rolename);
|
||||
$this->assertEquals('manager', $roleshortname);
|
||||
$this->assertEquals('ARCHETYPE: Manager', $rolearchetype);
|
||||
$this->assertEquals('Managers can access courses and modify them, but usually do not participate in them.',
|
||||
$roledescription);
|
||||
|
||||
@@ -151,14 +146,6 @@ final class roles_test extends core_reportbuilder_testcase {
|
||||
'role:name_operator' => select::EQUAL_TO,
|
||||
'role:name_value' => -1,
|
||||
], false],
|
||||
'Filter role archetype' => ['role:archetype', [
|
||||
'role:archetype_operator' => select::EQUAL_TO,
|
||||
'role:archetype_value' => 'student',
|
||||
], true],
|
||||
'Filter role archetype (no match)' => ['role:archetype', [
|
||||
'role:archetype_operator' => select::EQUAL_TO,
|
||||
'role:archetype_value' => 'teacher',
|
||||
], false],
|
||||
|
||||
// Role assignment.
|
||||
'Filter role assignment time modified' => ['role_assignment:timemodified', [
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Adds settings links to admin tree.
|
||||
*
|
||||
* AI gets top billing in general because it's the future.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2024 Matt Porritt <matt.porritt@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
if ($hassiteconfig) {
|
||||
// Add settings page for AI provider settings.
|
||||
$providers = new admin_settingpage('aiprovider', new lang_string('aiproviders', 'ai'));
|
||||
$providers->add(new admin_setting_heading('availableproviders',
|
||||
get_string('availableproviders', 'core_ai'),
|
||||
get_string('availableproviders_desc', 'core_ai')));
|
||||
$providers->add(new \core_admin\admin\admin_setting_plugin_manager(
|
||||
'aiprovider',
|
||||
\core_ai\table\aiprovider_management_table::class,
|
||||
'manageaiproviders',
|
||||
new lang_string('manageaiproviders', 'core_ai'),
|
||||
));
|
||||
$ADMIN->add('ai', $providers);
|
||||
|
||||
// Load settings for all AI providers.
|
||||
$plugins = core_plugin_manager::instance()->get_plugins_of_type('aiprovider');
|
||||
foreach ($plugins as $plugin) {
|
||||
/** @var \core\plugininfo\aiprovider $plugin */
|
||||
$plugin->load_settings($ADMIN, 'ai', $hassiteconfig);
|
||||
}
|
||||
|
||||
// Add settings page for AI placement settings.
|
||||
$placements = new admin_settingpage('aiplacement', new lang_string('aiplacements', 'ai'));
|
||||
$placements->add(new admin_setting_heading('availableplacements',
|
||||
get_string('availableplacements', 'core_ai'),
|
||||
get_string('availableplacements_desc', 'core_ai')));
|
||||
$placements->add(new \core_admin\admin\admin_setting_plugin_manager(
|
||||
'aiplacement',
|
||||
\core_ai\table\aiplacement_management_table::class,
|
||||
'manageaiplacements',
|
||||
new lang_string('manageaiplacements', 'core_ai'),
|
||||
));
|
||||
$ADMIN->add('ai', $placements);
|
||||
|
||||
// Load settings for all placements.
|
||||
$plugins = core_plugin_manager::instance()->get_plugins_of_type('aiplacement');
|
||||
foreach ($plugins as $plugin) {
|
||||
/** @var \core\plugininfo\aiprovider $plugin */
|
||||
$plugin->load_settings($ADMIN, 'ai', $hassiteconfig);
|
||||
}
|
||||
}
|
||||
@@ -152,12 +152,6 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) { // sp
|
||||
}
|
||||
$choices[HOMEPAGE_MYCOURSES] = new lang_string('mycourses', 'admin');
|
||||
$choices[HOMEPAGE_USER] = new lang_string('userpreference', 'admin');
|
||||
|
||||
// Allow hook callbacks to extend options.
|
||||
$hook = new \core_user\hook\extend_default_homepage();
|
||||
\core\di::get(\core\hook\manager::class)->dispatch($hook);
|
||||
$choices += $hook->get_options();
|
||||
|
||||
$temp->add(new admin_setting_configselect('defaulthomepage', new lang_string('defaulthomepage', 'admin'),
|
||||
new lang_string('configdefaulthomepage', 'admin'), get_default_home_page(), $choices));
|
||||
if (!isset($CFG->enabledashboard) || $CFG->enabledashboard) {
|
||||
|
||||
@@ -87,7 +87,7 @@ if (($hassiteconfig || has_any_capability(array(
|
||||
$ADMIN->add('badges',
|
||||
new admin_externalpage('newbadge',
|
||||
new lang_string('newbadge', 'badges'),
|
||||
new moodle_url('/badges/edit.php', ['action' => 'new']),
|
||||
new moodle_url('/badges/newbadge.php', array('type' => BADGE_TYPE_SITE)),
|
||||
array('moodle/badges:createbadge'), empty($CFG->enablebadges)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -420,12 +420,6 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
|
||||
new lang_string('generalgroups', 'backup'), new lang_string('configgeneralgroups', 'backup'),
|
||||
array('value' => 1, 'locked' => 0)));
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_general_competencies', new lang_string('generalcompetencies','backup'), new lang_string('configgeneralcompetencies','backup'), array('value'=>1, 'locked'=>0)));
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock(
|
||||
'backup/backup_general_customfield',
|
||||
new lang_string('generalcustomfield', 'backup'),
|
||||
new lang_string('configgeneralcustomfield', 'backup'),
|
||||
['value' => 1, 'locked' => 0],
|
||||
));
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_general_contentbankcontent',
|
||||
new lang_string('generalcontentbankcontent', 'backup'),
|
||||
new lang_string('configgeneralcontentbankcontent', 'backup'),
|
||||
@@ -437,6 +431,7 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
|
||||
['value' => 1, 'locked' => 0])
|
||||
);
|
||||
|
||||
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_general_legacyfiles',
|
||||
new lang_string('generallegacyfiles', 'backup'),
|
||||
new lang_string('configlegacyfiles', 'backup'), array('value' => 1, 'locked' => 0)));
|
||||
@@ -459,24 +454,12 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_activities', new lang_string('generalactivities','backup'), new lang_string('configgeneralactivities','backup'), array('value'=>1, 'locked'=>0)));
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_blocks', new lang_string('generalblocks','backup'), new lang_string('configgeneralblocks','backup'), array('value'=>1, 'locked'=>0)));
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_filters', new lang_string('generalfilters','backup'), new lang_string('configgeneralfilters','backup'), array('value'=>1, 'locked'=>0)));
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock(
|
||||
'backup/backup_import_badges',
|
||||
new lang_string('generalbadges', 'backup'),
|
||||
new lang_string('configgeneralbadges', 'backup'),
|
||||
['value' => 0, 'locked' => 0],
|
||||
));
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_calendarevents', new lang_string('generalcalendarevents','backup'), new lang_string('configgeneralcalendarevents','backup'), array('value'=>1, 'locked'=>0)));
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_questionbank', new lang_string('generalquestionbank','backup'), new lang_string('configgeneralquestionbank','backup'), array('value'=>1, 'locked'=>0)));
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_groups',
|
||||
new lang_string('generalgroups', 'backup'), new lang_string('configgeneralgroups', 'backup'),
|
||||
array('value' => 1, 'locked' => 0)));
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_import_competencies', new lang_string('generalcompetencies','backup'), new lang_string('configgeneralcompetencies','backup'), array('value'=>1, 'locked'=>0)));
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock(
|
||||
'backup/backup_import_customfield',
|
||||
new lang_string('generalcustomfield', 'backup'),
|
||||
new lang_string('configgeneralcustomfield', 'backup'),
|
||||
['value' => 1, 'locked' => 0],
|
||||
));
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock(
|
||||
'backup/backup_import_contentbankcontent',
|
||||
new lang_string('generalcontentbankcontent', 'backup'),
|
||||
@@ -606,12 +589,6 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
|
||||
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_groups', new lang_string('generalgroups', 'backup'),
|
||||
new lang_string('configgeneralgroups', 'backup'), 1));
|
||||
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_competencies', new lang_string('generalcompetencies','backup'), new lang_string('configgeneralcompetencies','backup'), 1));
|
||||
$temp->add(new admin_setting_configcheckbox(
|
||||
'backup/backup_auto_customfield',
|
||||
new lang_string('generalcustomfield', 'backup'),
|
||||
new lang_string('configgeneralcustomfield', 'backup'),
|
||||
1,
|
||||
));
|
||||
$temp->add(new admin_setting_configcheckbox(
|
||||
'backup/backup_auto_contentbankcontent',
|
||||
new lang_string('generalcontentbankcontent', 'backup'),
|
||||
@@ -691,12 +668,6 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_competencies',
|
||||
new lang_string('generalcompetencies', 'backup'),
|
||||
new lang_string('configrestorecompetencies', 'backup'), array('value' => 1, 'locked' => 0)));
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock(
|
||||
'restore/restore_general_customfield',
|
||||
new lang_string('generalcustomfield', 'backup'),
|
||||
new lang_string('configrestorecustomfield', 'backup'),
|
||||
['value' => 1, 'locked' => 0],
|
||||
));
|
||||
$temp->add(new admin_setting_configcheckbox_with_lock('restore/restore_general_contentbankcontent',
|
||||
new lang_string('generalcontentbankcontent', 'backup'),
|
||||
new lang_string('configrestorecontentbankcontent', 'backup'), array('value' => 1, 'locked' => 0)));
|
||||
|
||||
@@ -109,16 +109,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
|
||||
// Add the 'profiling' page to admin block.
|
||||
$ADMIN->add('development', $temp);
|
||||
|
||||
$ADMIN->add(
|
||||
parentname: 'development',
|
||||
something: new admin_externalpage(
|
||||
name: 'swaggerui',
|
||||
visiblename: new lang_string('swaggerui', 'admin'),
|
||||
url: "$CFG->wwwroot/admin/swaggerui.php",
|
||||
),
|
||||
);
|
||||
|
||||
// Web service test clients DO NOT COMMIT : THE EXTERNAL WEB PAGE IS NOT AN ADMIN PAGE !!!!!
|
||||
// Web service test clients DO NOT COMMIT : THE EXTERNAL WEB PAGE IS NOT AN ADMIN PAGE !!!!!
|
||||
$ADMIN->add('development', new admin_externalpage('testclient', new lang_string('testclient', 'webservice'), "$CFG->wwwroot/$CFG->admin/webservice/testclient.php"));
|
||||
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Configure the settings for file redaction service.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright Meirza <meirza.arson@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
if ($hassiteconfig) {
|
||||
if (!$ADMIN->locate('file_redactor')) {
|
||||
$ADMIN->add('server', new admin_category('file_redactor', get_string('redactor', 'core_files')));
|
||||
}
|
||||
|
||||
$manager = \core\di::get(\core_files\redactor\manager::class);
|
||||
|
||||
// Get settings from each service.
|
||||
foreach ($manager->get_service_classnames() as $servicename => $service) {
|
||||
$servicesettings = new admin_settingpage(
|
||||
$servicename,
|
||||
new lang_string("redactor:{$servicename}", 'core_files'),
|
||||
);
|
||||
$service::add_settings($servicesettings);
|
||||
|
||||
$ADMIN->add('file_redactor', $servicesettings);
|
||||
}
|
||||
}
|
||||
@@ -39,37 +39,18 @@ if ($hassiteconfig) {
|
||||
$temp->add(new admin_setting_configtext('defaultcity', new lang_string('defaultcity', 'core_admin'),
|
||||
new lang_string('defaultcity_help', 'core_admin'), ''));
|
||||
|
||||
$temp->add(new admin_setting_countrycodes('allcountrycodes', new lang_string('allcountrycodes', 'core_admin'),
|
||||
new lang_string('configallcountrycodes', 'core_admin')));
|
||||
|
||||
$temp->add(new admin_setting_heading('iplookup', new lang_string('iplookup', 'core_admin'),
|
||||
new lang_string('iplookupinfo', 'core_admin')));
|
||||
|
||||
$temp->add(new admin_setting_configfile('geoip2file', new lang_string('geoipfile', 'core_admin'),
|
||||
new lang_string('configgeoipfile', 'core_admin', $CFG->dataroot . '/geoip/'),
|
||||
$CFG->dataroot . '/geoip/GeoIP-City.mmdb'));
|
||||
|
||||
$temp->add(new admin_setting_configselect('geoipdbedition',
|
||||
new lang_string('geoipdbedition', 'core_admin'),
|
||||
new lang_string('geoipdbedition_desc', 'core_admin'), 'GeoLite2-City',
|
||||
['GeoLite2-City' => 'GeoLite2-City', 'GeoIP2-City' => 'GeoIP2-City']));
|
||||
|
||||
$temp->add(new admin_setting_configtext('geoipmaxmindaccid',
|
||||
new lang_string('geoipmaxmindaccid', 'core_admin'),
|
||||
new lang_string('geoipmaxmindaccid_desc', 'core_admin'),
|
||||
'',
|
||||
PARAM_TEXT,
|
||||
));
|
||||
|
||||
$temp->add(new admin_setting_configtext('geoipmaxmindlicensekey',
|
||||
new lang_string('geoipmaxmindlicensekey', 'core_admin'),
|
||||
new lang_string('geoipmaxmindlicensekey_desc', 'core_admin'),
|
||||
'',
|
||||
PARAM_TEXT,
|
||||
));
|
||||
$CFG->dataroot . '/geoip/GeoLite2-City.mmdb'));
|
||||
|
||||
$temp->add(new admin_setting_configtext('googlemapkey3', new lang_string('googlemapkey3', 'core_admin'),
|
||||
new lang_string('googlemapkey3_help', 'core_admin'), '', PARAM_RAW, 60));
|
||||
|
||||
$temp->add(new admin_setting_countrycodes('allcountrycodes', new lang_string('allcountrycodes', 'core_admin'),
|
||||
new lang_string('configallcountrycodes', 'core_admin')));
|
||||
}
|
||||
|
||||
$ADMIN->add('location', $temp);
|
||||
|
||||
@@ -102,8 +102,6 @@ if ($hassiteconfig) {
|
||||
new lang_string('limitconcurrentlogins_desc', 'core_auth'), 0, $options));
|
||||
$temp->add(new admin_setting_configtext('alternateloginurl', new lang_string('alternateloginurl', 'auth'),
|
||||
new lang_string('alternatelogin', 'auth', htmlspecialchars(get_login_url(), ENT_COMPAT)), ''));
|
||||
$temp->add(new admin_setting_configcheckbox('showloginform', new lang_string('showloginform', 'core_auth'),
|
||||
new lang_string('showloginform_desc', 'core_auth'), 1));
|
||||
$temp->add(new admin_setting_configtext('forgottenpasswordurl', new lang_string('forgottenpasswordurl', 'auth'),
|
||||
new lang_string('forgottenpassword', 'auth'), '', PARAM_URL));
|
||||
$temp->add(new admin_setting_confightmleditor('auth_instructions', new lang_string('instructions', 'auth'),
|
||||
@@ -287,7 +285,7 @@ if ($hassiteconfig) {
|
||||
$items[] = new admin_setting_configcheckbox('filtermatchonepertext', new lang_string('filtermatchonepertext', 'admin'), new lang_string('configfiltermatchonepertext', 'admin'), 0);
|
||||
$items[] = new admin_setting_configcheckbox('filternavigationwithsystemcontext',
|
||||
new lang_string('filternavigationwithsystemcontext', 'admin'),
|
||||
new lang_string('configfilternavigationwithsystemcontext', 'admin'), 1);
|
||||
new lang_string('configfilternavigationwithsystemcontext', 'admin'), 0);
|
||||
foreach ($items as $item) {
|
||||
$item->set_updatedcallback('reset_text_filters_cache');
|
||||
$temp->add($item);
|
||||
@@ -544,18 +542,6 @@ if ($hassiteconfig || has_capability('moodle/question:config', $systemcontext))
|
||||
$settings->add(new admin_setting_configselect('question_preview/history',
|
||||
get_string('responsehistory', 'question'), '', 0, $hiddenofvisible));
|
||||
|
||||
// Question editing settings.
|
||||
$settings = new admin_settingpage('qediting',
|
||||
get_string('questionediting', 'question'),
|
||||
'moodle/question:config');
|
||||
$ADMIN->add('qtypesettings', $settings);
|
||||
|
||||
$settings->add(new admin_setting_heading('qediting_options',
|
||||
'', get_string('questionediting_desc', 'question')));
|
||||
|
||||
$settings->add(new admin_setting_configcheckbox('questiondefaultssave',
|
||||
get_string('questiondefaultssave', 'question'), get_string('questiondefaultssave_desc', 'question'), 1));
|
||||
|
||||
// Settings for particular question types.
|
||||
$plugins = core_plugin_manager::instance()->get_plugins_of_type('qtype');
|
||||
core_collator::asort_objects_by_property($plugins, 'displayname');
|
||||
@@ -836,49 +822,12 @@ if ($hassiteconfig && core_communication\api::is_available()) {
|
||||
}
|
||||
}
|
||||
|
||||
// SMS plugins.
|
||||
if ($hassiteconfig) {
|
||||
$ADMIN->add(
|
||||
'modules',
|
||||
new admin_category(
|
||||
'sms',
|
||||
new lang_string('sms', 'core_sms'),
|
||||
),
|
||||
);
|
||||
$ADMIN->add(
|
||||
'sms',
|
||||
new admin_externalpage(
|
||||
'smsgateway',
|
||||
new lang_string('manage_sms_gateways', 'core_sms'),
|
||||
$CFG->wwwroot . '/sms/sms_gateways.php',
|
||||
),
|
||||
);
|
||||
foreach (core_component::get_plugin_list('smsgateway') as $plugin => $path) {
|
||||
$settingspath = $path . '/settings.php';
|
||||
if (file_exists($settingspath)) {
|
||||
$settings = new admin_settingpage(
|
||||
'smsgateway_' . $plugin . '_settings',
|
||||
new lang_string('pluginname', 'smsgateway_' . $plugin),
|
||||
'moodle/site:config',
|
||||
);
|
||||
include($settingspath);
|
||||
$ADMIN->add('smsgateway', $settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Content bank content types.
|
||||
if ($hassiteconfig) {
|
||||
$ADMIN->add('modules', new admin_category('contentbanksettings', new lang_string('contentbank')));
|
||||
$temp = new admin_settingpage('managecontentbanktypes', new lang_string('managecontentbanktypes'));
|
||||
$temp->add(new admin_setting_managecontentbankcontenttypes());
|
||||
$ADMIN->add('contentbanksettings', $temp);
|
||||
$ADMIN->add('contentbanksettings',
|
||||
new admin_externalpage('contentbank', new lang_string('contentbankcustomfields', 'contentbank'),
|
||||
$CFG->wwwroot . '/contentbank/customfield.php',
|
||||
'moodle/contentbank:configurecustomfields'
|
||||
)
|
||||
);
|
||||
$plugins = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
|
||||
foreach ($plugins as $plugin) {
|
||||
/** @var \core\plugininfo\contentbank $plugin */
|
||||
|
||||
@@ -126,7 +126,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
|
||||
$temp->add($adminsetting);
|
||||
$temp->add(new admin_setting_configcheckbox('passwordchangelogout',
|
||||
new lang_string('passwordchangelogout', 'admin'),
|
||||
new lang_string('passwordchangelogout_desc', 'admin'), 1));
|
||||
new lang_string('passwordchangelogout_desc', 'admin'), 0));
|
||||
|
||||
$temp->add(new admin_setting_configcheckbox('passwordchangetokendeletion',
|
||||
new lang_string('passwordchangetokendeletion', 'admin'),
|
||||
|
||||
@@ -181,16 +181,8 @@ if ($hassiteconfig) {
|
||||
new lang_string('configproxyuser', 'admin'), ''));
|
||||
$temp->add(new admin_setting_configpasswordunmask('proxypassword', new lang_string('proxypassword', 'admin'),
|
||||
new lang_string('configproxypassword', 'admin'), ''));
|
||||
|
||||
$setting = new admin_setting_configtext('proxybypass', new lang_string('proxybypass', 'admin'),
|
||||
new lang_string('configproxybypass', 'admin'), 'localhost,127.0.0.1');
|
||||
$setting->set_updatedcallback(function() {
|
||||
// Normalize $CFG->proxybypass value.
|
||||
$normalizedvalue = \core\ip_utils::normalize_internet_address_list(get_config('core', 'proxybypass'));
|
||||
set_config('proxybypass', $normalizedvalue);
|
||||
});
|
||||
$temp->add($setting);
|
||||
|
||||
$temp->add(new admin_setting_configtext('proxybypass', new lang_string('proxybypass', 'admin'),
|
||||
new lang_string('configproxybypass', 'admin'), 'localhost, 127.0.0.1'));
|
||||
$temp->add(new admin_setting_configcheckbox('proxylogunsafe', new lang_string('proxylogunsafe', 'admin'),
|
||||
new lang_string('configproxylogunsafe_help', 'admin'), 0));
|
||||
$temp->add(new admin_setting_configcheckbox('proxyfixunsafe', new lang_string('proxyfixunsafe', 'admin'),
|
||||
|
||||
@@ -25,7 +25,6 @@ if ($hassiteconfig) {
|
||||
$ADMIN->add('root', new admin_category('users', new lang_string('users','admin')));
|
||||
$ADMIN->add('root', new admin_category('courses', new lang_string('courses','admin')));
|
||||
$ADMIN->add('root', new admin_category('grades', new lang_string('grades')));
|
||||
$ADMIN->add('root', new admin_category('ai', new lang_string('ai', 'ai')));
|
||||
$ADMIN->add('root', new admin_category('analytics', new lang_string('analytics', 'analytics')));
|
||||
$ADMIN->add('root', new admin_category('competencies', new lang_string('competencies', 'core_competency')));
|
||||
$ADMIN->add('root', new admin_category('badges', new lang_string('badges'), empty($CFG->enablebadges)));
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Swagger UI for Moodle
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require('../config.php');
|
||||
require_once($CFG->libdir . '/adminlib.php');
|
||||
|
||||
$swaggerversion = '5.17.14';
|
||||
|
||||
$PAGE->set_url('/admin/swaggerui.php');
|
||||
|
||||
admin_externalpage_setup('swaggerui');
|
||||
|
||||
$PAGE->requires->css(new moodle_url("https://unpkg.com/swagger-ui-dist@{$swaggerversion}/swagger-ui.css"));
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
// These have to be manually added for now because they must be made cross-origin. The `js` method does not yet support this.
|
||||
echo html_writer::tag(
|
||||
tagname: 'script',
|
||||
contents: '',
|
||||
attributes: [
|
||||
'src' => new moodle_url("https://unpkg.com/swagger-ui-dist@{$swaggerversion}/swagger-ui-bundle.js"),
|
||||
'crossorigin' => 'crossorigin',
|
||||
],
|
||||
);
|
||||
echo html_writer::tag(
|
||||
tagname: 'script',
|
||||
contents: '',
|
||||
attributes: [
|
||||
'src' => new moodle_url("https://unpkg.com/swagger-ui-plugin-hierarchical-tags"),
|
||||
'crossorigin' => 'crossorigin',
|
||||
],
|
||||
);
|
||||
|
||||
$openapipath = moodle_url::routed_path('/api/rest/v2/openapi.json')->out();
|
||||
$swaggerinit = <<<JS
|
||||
window.ui = SwaggerUIBundle({
|
||||
url: "{$openapipath}",
|
||||
dom_id: '#swagger-ui',
|
||||
|
||||
// Enable the "Try it out" button by default.
|
||||
tryItOutEnabled: true,
|
||||
|
||||
// Show snippets different OS options.
|
||||
requestSnippetsEnabled: true,
|
||||
|
||||
deepLinking: true,
|
||||
|
||||
plugins: [
|
||||
HierarchicalTagsPlugin,
|
||||
],
|
||||
|
||||
hierarchicalTagSeparator: /[_]/
|
||||
});
|
||||
JS;
|
||||
|
||||
$PAGE->requires->js_init_code(
|
||||
jscode: $swaggerinit,
|
||||
ondomready: true,
|
||||
);
|
||||
|
||||
echo html_writer::div('', '', [
|
||||
'id' => 'swagger-ui',
|
||||
]);
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
+1
-34
@@ -24,7 +24,6 @@
|
||||
|
||||
require_once(__DIR__ . '/../config.php');
|
||||
require_once("{$CFG->libdir}/adminlib.php");
|
||||
require_once("tool/task/lib.php");
|
||||
|
||||
use core_admin\reportbuilder\local\systemreports\task_logs;
|
||||
use core_reportbuilder\system_report_factory;
|
||||
@@ -50,41 +49,9 @@ if (null !== $logid) {
|
||||
if ($download) {
|
||||
$filename = str_replace('\\', '_', $log->classname) . "-{$log->id}.log";
|
||||
header("Content-Disposition: attachment; filename=\"{$filename}\"");
|
||||
readstring_accel($log->output, 'text/plain');
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$class = new $log->classname;
|
||||
$title = $class->get_name();
|
||||
} catch (Exception $e) {
|
||||
$title = $log->classname;
|
||||
}
|
||||
$title .= " ($log->id)";
|
||||
|
||||
$PAGE->navbar->add($title, '');
|
||||
echo $OUTPUT->header();
|
||||
echo html_writer::start_tag('pre', ['class' => 'task-output', 'style' => 'min-height: 24lh']);
|
||||
|
||||
echo tool_task_mtrace_wrapper($log->output);
|
||||
echo html_writer::end_tag('pre');
|
||||
echo $OUTPUT->action_link(
|
||||
new moodle_url('/admin/tasklogs.php'),
|
||||
$strheading,
|
||||
null,
|
||||
null,
|
||||
new pix_icon('i/log', ''),
|
||||
);
|
||||
echo ' ';
|
||||
echo $OUTPUT->action_link(
|
||||
new moodle_url('/admin/tasklogs.php', ['logid' => $log->id, 'download' => true]),
|
||||
new lang_string('download'),
|
||||
null,
|
||||
null,
|
||||
new pix_icon('t/download', ''),
|
||||
);
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
readstring_accel($log->output, 'text/plain');
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
Setting.
|
||||
}}
|
||||
<div class="form-item row" id="{{id}}">
|
||||
<div class="form-label col-sm-3 text-sm-end">
|
||||
<div class="form-label col-sm-3 text-sm-right">
|
||||
{{#customcontrol}}
|
||||
<p {{#labelfor}}id="{{labelfor}}_label"{{/labelfor}}>
|
||||
{{{title}}}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_admin/setting_configtoggle
|
||||
|
||||
Template for toggle element in admin settings.
|
||||
|
||||
Context variables required for this template:
|
||||
* id - element id
|
||||
* checked - bool, true if the toggle is enabled
|
||||
* name - form element name
|
||||
* value - form for name field
|
||||
* action - action to be performed when the toggle is clicked
|
||||
* plugin - plugin name
|
||||
* state - state of the toggle
|
||||
* title - title of the toggle tooltip
|
||||
* label - label of the toggle
|
||||
* labelclasses - classes for the label (sr-only for screen readers)
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"id": "reality-toggle-3",
|
||||
"checked": true,
|
||||
"dataattributes": [{
|
||||
"name": "id",
|
||||
"value": "toggle-reality",
|
||||
"action": "togglestate",
|
||||
"plugin": "tool_reality",
|
||||
"state": 1
|
||||
}],
|
||||
"title": "Title example",
|
||||
"label": "Enable/disable reality",
|
||||
"labelclasses": "sr-only"
|
||||
}
|
||||
}}
|
||||
<div id="container-{{id}}"
|
||||
data-toggle-method="{{dataattributes.toggle-method}}"
|
||||
data-action="{{dataattributes.action}}"
|
||||
data-plugin="{{dataattributes.plugin}}"
|
||||
data-state="{{dataattributes.state}}"
|
||||
data-value="{{dataattributes.value}}"
|
||||
>
|
||||
{{> core/toggle }}
|
||||
</div>
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
Setting description.
|
||||
}}
|
||||
<div class="form-item row">
|
||||
<div class="form-label col-sm-3 text-sm-end">
|
||||
<div class="form-label col-sm-3 text-sm-right">
|
||||
<label>
|
||||
{{{title}}}
|
||||
</label>
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_admin/setting_savebutton
|
||||
|
||||
Admin save button settings template.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
}
|
||||
}}
|
||||
{{!
|
||||
Settings.
|
||||
}}
|
||||
<div class="row">
|
||||
<div class="offset-sm-3 col-sm-3">
|
||||
<button type="submit" class="btn btn-primary">{{#str}}savechanges, admin{{/str}}</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,39 +0,0 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_admin/table/namedesc
|
||||
|
||||
Admin setting template to display an name and description
|
||||
in a single table cell.
|
||||
|
||||
Context variables required for this template:
|
||||
* name - Name of the table setting
|
||||
* description - Description of the setting
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"name": "Fancy Setting",
|
||||
"description": "Text that describes this setting well"
|
||||
}
|
||||
}}
|
||||
{{!
|
||||
Name and description.
|
||||
}}
|
||||
<div>
|
||||
<div><strong>{{name}}</strong></div>
|
||||
<div>{{description}}</div>
|
||||
</div>
|
||||
@@ -58,7 +58,7 @@
|
||||
<a
|
||||
href="{{reporturl}}"
|
||||
id="theme-usage-report-{{choose}}"
|
||||
class="btn btn-link p-0 ms-2"
|
||||
class="btn btn-link p-0 ml-2"
|
||||
title="{{#str}}themeusagereportname, admin, {{name}}{{/str}}">
|
||||
<i class="icon fa fa-area-chart m-0" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{#str}}themeusagereportname, admin, {{name}}{{/str}}</span>
|
||||
@@ -68,7 +68,7 @@
|
||||
<a
|
||||
href="{{settingsurl}}"
|
||||
id="theme-settings-{{choose}}"
|
||||
class="btn btn-link p-0 ms-2"
|
||||
class="btn btn-link p-0 ml-2"
|
||||
title="{{#str}}themeeditsettingsname, admin, {{name}}{{/str}}">
|
||||
<i class="icon fa fa-cog m-0" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{#str}}themeeditsettingsname, admin, {{name}}{{/str}}</span>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<div class="lead">{{tokenname}}</div>
|
||||
<div class="d-flex justify-content-start align-middle">
|
||||
<div class="lead text-break pt-1" id="copytoclipboardtoken">{{token}}</div>
|
||||
<button class="btn btn-primary ms-2" data-action="copytoclipboard" data-clipboard-target="#copytoclipboardtoken" data-clipboard-success-message="{{#str}}tokencopied, webservice{{/str}}">
|
||||
<button class="btn btn-primary ml-2" data-action="copytoclipboard" data-clipboard-target="#copytoclipboardtoken" data-clipboard-success-message="{{#str}}tokencopied, webservice{{/str}}">
|
||||
{{#pix}}t/copy, core {{/pix}}{{#str}}copytoclipboard{{/str}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -81,8 +81,6 @@ class behat_admin extends behat_base {
|
||||
|
||||
$this->execute('behat_forms::i_set_the_field_with_xpath_to', [$fieldxpath, $value]);
|
||||
$this->execute("behat_general::i_click_on", [get_string('savechanges'), 'button']);
|
||||
// Wait for the page to be redirected.
|
||||
$this->execute("behat_general::i_wait_to_be_redirected");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,8 +90,6 @@ class behat_admin extends behat_base {
|
||||
* @Given /^the following config values are set as admin:$/
|
||||
* @param TableNode $table
|
||||
*/
|
||||
#[\core\attribute\example('And the following config values are set as admin:
|
||||
| sendcoursewelcomemessage | 0 | enrol_manual |')]
|
||||
public function the_following_config_values_are_set_as_admin(TableNode $table) {
|
||||
|
||||
if (!$data = $table->getRowsHash()) {
|
||||
|
||||
@@ -8,14 +8,26 @@ Feature: An administrator can manage Block plugins
|
||||
Scenario: An administrator can control the enabled state of block plugins using JavaScript
|
||||
Given I am logged in as "admin"
|
||||
And I navigate to "Plugins > Blocks > Manage blocks" in site administration
|
||||
When I toggle the "Disable Latest badges" admin switch "off"
|
||||
And I should see "Latest badges disabled."
|
||||
And I reload the page
|
||||
Then I should see "Enable Latest badges"
|
||||
And I toggle the "Enable Latest badges" admin switch "on"
|
||||
And I should see "Latest badges enabled."
|
||||
And I reload the page
|
||||
Then I should see "Disable Latest badges"
|
||||
When I click on "Disable Latest badges" "link"
|
||||
Then I should see "Latest badges disabled."
|
||||
And "Disable Latest badges" "link" should not exist
|
||||
But "Enable Latest badges" "link" should exist
|
||||
When I click on "Enable Latest badges" "link"
|
||||
Then I should see "Latest badges enabled."
|
||||
And "Enable Latest badges" "link" should not exist
|
||||
But "Disable Latest badges" "link" should exist
|
||||
|
||||
Scenario: An administrator can control the enabled state of block plugins without JavaScript
|
||||
Given I am logged in as "admin"
|
||||
And I navigate to "Plugins > Blocks > Manage blocks" in site administration
|
||||
When I click on "Disable Latest badges" "link"
|
||||
Then I should see "Latest badges disabled."
|
||||
And "Disable Latest badges" "link" should not exist
|
||||
But "Enable Latest badges" "link" should exist
|
||||
When I click on "Enable Latest badges" "link"
|
||||
Then I should see "Latest badges enabled."
|
||||
And "Enable Latest badges" "link" should not exist
|
||||
But "Disable Latest badges" "link" should exist
|
||||
|
||||
@javascript
|
||||
Scenario: An administrator can control the protected state of block plugins using JavaScript
|
||||
|
||||
@@ -6,14 +6,25 @@ Feature: An administrator can manage Media plugins
|
||||
|
||||
@javascript
|
||||
Scenario: An administrator can control the enabled state of media plugins using JavaScript
|
||||
The state change should be reflected in the UI and persist across page reloads
|
||||
Given I am logged in as "admin"
|
||||
And I navigate to "Plugins > Media players > Manage media players" in site administration
|
||||
When I toggle the "Disable YouTube" admin switch "off"
|
||||
And I should see "YouTube disabled."
|
||||
And I reload the page
|
||||
Then I should see "Enable YouTube"
|
||||
And I toggle the "Enable YouTube" admin switch "on"
|
||||
And I should see "YouTube enabled."
|
||||
And I reload the page
|
||||
Then I should see "Disable YouTube"
|
||||
When I click on "Disable YouTube" "link"
|
||||
Then I should see "YouTube disabled."
|
||||
And "Disable YouTube" "link" should not exist
|
||||
But "Enable YouTube" "link" should exist
|
||||
When I click on "Enable YouTube" "link"
|
||||
Then I should see "YouTube enabled."
|
||||
And "Enable YouTube" "link" should not exist
|
||||
But "Disable YouTube" "link" should exist
|
||||
|
||||
Scenario: An administrator can control the enabled state of media plugins without JavaScript
|
||||
Given I am logged in as "admin"
|
||||
And I navigate to "Plugins > Media players > Manage media players" in site administration
|
||||
When I click on "Disable YouTube" "link"
|
||||
Then I should see "YouTube disabled."
|
||||
And "Disable YouTube" "link" should not exist
|
||||
But "Enable YouTube" "link" should exist
|
||||
When I click on "Enable YouTube" "link"
|
||||
Then I should see "YouTube enabled."
|
||||
And "Enable YouTube" "link" should not exist
|
||||
But "Disable YouTube" "link" should exist
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
@core @core_admin
|
||||
Feature: Manage external services tokens
|
||||
In order to manage external service usage
|
||||
As an admin
|
||||
I need to be able to create, filter and delete tokens
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | password | firstname | lastname |
|
||||
| user1 | user1 | Firstname1 | Lastname1 |
|
||||
| user2 | user2 | Firstname2 | Lastname2 |
|
||||
| user3 | user3 | Firstname3 | Lastname3 |
|
||||
| user4 | user4 | Firstname4 | Lastname4 |
|
||||
And I change window size to "small"
|
||||
|
||||
@javascript
|
||||
Scenario: Add a token to user identified by name and then delete that token
|
||||
Given I log in as "admin"
|
||||
And I am on site homepage
|
||||
And I navigate to "Server > Web services > Manage tokens" in site administration
|
||||
And I press "Create token"
|
||||
And I set the field "Name" to "Webservice1"
|
||||
And I set the field "User" to "Firstname1 Lastname1"
|
||||
And I set the field "Service" to "Moodle mobile web service"
|
||||
And I set the field "IP restriction" to "127.0.0.1"
|
||||
When I press "Save changes"
|
||||
Then the following should exist in the "generaltable" table:
|
||||
| Name | First name | Service | IP restriction | Last access |
|
||||
| Webservice1 | Firstname1 Lastname1 | Moodle mobile web service | 127.0.0.1 | Never |
|
||||
|
||||
# Verify the message and the "Copy to clipboard" button.
|
||||
And I should see "Copy the token now. It won't be shown again once you leave this page."
|
||||
And "Copy to clipboard" "button" should exist
|
||||
|
||||
# New token can only read once.
|
||||
And I reload the page
|
||||
And I should not see "Copy the token now. It won't be shown again once you leave this page."
|
||||
And "Copy to clipboard" "button" should not exist
|
||||
|
||||
# Delete token.
|
||||
And I click on "Delete" "link" in the "Webservice1" "table_row"
|
||||
And I should see "Do you really want to delete this web service token for Firstname1 Lastname1 on the service Moodle mobile web service?"
|
||||
And I press "Delete"
|
||||
And "Webservice1" "table_row" should not exist
|
||||
|
||||
@javascript @skip_chrome_zerosize
|
||||
Scenario: Tokens can be filtered by name (case-insensitive), by user and by service
|
||||
Given the following "core_webservice > Service" exists:
|
||||
| name | Site information |
|
||||
| shortname | siteinfo |
|
||||
| enabled | 1 |
|
||||
And the following "core_webservice > Service function" exists:
|
||||
| service | siteinfo |
|
||||
| functions | core_webservice_get_site_info |
|
||||
And the following "core_webservice > Tokens" exist:
|
||||
| user | service | name |
|
||||
| user2 | siteinfo | WEBservice1 |
|
||||
| user3 | moodle_mobile_app | webservicE3 |
|
||||
| user4 | siteinfo | New service2 |
|
||||
When I log in as "admin"
|
||||
And I navigate to "Server > Web services > Manage tokens" in site administration
|
||||
|
||||
# All created tokens are shown by default.
|
||||
And "Firstname1 Lastname1" "table_row" should not exist
|
||||
And I should see "Site information" in the "Firstname2 Lastname2" "table_row"
|
||||
And I should see "Moodle mobile web service" in the "Firstname3 Lastname3" "table_row"
|
||||
And I should see "Site information" in the "Firstname4 Lastname4" "table_row"
|
||||
|
||||
# Filter tokens by by name (case-insensitive).
|
||||
And I click on "Tokens filter" "link"
|
||||
And I set the field "Name" to "webservice"
|
||||
And I press "Show only matching tokens"
|
||||
And I should see "Site information" in the "Firstname2 Lastname2" "table_row"
|
||||
And I should see "Moodle mobile web service" in the "Firstname3 Lastname3" "table_row"
|
||||
And "Firstname4 Lastname4" "table_row" should not exist
|
||||
|
||||
# Reset the filter.
|
||||
And I press "Show all tokens"
|
||||
And I should see "Site information" in the "Firstname2 Lastname2" "table_row"
|
||||
And I should see "Moodle mobile web service" in the "Firstname3 Lastname3" "table_row"
|
||||
And I should see "Site information" in the "Firstname4 Lastname4" "table_row"
|
||||
|
||||
# Filter tokens by user (note we can select the user by the identity field here).
|
||||
When I click on "Tokens filter" "link"
|
||||
And I set the field "User" to "user2@example.com"
|
||||
And I press "Show only matching tokens"
|
||||
Then "Firstname3 Lastname3" "table_row" should not exist
|
||||
And "Firstname4 Lastname4" "table_row" should not exist
|
||||
And I should see "Site information" in the "Firstname2 Lastname2" "table_row"
|
||||
|
||||
# Reset the filter.
|
||||
And I press "Show all tokens"
|
||||
And I should see "Site information" in the "Firstname2 Lastname2" "table_row"
|
||||
And I should see "Moodle mobile web service" in the "Firstname3 Lastname3" "table_row"
|
||||
And I should see "Site information" in the "Firstname4 Lastname4" "table_row"
|
||||
|
||||
# Filter tokens by service.
|
||||
And I click on "Tokens filter" "link"
|
||||
And I set the field "Service" to "Site information"
|
||||
And I press "Show only matching tokens"
|
||||
And I should see "Site information" in the "Firstname2 Lastname2" "table_row"
|
||||
And I should see "Site information" in the "Firstname4 Lastname4" "table_row"
|
||||
And "Firstname3 Lastname3" "table_row" should not exist
|
||||
@@ -1,40 +0,0 @@
|
||||
@core @core_admin
|
||||
Feature: Plugins overview page
|
||||
In order to manage plugin and settings
|
||||
As an admin
|
||||
I can access the plugins overview page
|
||||
|
||||
Background:
|
||||
Given I am logged in as "admin"
|
||||
And I navigate to "Plugins > Plugins overview" in site administration
|
||||
|
||||
Scenario: An administrator can view plugins overview page
|
||||
Then "Plugins overview" "text" should exist
|
||||
And "Check for available updates" "button" should exist
|
||||
And "All plugins" "link" should exist
|
||||
And "Additional plugins" "link" should exist
|
||||
|
||||
Scenario: An administrator can access list of activities when activity modules cog icon is pressed
|
||||
When I click on "Settings" "link" in the "Activity modules" "table_row"
|
||||
Then "Activities" "text" should exist
|
||||
And "Manage activities" "text" should exist in the ".breadcrumb" "css_element"
|
||||
And "Activity modules" "link" should exist in the ".breadcrumb" "css_element"
|
||||
|
||||
Scenario: An administrator can access plugin settings
|
||||
When I click on "Settings" "link" in the "Assignment" "table_row"
|
||||
Then "Assignment settings" "text" should exist
|
||||
And "Assignment settings" "text" should exist in the ".breadcrumb" "css_element"
|
||||
And "Assignment" "link" should exist in the ".breadcrumb" "css_element"
|
||||
And "Activity modules" "link" should exist in the ".breadcrumb" "css_element"
|
||||
|
||||
Scenario: Plugins with dependencies cannot be uninstalled
|
||||
When I navigate to "Plugins > Plugins overview" in site administration
|
||||
Then "Uninstall" "link" should not exist in the "Database" "table_row"
|
||||
And "Required by: filter_data" "text" should exist in the "Database" "table_row"
|
||||
|
||||
Scenario: Cancelling plugin uninstall does not uninstall the selected plugin
|
||||
When I click on "Uninstall" "link" in the "Assignment" "table_row"
|
||||
Then "Uninstalling Assignment" "text" should exist
|
||||
And "You are about to uninstall the plugin Assignment. This will completely delete everything in the database associated with this plugin, including its configuration, log records, user files managed by the plugin etc. There is no way back and Moodle itself does not create any recovery backup. Are you SURE you want to continue?" "text" should exist
|
||||
And I click on "Cancel" "button"
|
||||
And "Uninstall" "link" should exist in the "Assignment" "table_row"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
@core @core_admin @core_webservice @javascript
|
||||
@core @core_admin @javascript
|
||||
Feature: Verify the breadcrumbs in external webservice site administration pages
|
||||
Whenever I navigate to external webservice page in site administration
|
||||
As an admin
|
||||
+2
-3
@@ -1,4 +1,4 @@
|
||||
@core @core_admin @core_webservice @javascript
|
||||
@core @core_admin @javascript
|
||||
Feature: Verify the breadcrumbs in webservice tokens site administration pages
|
||||
Whenever I navigate to manage tokens page in site administration
|
||||
As an admin
|
||||
@@ -18,9 +18,8 @@ Feature: Verify the breadcrumbs in webservice tokens site administration pages
|
||||
And "Manage tokens" "link" should exist in the ".breadcrumb" "css_element"
|
||||
And "Web services" "link" should exist in the ".breadcrumb" "css_element"
|
||||
And I set the field "User" to "John Doe"
|
||||
And I set the field "Service" to "Moodle mobile web service"
|
||||
And I press "Save changes"
|
||||
When I press "Delete" action in the "John Doe" report row
|
||||
When I click on "Delete" "link"
|
||||
Then "Delete token" "text" should exist in the ".breadcrumb" "css_element"
|
||||
And "Manage tokens" "link" should exist in the ".breadcrumb" "css_element"
|
||||
And "Web services" "link" should exist in the ".breadcrumb" "css_element"
|
||||
+2
-2
@@ -31,7 +31,7 @@ require_once($CFG->dirroot . '/webservice/tests/helpers.php');
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
final class set_block_protection_test extends \externallib_advanced_testcase {
|
||||
class set_block_protection_test extends \externallib_advanced_testcase {
|
||||
/**
|
||||
* Test execute method with no login.
|
||||
*/
|
||||
@@ -85,7 +85,7 @@ final class set_block_protection_test extends \externallib_advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function execute_provider(): array {
|
||||
public function execute_provider(): array {
|
||||
return [
|
||||
[
|
||||
'block_login',
|
||||
|
||||
+3
-3
@@ -33,7 +33,7 @@ require_once($CFG->dirroot . '/webservice/tests/helpers.php');
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
final class set_plugin_order_test extends \externallib_advanced_testcase {
|
||||
class set_plugin_order_test extends \externallib_advanced_testcase {
|
||||
/**
|
||||
* Text execute method for editor plugins, which support ordering.
|
||||
*
|
||||
@@ -72,7 +72,7 @@ final class set_plugin_order_test extends \externallib_advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function execute_editor_provider(): array {
|
||||
public function execute_editor_provider(): array {
|
||||
$pluginmanager = \core_plugin_manager::instance();
|
||||
$allplugins = array_keys($pluginmanager->get_plugins_of_type('editor'));
|
||||
|
||||
@@ -142,7 +142,7 @@ final class set_plugin_order_test extends \externallib_advanced_testcase {
|
||||
$this->assertIsArray(set_plugin_order::execute($plugin, 1));
|
||||
}
|
||||
|
||||
public static function execute_non_orderable_provider(): array {
|
||||
public function execute_non_orderable_provider(): array {
|
||||
return [
|
||||
// Activities do not support ordering.
|
||||
['mod_assign'],
|
||||
|
||||
+2
-2
@@ -31,7 +31,7 @@ require_once($CFG->dirroot . '/webservice/tests/helpers.php');
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
final class set_plugin_state_test extends \externallib_advanced_testcase {
|
||||
class set_plugin_state_test extends \externallib_advanced_testcase {
|
||||
/**
|
||||
* Text execute method.
|
||||
*
|
||||
@@ -66,7 +66,7 @@ final class set_plugin_state_test extends \externallib_advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function execute_standard_provider(): array {
|
||||
public function execute_standard_provider(): array {
|
||||
$generatetestsfor = function (string $plugin): array {
|
||||
return [
|
||||
[
|
||||
|
||||
@@ -20,9 +20,14 @@ namespace core_admin\reportbuilder\datasource;
|
||||
|
||||
use core\task\database_logger;
|
||||
use core_reportbuilder_generator;
|
||||
use core_reportbuilder_testcase;
|
||||
use core_reportbuilder\local\filters\{boolean_select, date, duration, number, select, text};
|
||||
use core_reportbuilder\task\send_schedules;
|
||||
use core_reportbuilder\tests\core_reportbuilder_testcase;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php");
|
||||
|
||||
/**
|
||||
* Unit tests for task logs datasource
|
||||
@@ -32,7 +37,7 @@ use core_reportbuilder\tests\core_reportbuilder_testcase;
|
||||
* @copyright 2022 Paul Holden <paulh@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
final class task_logs_test extends core_reportbuilder_testcase {
|
||||
class task_logs_test extends core_reportbuilder_testcase {
|
||||
|
||||
/**
|
||||
* Test default datasource
|
||||
@@ -105,7 +110,7 @@ final class task_logs_test extends core_reportbuilder_testcase {
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public static function datasource_filters_provider(): array {
|
||||
public function datasource_filters_provider(): array {
|
||||
return [
|
||||
'Filter name' => ['task_log:name', [
|
||||
'task_log:name_values' => [send_schedules::class],
|
||||
|
||||
@@ -30,7 +30,7 @@ use core_reportbuilder\local\report\filter;
|
||||
* @copyright 2023 Paul Holden <paulh@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
final class courserole_test extends advanced_testcase {
|
||||
class courserole_test extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Data provider for {@see test_get_sql_filter}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
# tool (plugin type) Upgrade notes
|
||||
|
||||
## 4.5
|
||||
|
||||
### Removed
|
||||
|
||||
- The Convert to InnoDB plugin (`tool_innodb`) has been completely removed.
|
||||
|
||||
For more information see [MDL-78776](https://tracker.moodle.org/browse/MDL-78776)
|
||||
@@ -1,3 +0,0 @@
|
||||
define("tool_admin_presets/admin_presets_list",["exports","core/event_dispatcher","core/notification","core/prefetch","core/str","core/pending","core_reportbuilder/local/events","core_reportbuilder/local/selectors","core/toast","tool_admin_presets/repository"],(function(_exports,_event_dispatcher,_notification,_prefetch,_str,_pending,reportEvents,reportSelectors,_toast,_repository){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),reportEvents=_interopRequireWildcard(reportEvents),reportSelectors=_interopRequireWildcard(reportSelectors);_exports.init=()=>{(0,_prefetch.prefetchStrings)("core",["delete"]),(0,_prefetch.prefetchStrings)("tool_admin_presets",["deleteshow","deletepreset","eventpresetdeleted","deletepreviouslyapplied"]),document.addEventListener("click",(event=>{const presetDelete=event.target.closest('[data-action="admin-preset-delete"]');if(presetDelete){event.preventDefault();const triggerElement=presetDelete.closest(".dropdown").querySelector(".dropdown-toggle"),stringid=presetDelete.dataset.presetRollback?"deletepreviouslyapplied":"deletepreset";_notification.default.saveCancelPromise((0,_str.getString)("deleteshow","tool_admin_presets"),(0,_str.getString)(stringid,"tool_admin_presets",presetDelete.dataset.presetName),(0,_str.getString)("delete","core"),{triggerElement:triggerElement}).then((()=>{const pendingPromise=new _pending.default("tool/admin_presets:deletepreset"),reportElement=event.target.closest(reportSelectors.regions.report);return(0,_repository.deletePreset)(presetDelete.dataset.presetId).then((()=>(0,_toast.add)((0,_str.getString)("eventpresetdeleted","tool_admin_presets")))).then((()=>((0,_event_dispatcher.dispatchEvent)(reportEvents.tableReload,{preservePagination:!0},reportElement),pendingPromise.resolve()))).catch(_notification.default.exception)})).catch((()=>{}))}}))}}));
|
||||
|
||||
//# sourceMappingURL=admin_presets_list.min.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"admin_presets_list.min.js","sources":["../src/admin_presets_list.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Admin presets list management\n *\n * @module tool_admin_presets/admin_presets_list\n * @copyright 2024 David Carrillo <davidmc@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport Notification from 'core/notification';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport Pending from 'core/pending';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\nimport {add as addToast} from 'core/toast';\nimport {deletePreset} from 'tool_admin_presets/repository';\n\n/**\n * Initialise module\n */\nexport const init = () => {\n prefetchStrings('core', [\n 'delete',\n ]);\n\n prefetchStrings('tool_admin_presets', [\n 'deleteshow',\n 'deletepreset',\n 'eventpresetdeleted',\n 'deletepreviouslyapplied'\n ]);\n\n document.addEventListener('click', event => {\n const presetDelete = event.target.closest('[data-action=\"admin-preset-delete\"]');\n if (presetDelete) {\n event.preventDefault();\n\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = presetDelete.closest('.dropdown').querySelector('.dropdown-toggle');\n const stringid = presetDelete.dataset.presetRollback ? 'deletepreviouslyapplied' : 'deletepreset';\n\n /* eslint-disable promise/no-nesting */\n Notification.saveCancelPromise(\n getString('deleteshow', 'tool_admin_presets'),\n getString(stringid, 'tool_admin_presets', presetDelete.dataset.presetName),\n getString('delete', 'core'),\n {triggerElement}\n ).then(() => {\n const pendingPromise = new Pending('tool/admin_presets:deletepreset');\n const reportElement = event.target.closest(reportSelectors.regions.report);\n\n return deletePreset(presetDelete.dataset.presetId)\n .then(() => addToast(getString('eventpresetdeleted', 'tool_admin_presets')))\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n};\n"],"names":["document","addEventListener","event","presetDelete","target","closest","preventDefault","triggerElement","querySelector","stringid","dataset","presetRollback","saveCancelPromise","presetName","then","pendingPromise","Pending","reportElement","reportSelectors","regions","report","presetId","reportEvents","tableReload","preservePagination","resolve","catch","Notification","exception"],"mappings":"+qDAsCoB,mCACA,OAAQ,CACpB,yCAGY,qBAAsB,CAClC,aACA,eACA,qBACA,4BAGJA,SAASC,iBAAiB,SAASC,cACzBC,aAAeD,MAAME,OAAOC,QAAQ,0CACtCF,aAAc,CACdD,MAAMI,uBAGAC,eAAiBJ,aAAaE,QAAQ,aAAaG,cAAc,oBACjEC,SAAWN,aAAaO,QAAQC,eAAiB,0BAA4B,qCAGtEC,mBACT,kBAAU,aAAc,uBACxB,kBAAUH,SAAU,qBAAsBN,aAAaO,QAAQG,aAC/D,kBAAU,SAAU,QACpB,CAACN,eAAAA,iBACHO,MAAK,WACGC,eAAiB,IAAIC,iBAAQ,mCAC7BC,cAAgBf,MAAME,OAAOC,QAAQa,gBAAgBC,QAAQC,eAE5D,4BAAajB,aAAaO,QAAQW,UACpCP,MAAK,KAAM,eAAS,kBAAU,qBAAsB,yBACpDA,MAAK,yCACYQ,aAAaC,YAAa,CAACC,oBAAoB,GAAOP,eAC7DF,eAAeU,aAEzBC,MAAMC,sBAAaC,cACzBF,OAAM"}
|
||||
@@ -1,10 +0,0 @@
|
||||
define("tool_admin_presets/repository",["exports","core/ajax"],(function(_exports,_ajax){var obj;
|
||||
/**
|
||||
* Module to handle presets AJAX requests
|
||||
*
|
||||
* @module tool_admin_presets/repository
|
||||
* @copyright 2024 David Carrillo <davidmc@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.deletePreset=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.deletePreset=presetId=>{const request={methodname:"tool_admin_presets_delete_preset",args:{id:presetId}};return _ajax.default.call([request])[0]}}));
|
||||
|
||||
//# sourceMappingURL=repository.min.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"repository.min.js","sources":["../src/repository.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Module to handle presets AJAX requests\n *\n * @module tool_admin_presets/repository\n * @copyright 2024 David Carrillo <davidmc@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\n\n/**\n * Delete given preset\n *\n * @param {Number} presetId\n * @return {Promise}\n */\nexport const deletePreset = presetId => {\n const request = {\n methodname: 'tool_admin_presets_delete_preset',\n args: {id: presetId}\n };\n\n return Ajax.call([request])[0];\n};\n"],"names":["presetId","request","methodname","args","id","Ajax","call"],"mappings":";;;;;;;oKA+B4BA,iBAClBC,QAAU,CACZC,WAAY,mCACZC,KAAM,CAACC,GAAIJ,kBAGRK,cAAKC,KAAK,CAACL,UAAU"}
|
||||
@@ -1,82 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Admin presets list management
|
||||
*
|
||||
* @module tool_admin_presets/admin_presets_list
|
||||
* @copyright 2024 David Carrillo <davidmc@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
import {dispatchEvent} from 'core/event_dispatcher';
|
||||
import Notification from 'core/notification';
|
||||
import {prefetchStrings} from 'core/prefetch';
|
||||
import {getString} from 'core/str';
|
||||
import Pending from 'core/pending';
|
||||
import * as reportEvents from 'core_reportbuilder/local/events';
|
||||
import * as reportSelectors from 'core_reportbuilder/local/selectors';
|
||||
import {add as addToast} from 'core/toast';
|
||||
import {deletePreset} from 'tool_admin_presets/repository';
|
||||
|
||||
/**
|
||||
* Initialise module
|
||||
*/
|
||||
export const init = () => {
|
||||
prefetchStrings('core', [
|
||||
'delete',
|
||||
]);
|
||||
|
||||
prefetchStrings('tool_admin_presets', [
|
||||
'deleteshow',
|
||||
'deletepreset',
|
||||
'eventpresetdeleted',
|
||||
'deletepreviouslyapplied'
|
||||
]);
|
||||
|
||||
document.addEventListener('click', event => {
|
||||
const presetDelete = event.target.closest('[data-action="admin-preset-delete"]');
|
||||
if (presetDelete) {
|
||||
event.preventDefault();
|
||||
|
||||
// Use triggerElement to return focus to the action menu toggle.
|
||||
const triggerElement = presetDelete.closest('.dropdown').querySelector('.dropdown-toggle');
|
||||
const stringid = presetDelete.dataset.presetRollback ? 'deletepreviouslyapplied' : 'deletepreset';
|
||||
|
||||
/* eslint-disable promise/no-nesting */
|
||||
Notification.saveCancelPromise(
|
||||
getString('deleteshow', 'tool_admin_presets'),
|
||||
getString(stringid, 'tool_admin_presets', presetDelete.dataset.presetName),
|
||||
getString('delete', 'core'),
|
||||
{triggerElement}
|
||||
).then(() => {
|
||||
const pendingPromise = new Pending('tool/admin_presets:deletepreset');
|
||||
const reportElement = event.target.closest(reportSelectors.regions.report);
|
||||
|
||||
return deletePreset(presetDelete.dataset.presetId)
|
||||
.then(() => addToast(getString('eventpresetdeleted', 'tool_admin_presets')))
|
||||
.then(() => {
|
||||
dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);
|
||||
return pendingPromise.resolve();
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
}).catch(() => {
|
||||
return;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user