Compare commits
1766 Commits
v4.2.0-rc2
..
v3.8.8
| Author | SHA1 | Date | |
|---|---|---|---|
| 6f7e3ca2f7 | |||
| b2c0deeae8 | |||
| 43222b3990 | |||
| 552c8dfaee | |||
| 72ce91171d | |||
| a5d39f81c8 | |||
| f020baba0d | |||
| 3044e7f136 | |||
| a8bfd397d7 | |||
| 02fecb2c1e | |||
| e99235d097 | |||
| 74706d5f30 | |||
| 050f6c9c08 | |||
| 955f74c2e4 | |||
| 9cb1dd4e5d | |||
| d5eea29933 | |||
| 671f2b2eab | |||
| 4295aad82a | |||
| 363c471929 | |||
| 72e54c8125 | |||
| 91c5e66892 | |||
| 9feac7b454 | |||
| 9f2d67adcf | |||
| a0cfe03ab7 | |||
| 410364509e | |||
| 3958c324f7 | |||
| 6f555e3dc5 | |||
| 8a3908b13e | |||
| d1084fb368 | |||
| ae201b9bb9 | |||
| d2a181feb2 | |||
| 8b1a5d6cfe | |||
| b6288afc2f | |||
| 0d53af4307 | |||
| 855457ad33 | |||
| c5d829351d | |||
| 48410fa3b4 | |||
| 323c12674a | |||
| 37057940b0 | |||
| b494696246 | |||
| 1689e0d02d | |||
| a2ecb5fee0 | |||
| ce6c648a5c | |||
| 708708389a | |||
| 882240bea8 | |||
| 586b1a6f42 | |||
| a1e9aac687 | |||
| f043935fe9 | |||
| 87ff32287f | |||
| 614af534e9 | |||
| f0bb4a442a | |||
| 680cb4c2bc | |||
| bbf25a740b | |||
| fdf9d2c0d7 | |||
| 38a5690419 | |||
| 38aec55521 | |||
| b820cbd7b2 | |||
| 7595f8d9f1 | |||
| 9134ec4414 | |||
| 4ca510ef8f | |||
| 2eb25578d7 | |||
| 40b559b8a2 | |||
| 9b7dec7967 | |||
| 540c5032ec | |||
| 2f627b199a | |||
| 9590644949 | |||
| b3eb0b4ed8 | |||
| 9e09004bf9 | |||
| ac62bf1f10 | |||
| da7a9d4a5b | |||
| 15deb6037a | |||
| 7ba62ce566 | |||
| 6fb9ff4a8d | |||
| c1b5bd5f2c | |||
| 90645731dd | |||
| 617a39a855 | |||
| 733a066172 | |||
| ef5abe6f1a | |||
| 997a6d6aea | |||
| 782d9e0a70 | |||
| 899ebabd4a | |||
| 979acac249 | |||
| 68636f2c58 | |||
| 88642c8f8d | |||
| fa5227be64 | |||
| 5460610eab | |||
| f00ea29c80 | |||
| 10bf4c6c69 | |||
| 4cd4d59006 | |||
| 461552b315 | |||
| d2e4b192ec | |||
| 8cfd3f2b72 | |||
| 885cb5e157 | |||
| 4f6fe6ceb1 | |||
| 8a3b7b90e8 | |||
| fd2c79545f | |||
| e1bb0014c1 | |||
| bcfa789822 | |||
| e9cb4f23a5 | |||
| a476c8f0bd | |||
| 50285a5011 | |||
| 069e41e2e0 | |||
| 2f842d0d45 | |||
| 6e7d2022d1 | |||
| 161a902514 | |||
| 4c4c49036c | |||
| 6aeca2cc3b | |||
| 85ca7d98de | |||
| cc724b40f1 | |||
| be139ca945 | |||
| 3cb4a45cb4 | |||
| dee26a0fbd | |||
| 6cacbf2d63 | |||
| 56c8c6b292 | |||
| 1db6212c68 | |||
| 29bf8d5176 | |||
| 581c909321 | |||
| 7cbdc8a996 | |||
| fd62f6e03e | |||
| 8ae2fb1773 | |||
| f577d6f6c3 | |||
| ae166e60bb | |||
| 24459b780e | |||
| d476304b38 | |||
| 1bd64473f0 | |||
| bb9129ae61 | |||
| 08c25c25fe | |||
| 63c147d6b1 | |||
| 9101b9006e | |||
| deab3d8e45 | |||
| c482ea0df5 | |||
| 1402cad6fd | |||
| 17fa0a030e | |||
| 1620feb69f | |||
| 46353d4017 | |||
| 224efa43c6 | |||
| 20d6adc2ad | |||
| 5daa405eaa | |||
| bf91c850ec | |||
| 57fb23822b | |||
| 2dfebe709e | |||
| 130707d4e2 | |||
| 8658c28687 | |||
| 64372b6d2f | |||
| c5c5e74568 | |||
| 8a60f4ba20 | |||
| 06eeb0fb89 | |||
| 7d6d3ee26f | |||
| 6ba22833e2 | |||
| 87dca5dfd7 | |||
| f93c934043 | |||
| 37757a69cd | |||
| bda7205584 | |||
| 76e8f1f260 | |||
| 4ac4c9a6c9 | |||
| 46fcf48619 | |||
| 57c5a07aaa | |||
| b7f6f951ca | |||
| ccc768d30b | |||
| 483ff0bb44 | |||
| 6f54a6cdd3 | |||
| e1ee9ea78c | |||
| b7d173d89e | |||
| 3eb031e018 | |||
| cdfc0502fa | |||
| 7670873a09 | |||
| f61badb187 | |||
| db8aa0eda1 | |||
| 4f05d0ecb7 | |||
| d66ced33a2 | |||
| baa6a41975 | |||
| 4cf56a012d | |||
| 3b96a8b88b | |||
| 5d357f9849 | |||
| 5d9a5d8fef | |||
| 0e75bc4ded | |||
| 08c84a9d72 | |||
| 2dc4c36983 | |||
| f3ffadb77b | |||
| 251803de94 | |||
| 1c067b30a7 | |||
| 80878e887f | |||
| e2c2d15f09 | |||
| 988f42c05a | |||
| 4051117877 | |||
| b98729c600 | |||
| 6fbebf7cd7 | |||
| 9f4b10f606 | |||
| f743468808 | |||
| a6ca410069 | |||
| 482d85942f | |||
| 7b6fbd9861 | |||
| a49559f222 | |||
| 320c5ec06c | |||
| 698f99209d | |||
| 89ef8cc223 | |||
| e3db7a4767 | |||
| 22e41d742f | |||
| 22b4979999 | |||
| ded0ed9d37 | |||
| 981bce6288 | |||
| 3883bba766 | |||
| 9aaab50cd5 | |||
| 60ae32c162 | |||
| 261d79a5ed | |||
| fd9db57264 | |||
| bc56a460b7 | |||
| f6eafcae3e | |||
| e7aa9549a5 | |||
| 3a12cb5495 | |||
| 6d79bed31b | |||
| 34ea79f1d4 | |||
| 444c2e1df1 | |||
| 965846f005 | |||
| 3317f7aa94 | |||
| e523e85462 | |||
| 82a400be59 | |||
| 30268a1fe1 | |||
| 66de9e27be | |||
| 2cd98f15b3 | |||
| e92ac3eaf7 | |||
| a04cc0b511 | |||
| 81e84d2034 | |||
| 7af6028c51 | |||
| e92ffed40d | |||
| 9fad671e5d | |||
| 2020abe0d8 | |||
| 8c461fc2be | |||
| 064f9943a5 | |||
| 0df84ece36 | |||
| 15a185340a | |||
| b4b15278ea | |||
| cd258845b9 | |||
| f7500502cc | |||
| af2a02e460 | |||
| 73fe545030 | |||
| 3ee3bef023 | |||
| e2558ec396 | |||
| 6a22d520ee | |||
| d5f01f6a98 | |||
| 7400b95a23 | |||
| 0df4a06a97 | |||
| 49f5600cf5 | |||
| 46000ff13a | |||
| 6879b59ec1 | |||
| df6b15c7dc | |||
| c209325895 | |||
| bfe808fd62 | |||
| 19a6a2fcc0 | |||
| 7c68554277 | |||
| cf149b7e39 | |||
| 125cbf474e | |||
| 1339e312a6 | |||
| 10f88d9ab6 | |||
| 26b34e8570 | |||
| ba24d5c214 | |||
| a007895a0a | |||
| 973631628c | |||
| 9167c6d47c | |||
| 7119de9e92 | |||
| 00492277ae | |||
| b773f03809 | |||
| 8fc32d4c3e | |||
| 19cce33460 | |||
| 323a15ba58 | |||
| 8912dcbe15 | |||
| 3154901c1f | |||
| f24a2515cb | |||
| ffa8aa21b8 | |||
| 8a15c4e0ca | |||
| 1eaf199454 | |||
| 47fe02cbea | |||
| 82a1f6cd33 | |||
| 80b632c94a | |||
| efcc68a5ce | |||
| 800f4dfb15 | |||
| a1ebb252cb | |||
| c673616612 | |||
| 2262d926eb | |||
| deb0ad9b62 | |||
| 3b52d1658e | |||
| dba8fdf878 | |||
| 2178327a55 | |||
| bdb752aa39 | |||
| 0a238780e7 | |||
| 91984f38f7 | |||
| e01066d67c | |||
| 7069947949 | |||
| 620e5285fe | |||
| 99b1742961 | |||
| cf6005dc08 | |||
| cba7517ca7 | |||
| ace99685b5 | |||
| 979f22b0ea | |||
| 4fc00c4b68 | |||
| 6018a70469 | |||
| 5c2c14b32b | |||
| 53827dd118 | |||
| ba7d2b6c05 | |||
| ed55aff5c9 | |||
| f710fba95b | |||
| 5b2fb7946a | |||
| 58e775946a | |||
| debc5e451d | |||
| 09c7f259dc | |||
| d6cb91de83 | |||
| 64153ea77f | |||
| 3a1f0f9641 | |||
| df4eab54cb | |||
| 85cfba7f21 | |||
| bd7cfe1981 | |||
| 8849a99cc0 | |||
| 4b6827d042 | |||
| eab36a73fb | |||
| ff6939dce4 | |||
| a13ccd39ec | |||
| b60c6b3bdf | |||
| 71ca64f7f8 | |||
| 92111f008f | |||
| 284baff563 | |||
| 1fc9aa9cc4 | |||
| b21d86f807 | |||
| 0268610d25 | |||
| 472d293a16 | |||
| 168fa1dda1 | |||
| a8e70bad23 | |||
| 2072b9a9e3 | |||
| f15f0b1cdf | |||
| d2dd04ff2d | |||
| 7ed55774f1 | |||
| 784156217a | |||
| 0425db5a80 | |||
| 6e1451fb39 | |||
| 8c274e82f4 | |||
| 4a488b9a5c | |||
| 11e49d6676 | |||
| 6f4e2cb799 | |||
| 7790b6727a | |||
| 20f7e23af4 | |||
| 52b6d73c03 | |||
| 78eacf034a | |||
| 67e3dbab7f | |||
| ed3482f9cc | |||
| 3852238df4 | |||
| 1c51eaa50c | |||
| c329d99d8a | |||
| f48f213e40 | |||
| c2686daae4 | |||
| 28a155e712 | |||
| f90459df6f | |||
| 859a49e2ac | |||
| bf0778595f | |||
| 3a257a2808 | |||
| 45f5453b2a | |||
| b74d8568eb | |||
| 9f770c1be6 | |||
| 049bb1d7a7 | |||
| 687047148f | |||
| ab48f37f68 | |||
| 6180264ae8 | |||
| b7905dd4e0 | |||
| 8d470c5802 | |||
| 1d183e38b1 | |||
| 80efaa9828 | |||
| 25c47ba407 | |||
| 33f880fba7 | |||
| 9e9a1db47a | |||
| a711c69d8f | |||
| 2a98eedef0 | |||
| dadccb9237 | |||
| 2ce0766cfc | |||
| d456737d72 | |||
| cef948595b | |||
| 24bf9c39d9 | |||
| adca4fe505 | |||
| a44ce9a024 | |||
| fde001145b | |||
| 85ffac56e8 | |||
| 11b4e810fb | |||
| ab1545b20e | |||
| c3673a9be1 | |||
| 9283ba7380 | |||
| bb41dd7921 | |||
| 8305c3ce9a | |||
| 1592b68f0b | |||
| faf7b144a1 | |||
| 08c359f366 | |||
| b03196fe78 | |||
| ec92de9187 | |||
| c3fcedf205 | |||
| d2dec45ec8 | |||
| 84564c42c8 | |||
| 1084eba9a8 | |||
| 6895074a62 | |||
| d79cd16ae7 | |||
| 9579c751ab | |||
| 1cb1763216 | |||
| 2fb006bfb1 | |||
| fbc937f4b4 | |||
| e30b6117f7 | |||
| d0d16bd8fb | |||
| dc378c70e3 | |||
| 15aa7e0b20 | |||
| 5192605a11 | |||
| c6d2515534 | |||
| 4f3271c536 | |||
| b4025d8655 | |||
| a4ab0db4bd | |||
| 972f50ff90 | |||
| 5aeec826f8 | |||
| 94d2dcb8e1 | |||
| c8273f7d7f | |||
| affb1ae2ff | |||
| fea72a6eb3 | |||
| 64d0d94aeb | |||
| 9b4d87bcc2 | |||
| 18740e4938 | |||
| 261be42ab5 | |||
| 181bef2ed6 | |||
| 94bd3452e4 | |||
| fb9670a9b2 | |||
| 00cbbbc930 | |||
| bb31c5eb3e | |||
| 9d5a987a97 | |||
| a1b7b8b98f | |||
| 1e157fb528 | |||
| b5c1e5379e | |||
| c78a136be4 | |||
| 978fff44ab | |||
| 3b212136de | |||
| 9ffab782f7 | |||
| 30ccbb075c | |||
| 793823bca8 | |||
| dd8b086494 | |||
| 30b2b58c27 | |||
| 8c5a8a25b5 | |||
| f6c9f72a76 | |||
| 7e1223958c | |||
| 43362649e5 | |||
| bddf4eecf6 | |||
| e881a516d0 | |||
| 62c1a7b816 | |||
| 0bca22b30f | |||
| 45d06fe9b6 | |||
| 43930d5e8a | |||
| b226665e80 | |||
| edd91d16fa | |||
| 5c3bdc9259 | |||
| 8dc60e1926 | |||
| b91869f889 | |||
| 25128d16aa | |||
| 62cdf371df | |||
| e6bf955210 | |||
| 4ec7c29b30 | |||
| cf6cdd99d4 | |||
| 1ed9fa8f4a | |||
| f520dbe674 | |||
| b61392fa53 | |||
| c4fc929e3f | |||
| ae320424ef | |||
| 2d14596f5a | |||
| 11e171ffe5 | |||
| 3d5679803d | |||
| bdea78ba61 | |||
| c6fb7763bb | |||
| 1d412ff1df | |||
| 2e93f26f2f | |||
| 81dfa11ba8 | |||
| 6a6caa019f | |||
| c1b3ba3e07 | |||
| 5d412e18e7 | |||
| b73bb7b9c4 | |||
| d4d744d544 | |||
| 4cdcf3a407 | |||
| a0283e7bb3 | |||
| 0ae11f6123 | |||
| c1efa9acf4 | |||
| 5ed2aef513 | |||
| 1466bef278 | |||
| 7132e1a398 | |||
| 4328b93712 | |||
| bdfc738c6f | |||
| 07d4b70470 | |||
| de58d4b0a1 | |||
| 349ebe7f45 | |||
| fe39303913 | |||
| 7abc2da93b | |||
| bf9d8d79d5 | |||
| 155332f4aa | |||
| 24c68767e5 | |||
| 5b38dd9e1e | |||
| 72f730972a | |||
| f5c7fcd181 | |||
| 2d436520dc | |||
| eb5d0d7d59 | |||
| d158589158 | |||
| 268e3b893a | |||
| 54004f286e | |||
| 12baca4b15 | |||
| 08e86bd7b1 | |||
| 36e03f1596 | |||
| b1341895b1 | |||
| 6d3dca55f8 | |||
| d86741395e | |||
| 0f6c21c836 | |||
| 8ae39ac69e | |||
| 957baa0871 | |||
| 678bcecc0d | |||
| 06f6b499eb | |||
| 88aa4e2071 | |||
| 49403f979a | |||
| 28047fa3b1 | |||
| 98292864e8 | |||
| 71f3a7fca9 | |||
| 8a416d4e63 | |||
| 94f4f39ab7 | |||
| ad30b41f91 | |||
| 7803827502 | |||
| a6986b3e36 | |||
| 73b7597a8c | |||
| 9b54ddd465 | |||
| 39dc97aef9 | |||
| 8351210984 | |||
| bfce37757f | |||
| b4d8be2042 | |||
| 7d3b148e94 | |||
| 595e05d2a7 | |||
| 4a776d2f67 | |||
| 975c1d4438 | |||
| 019e7dd26c | |||
| 1b479562cd | |||
| d2b35aea26 | |||
| 3678ad0949 | |||
| 8c4893ffdc | |||
| bab00d9b8c | |||
| 88723a6a1d | |||
| 035c042f1e | |||
| 6ed3f966b7 | |||
| a7f70278c8 | |||
| ae1e1dfbd6 | |||
| 5f417739f3 | |||
| 0d2f34b40d | |||
| e0aa753fe1 | |||
| f001dc447e | |||
| cc80dec71e | |||
| b89ebd255c | |||
| 0c773a6a16 | |||
| b45835b7b8 | |||
| 5aacc83bc4 | |||
| ebb0e91226 | |||
| 3ecf4dff52 | |||
| 3f2b9661d8 | |||
| 6b3d7a34dd | |||
| 2e38a7cc40 | |||
| 95269fdb77 | |||
| 5a1745b254 | |||
| e271e28e78 | |||
| 9cfcb4bf8a | |||
| f25f8f0701 | |||
| 03670af6da | |||
| 13f06f7a8e | |||
| a080d4c0df | |||
| ab7849c105 | |||
| 756e148f31 | |||
| 5a2ca41bba | |||
| c9d1d8141f | |||
| 34616581c5 | |||
| 33ec9e3a37 | |||
| 147b984630 | |||
| 4a2a779e12 | |||
| 6364b15e24 | |||
| b71744bb4d | |||
| 241eacaa7a | |||
| 37f3c318ea | |||
| 4a6de87cae | |||
| a89aaec1af | |||
| 00df049b9a | |||
| a37313a6d1 | |||
| b3c5e4f5d7 | |||
| 5eb38638bb | |||
| aeb85b66f2 | |||
| 7ba288d1d0 | |||
| d7e8f35e91 | |||
| db6f80a784 | |||
| b20a875381 | |||
| f3d6fa182b | |||
| 68a6b6db8d | |||
| 9a66680ae1 | |||
| 8d90570067 | |||
| a30f7f13ea | |||
| d780b1866e | |||
| 273ef0b4e3 | |||
| 8402724494 | |||
| 83b5aea43c | |||
| 51f4f8c612 | |||
| 5d9fb70f70 | |||
| 0368c8e2a8 | |||
| 9fea1cc6e4 | |||
| b09e569189 | |||
| 9afc404947 | |||
| 70d6af63ba | |||
| 4338982098 | |||
| 8634f2b672 | |||
| 678ca3ded7 | |||
| 7ec2bf3776 | |||
| a6227d5675 | |||
| e4d47ed2cc | |||
| 4d069c9339 | |||
| 3e741db933 | |||
| 90221b5ccf | |||
| 2b36e5be2e | |||
| f2de1293e1 | |||
| ea79b6597a | |||
| 3b97a56c6a | |||
| 07414b90e3 | |||
| d49c18bba5 | |||
| 82ddfd7ea6 | |||
| ecc61a125b | |||
| d8538f29c4 | |||
| 881b487433 | |||
| da91dd3ea0 | |||
| e0c0a0c05c | |||
| 246a03f7dd | |||
| 121373ec8f | |||
| 8fb613ee9f | |||
| 44fd35e8ee | |||
| 63b646acb1 | |||
| a28a24e2f2 | |||
| 3639dc845e | |||
| d63c297351 | |||
| 54faefbefb | |||
| 8c3e24b46c | |||
| 735de9020e | |||
| 71e5f30504 | |||
| 5b9c5ccf83 | |||
| 46b6ca69d1 | |||
| dbd41fa613 | |||
| 381f45d005 | |||
| 0397309abf | |||
| dca95c6fe4 | |||
| 112fc4d513 | |||
| 3b81550b8e | |||
| 0cf43f11f0 | |||
| 035f5d9423 | |||
| 1bae8bd482 | |||
| 3bd1b121ed | |||
| db58c4cdf2 | |||
| b730bb5702 | |||
| d45a6dd3c3 | |||
| 0097d8d481 | |||
| ef6538a8c7 | |||
| 7a54747c9a | |||
| fe65ebcf1c | |||
| bc2d3e8bb9 | |||
| d6ce289176 | |||
| d690ed3dd3 | |||
| 704153c454 | |||
| 37ee0fd966 | |||
| 58bf57d9d3 | |||
| 71d4cc2f4e | |||
| 913b96cb21 | |||
| 7c2fef6484 | |||
| 687c727cf9 | |||
| ee6d899130 | |||
| c18b08912f | |||
| a46018529b | |||
| 5ae95f265a | |||
| 456530205e | |||
| 6bfddeea21 | |||
| 4557b7fed2 | |||
| d93d8f65a8 | |||
| 406d9bb40f | |||
| cd8c136c70 | |||
| a66d07b15c | |||
| 44b19c47bf | |||
| 81b393a717 | |||
| 6fb0f6f941 | |||
| 4d26e345d0 | |||
| 1c8b779f27 | |||
| a4099f2932 | |||
| 2557a7f8ff | |||
| 9518c81ab8 | |||
| befe7cce8d | |||
| 2dc698fb17 | |||
| 898415efec | |||
| 5b5233321e | |||
| 4d4ad0fe60 | |||
| 8fcb1b459d | |||
| 8816107de4 | |||
| 42cf09b5b4 | |||
| 408c793ed7 | |||
| 6324b63372 | |||
| 3c91dc0784 | |||
| 4d3658f7d8 | |||
| bcbc91091c | |||
| d57acc9d80 | |||
| bc125390bb | |||
| f097a1a20f | |||
| adc032b170 | |||
| 6c8f7e1ce2 | |||
| 8cb7cb1fdc | |||
| 5f6ce419c4 | |||
| 74235813b3 | |||
| 409baab766 | |||
| ee884c9728 | |||
| c6a9898ed0 | |||
| 80c3dcb8bf | |||
| 3c142d923f | |||
| 53e4c04237 | |||
| 8cf375b627 | |||
| 5e7c083dba | |||
| 7d896212f2 | |||
| 3a6950ccd7 | |||
| 24562c031c | |||
| deca8028d9 | |||
| 3063e8415a | |||
| ccbe61ee98 | |||
| ef874ad928 | |||
| 9a33db6bea | |||
| b51501b403 | |||
| 4f142bccce | |||
| dac9cc3139 | |||
| 281c882327 | |||
| bbc79e490a | |||
| 666365e46b | |||
| 28509546d3 | |||
| a860a76a68 | |||
| 8d650d9a01 | |||
| bbaab1109e | |||
| c8a07a5b68 | |||
| abb076ebbd | |||
| 56d6a5d731 | |||
| 59417f0b1d | |||
| 4180fe0f42 | |||
| 8a53b489bb | |||
| fecb0dcceb | |||
| 2b0ae390d7 | |||
| 1f274f5032 | |||
| 6f0115e399 | |||
| 4369ef0304 | |||
| fe3d7cdbaa | |||
| c852856ba7 | |||
| 019420fd11 | |||
| c2263f903d | |||
| 719333e0ec | |||
| 3a98b992e9 | |||
| 27a12aba40 | |||
| b529ffad65 | |||
| b5b8b1a486 | |||
| 2f360b90c5 | |||
| efa47a2ba5 | |||
| 4b47df0236 | |||
| d9fe693347 | |||
| 74206f3d78 | |||
| 0fce71647f | |||
| 3d50844c9f | |||
| 661c7a1988 | |||
| f8f58c441b | |||
| cdb0af69e1 | |||
| 85da65606b | |||
| 6e64255df2 | |||
| ec602d2f26 | |||
| 6079a72373 | |||
| 111fc893da | |||
| 5d7f88d98b | |||
| 3eb69a0a9e | |||
| 465c6e7c45 | |||
| 46323748fa | |||
| 32d28f8fac | |||
| 2591f4ce79 | |||
| c3764823cd | |||
| 49e32e25ff | |||
| 46962930f7 | |||
| 4c97f30360 | |||
| 35282e4d1a | |||
| 78f4960bce | |||
| 6fa2f55cd0 | |||
| d3756efc83 | |||
| 261804e95c | |||
| 53c46ffb2e | |||
| 7ba5f4896e | |||
| 3a76c07806 | |||
| 1794636ee6 | |||
| 24906971d7 | |||
| 1ac59631d8 | |||
| 4a77582d16 | |||
| 2140fe5549 | |||
| fc13b8c1a0 | |||
| f4163d2748 | |||
| a90af06899 | |||
| 81e276c743 | |||
| 45da2ac9d3 | |||
| 8c83815ab1 | |||
| e34877300c | |||
| e917a595b2 | |||
| 852cda447f | |||
| 9e2cb2075c | |||
| 13b26170b9 | |||
| 6f792b5ed8 | |||
| 1612572a62 | |||
| 5c5b0ee137 | |||
| 582e43bebe | |||
| 2ea2d6a72f | |||
| 37d7820816 | |||
| fc2c76e78e | |||
| 4c5684135a | |||
| 35dd608161 | |||
| 9faa136ca0 | |||
| 667d004a17 | |||
| 640838c117 | |||
| f4a77e8381 | |||
| 833d8bceb8 | |||
| bc010acae0 | |||
| 0deabbfbfd | |||
| 404288d3bc | |||
| b16b681a79 | |||
| 14cfb3dcbf | |||
| 074bdceb1f | |||
| c7545dc852 | |||
| cd1a39717a | |||
| c933e28af6 | |||
| 0b62a26225 | |||
| 8a977030fc | |||
| 0ff1309b3a | |||
| 26badfdd1a | |||
| 1dafe5fbe5 | |||
| f8621e3d58 | |||
| e48b197957 | |||
| 7045cd8e67 | |||
| 48583d2740 | |||
| 4ad9de59ea | |||
| b22accf82b | |||
| 99f948b4df | |||
| e94499f9b3 | |||
| 4e60865e2c | |||
| 43268a6a1c | |||
| 38cc6d1071 | |||
| 92ba52e21d | |||
| 13215b2415 | |||
| 0b464b93d8 | |||
| e3b6ebadcc | |||
| 6944da3315 | |||
| e67a6060e0 | |||
| 0aadca7a48 | |||
| 0efd87b0f2 | |||
| d42ad4800d | |||
| 19ebf2dc92 | |||
| a644c50920 | |||
| de19504712 | |||
| ff5773d511 | |||
| 314e9a4064 | |||
| 67ba731f8f | |||
| 2f5490410e | |||
| 34f176586b | |||
| 7a168c3beb | |||
| 0be9bb13c7 | |||
| 55dde65d11 | |||
| 45978220ab | |||
| e8e5f49883 | |||
| efeef8d82d | |||
| 9821d4d9a7 | |||
| f138423971 | |||
| e6d212fc07 | |||
| 1adda884fd | |||
| 4fae2c3001 | |||
| 3427a6ec76 | |||
| ea5c0c6b61 | |||
| fcbc4ccee6 | |||
| dd293c28e1 | |||
| 79c6d0a453 | |||
| 1ac5b70c5a | |||
| 9b38e44dfb | |||
| 57382b0e5e | |||
| 2d2e93c6c6 | |||
| e23b038479 | |||
| a2defe29c4 | |||
| aecc9e7170 | |||
| a30ff32351 | |||
| 1b34fbb716 | |||
| 69e2477f90 | |||
| bca7a73d14 | |||
| c27c7abb6b | |||
| 82d46e5037 | |||
| 4853c245c5 | |||
| 4d56bb43b3 | |||
| 0cbb91e4c0 | |||
| 2ef16003f1 | |||
| 80787a98b3 | |||
| 698f0cff54 | |||
| 0e73dacaaf | |||
| 2ba93cc439 | |||
| cd0af947a7 | |||
| 9b2253898a | |||
| a81508489d | |||
| 3ec67cf4bb | |||
| 365bfab967 | |||
| a8b4b9f407 | |||
| c59656006b | |||
| 84d550cacb | |||
| b238304072 | |||
| 10127ac33d | |||
| 6a7350d44b | |||
| ed19fe85e4 | |||
| bd76f0bb66 | |||
| c78ebc9586 | |||
| d2886d283a | |||
| 3ab743dcd9 | |||
| 6f0506fa9d | |||
| 2faa47c934 | |||
| 6c86327b33 | |||
| 6619bdf99d | |||
| 74860627f2 | |||
| 738da05555 | |||
| 9c07f29d8e | |||
| ed68a025ef | |||
| 5afef2670e | |||
| fda378562d | |||
| 5edc307f7a | |||
| 8f17c2f11c | |||
| b8b889c7c9 | |||
| 2b188d2299 | |||
| 926277865c | |||
| 1f59f848d7 | |||
| b6dfd9d89e | |||
| 837651249b | |||
| c4015ffff4 | |||
| 91d3e7a632 | |||
| b9fc865245 | |||
| 0bda145eaa | |||
| fadae3cfe9 | |||
| 8aa0b41e9b | |||
| e247ec921a | |||
| 1673baf5dd | |||
| 7172233782 | |||
| 4508f85b7b | |||
| bd0a6a98b8 | |||
| 9ae897f9c9 | |||
| 02e93de9e1 | |||
| 1b8bed6664 | |||
| 41c21d354e | |||
| b3c89dafd3 | |||
| 9504266620 | |||
| f1dbea7200 | |||
| 18aa8915f0 | |||
| 6f869765b8 | |||
| ab320de86d | |||
| 871291264b | |||
| cfab3a6e6c | |||
| b1345143ea | |||
| 11e2fc4b0a | |||
| 3cc76c335a | |||
| 5190632f6b | |||
| 8a29ee1722 | |||
| 2ddc2e8430 | |||
| f6f68bd59f | |||
| f2e7e84f0e | |||
| 577e336927 | |||
| 69ae995d5c | |||
| 2fba5aee43 | |||
| 2564088c4c | |||
| 91d3dc31a6 | |||
| f0e452240e | |||
| 01ee979b0e | |||
| d9245d6f87 | |||
| c9d9fb3ff7 | |||
| 3c67ae2c14 | |||
| 6fc1bf6646 | |||
| ece9b780d5 | |||
| 0bfb8c4d83 | |||
| d95f185556 | |||
| 53611ca744 | |||
| a56ecac082 | |||
| 8f15f3e753 | |||
| 23cd7eb539 | |||
| af9bf5f609 | |||
| d946fcd870 | |||
| 7a53f91feb | |||
| 74b553af3e | |||
| 1cbf0b9513 | |||
| d75dd9318a | |||
| 6d9c9e24a4 | |||
| 7d599aeea4 | |||
| 65e1204528 | |||
| 055bd177fd | |||
| 0d4a715c46 | |||
| 51c65d7bc5 | |||
| af64d73515 | |||
| 27e80b99b7 | |||
| 79542e4f11 | |||
| a58cfea205 | |||
| 114d51f98b | |||
| 5ff9020fe3 | |||
| 92deb62243 | |||
| f769b8f4d0 | |||
| bc113f89cc | |||
| f18b531639 | |||
| a18a00bd2d | |||
| d37c61365a | |||
| 15ffdf736a | |||
| 42eae914f1 | |||
| 6effee8992 | |||
| eeef704d40 | |||
| f42b67ab2c | |||
| 088d4309fe | |||
| 4281fe1070 | |||
| a6f315c1e7 | |||
| 094b66ea85 | |||
| 8af31de77b | |||
| a84620c2d5 | |||
| 6da6114778 | |||
| 59afcdd850 | |||
| 69b0bbde10 | |||
| 656a05bef8 | |||
| 76223a290d | |||
| 801c9c2717 | |||
| fc2497fbc5 | |||
| 6c73d86264 | |||
| 3aa10ddd4f | |||
| 660fa2871a | |||
| 81c011d388 | |||
| 24d7375f0f | |||
| 71caffb7e0 | |||
| 04e9010c92 | |||
| 9912c37afd | |||
| d37757abf1 | |||
| f9b144d6bf | |||
| 85bf5b2e2c | |||
| cc36c39b46 | |||
| 5d2be187a2 | |||
| 10bc73d5b6 | |||
| bde34b41f8 | |||
| 80827f076c | |||
| 059f402a51 | |||
| 9116fd6e97 | |||
| 06fe619327 | |||
| fe2fc1c79b | |||
| 78939c0069 | |||
| db94d504f7 | |||
| cba151a391 | |||
| 00de69b59f | |||
| 072050567a | |||
| 76b745e76a | |||
| cd93d0b9cf | |||
| 11278ad7a3 | |||
| d9c1feece4 | |||
| 35bf8e5c25 | |||
| 666cc20c30 | |||
| e0fd39791c | |||
| dcc4bfa042 | |||
| d0323fb7c6 | |||
| 0a9b84efff | |||
| f8c8576f63 | |||
| 14a4038810 | |||
| d6347d8c3c | |||
| 29dccc952c | |||
| 100c569431 | |||
| b1c442ccc4 | |||
| e45c2fd424 | |||
| 7ada02755d | |||
| f7c1752fb3 | |||
| 210794be38 | |||
| 6278b8af59 | |||
| 9ff584a1e9 | |||
| 194a801160 | |||
| 5af33db074 | |||
| 052c43eec9 | |||
| f43c324b0b | |||
| 64bff8a106 | |||
| 6090492eec | |||
| 3d94784459 | |||
| 4e445b9709 | |||
| 706f17d90f | |||
| de7c77913c | |||
| 485917574e | |||
| c20f4f5efd | |||
| 8896d073e2 | |||
| c08eb88ae9 | |||
| dceb61ac68 | |||
| 79d668fc8f | |||
| 305060c055 | |||
| 49f07dc861 | |||
| d6e96f06cc | |||
| 7d726708ff | |||
| c332400ba6 | |||
| 94a55738ad | |||
| b77c5e78dc | |||
| 5dfebd739f | |||
| cabbb145dd | |||
| 351262b192 | |||
| 74ff19965a | |||
| c1159327b4 | |||
| b5c776a789 | |||
| 0a92167603 | |||
| 7cb0548c1b | |||
| 214542af79 | |||
| 6a0f166fef | |||
| c904d2249f | |||
| 63cf950324 | |||
| 5a63310250 | |||
| 98014f03cd | |||
| 80a8c362ea | |||
| 42396a33e9 | |||
| 46a290b1b9 | |||
| edb85411b8 | |||
| b90f72b304 | |||
| 11b3c817a5 | |||
| bcd723cf97 | |||
| 13413304ef | |||
| 39bf9c63e6 | |||
| 8d6234bedc | |||
| a7ac45bc7d | |||
| 1170bb3465 | |||
| a8c0956b22 | |||
| 231d5161d6 | |||
| 96988de3ff | |||
| 255b396328 | |||
| 525688f879 | |||
| 8effb03227 | |||
| 0b4ac78d6d | |||
| 9c42bab6bc | |||
| 4ea9f5d7db | |||
| 390a7cc8d2 | |||
| b3c6d95eee | |||
| c0c6ffd9d5 | |||
| 8cee84e682 | |||
| 97d21f097d | |||
| aefb25dae5 | |||
| b16cd34860 | |||
| 795a11a328 | |||
| f9ecf3d3af | |||
| 463ad12363 | |||
| 3b1be8fb8f | |||
| 3466e64d62 | |||
| b38841280d | |||
| 81c8aee9f2 | |||
| 1e1eed0aa4 | |||
| d2791a63ec | |||
| ecc9d9c48c | |||
| 3999cbaa6a | |||
| 6e3dfc7927 | |||
| 4a363544eb | |||
| 478c14d75d | |||
| 02ed9c4196 | |||
| 7d4ade8bb1 | |||
| 3eeadb6ae2 | |||
| 548e3f2aef | |||
| 7e6d6f97aa | |||
| 75532d2266 | |||
| 49914a57d4 | |||
| c23192f642 | |||
| fa33530059 | |||
| 02e24d7098 | |||
| 3d247d80bb | |||
| 9f9134e1ae | |||
| 39f07a058f | |||
| 8d0fb6a548 | |||
| 658624d7d0 | |||
| 4390cc47d9 | |||
| d89837abf6 | |||
| b669b3cb86 | |||
| 570faa0c48 | |||
| ed8046b929 | |||
| c155794221 | |||
| c1707b2b50 | |||
| eaf3679e08 | |||
| b6caef105b | |||
| 8eab502760 | |||
| 213d9daf8d | |||
| f09e31b9c2 | |||
| 241edb45ec | |||
| e8c219a187 | |||
| 41831d5651 | |||
| 9fbbb1e46d | |||
| 4e64d26901 | |||
| d9c9c01f26 | |||
| da8426112f | |||
| dc0cbc24f3 | |||
| 98ae29ae24 | |||
| 46fe2c5533 | |||
| cf0270260b | |||
| c84b8e77f1 | |||
| 95d3c122bf | |||
| 4fdcc6a534 | |||
| e1f9aa101b | |||
| cc3985ed02 | |||
| 9d0c43f072 | |||
| c16c0271bb | |||
| bee8448520 | |||
| d23fba1deb | |||
| 1e19d154ba | |||
| d9eea68192 | |||
| e2e6b49aab | |||
| 41a5c8c54d | |||
| b29634d89f | |||
| 3611f682e9 | |||
| 84c87b62f2 | |||
| 9ecde13781 | |||
| 90227d2d76 | |||
| 06c3a4cc55 | |||
| eed0cbe176 | |||
| c78e908404 | |||
| 7862e1fa12 | |||
| 2f2f184cdb | |||
| c96bfdd6a0 | |||
| af97e88071 | |||
| 355f829ad9 | |||
| 8a90179246 | |||
| faf7ab39f2 | |||
| 57cc9b6499 | |||
| 736f6a6602 | |||
| 1c24b2f567 | |||
| c9a466cee7 | |||
| 6b46f1a53a | |||
| 55b717ee66 | |||
| 4d1ccc6700 | |||
| 4293339fc9 | |||
| fef31ac7c1 | |||
| 3f128fff10 | |||
| 1aa7347965 | |||
| 8fba462e74 | |||
| 19b644ed08 | |||
| 1bbace8bd0 | |||
| be9325c7cf | |||
| 836e523edd | |||
| 85162ad8e1 | |||
| 11212ea69b | |||
| 399369fb19 | |||
| 11a8af9012 | |||
| bb24115b88 | |||
| d7bca2339a | |||
| eebe003104 | |||
| 9ce2442ea9 | |||
| 9f4968676a | |||
| fc51caf533 | |||
| 9044d294a2 | |||
| 628c056258 | |||
| cd9828dadf | |||
| 28f2858004 | |||
| 941507e78a | |||
| 1954482cc5 | |||
| 7d8096eb18 | |||
| 1cf6629ba2 | |||
| d403416579 | |||
| 9d23ee9003 | |||
| c706dd09a3 | |||
| 563931d924 | |||
| a552c30018 | |||
| 5288ca8a73 | |||
| 2f7e2ff457 | |||
| 714a816050 | |||
| e490f86d57 | |||
| eccde3bc19 | |||
| 4c16eba0b5 | |||
| ec7ce8f204 | |||
| 47f96d5e9f | |||
| be47691d79 | |||
| 235f139992 | |||
| e10cf2368a | |||
| 7bda43ee60 | |||
| 28298a6fe9 | |||
| ddaee0c87d | |||
| 9515f40ced | |||
| 7949aaceae | |||
| 305807d27b | |||
| 845c943d7f | |||
| 007ab3cf17 | |||
| 8540968781 | |||
| 8bf66cf2d3 | |||
| e0ba5fb608 | |||
| 3687eb52a0 | |||
| 80f4ad43b8 | |||
| 9da23023af | |||
| caf37336d2 | |||
| b3eb933a40 | |||
| b7fe18eb8b | |||
| 4a2e0b810f | |||
| b4a13e9a65 | |||
| 324d931ed0 | |||
| 535be631f8 | |||
| c2a1fb945e | |||
| 9adfd8ccd5 | |||
| 779f58b227 | |||
| a4a667ab06 | |||
| fc7e263912 | |||
| c47d17b93b | |||
| 5aea4d98cb | |||
| b6970abb18 | |||
| d2b37ff747 | |||
| 75d4b0f949 | |||
| d2c96c5be9 | |||
| 80509c0cdd | |||
| 2de3eefda5 | |||
| 5d83769d27 | |||
| 341a509763 | |||
| 0111c671e4 | |||
| a8df356e33 | |||
| 69935b2021 | |||
| b424d7956e | |||
| 34a98290d2 | |||
| 706e99c6d4 | |||
| 169917563c | |||
| 7df5814667 | |||
| fa88a2c32a | |||
| 66fbe3c48b | |||
| 3d20910e88 | |||
| 7d623c4a45 | |||
| 1c447dcc44 | |||
| ae778f4193 | |||
| c1fbab8a81 | |||
| 26143b5d62 | |||
| 7b37466710 | |||
| 09f6b75f70 | |||
| cd0e753519 | |||
| 4d951d4bac | |||
| 421537407d | |||
| 61f6f15177 | |||
| 11a4df56f8 | |||
| 7bde84a705 | |||
| 4d4b22da05 | |||
| 85a43994ca | |||
| fcf3d8b748 | |||
| 663cdc54fe | |||
| 4eeb89d0a7 | |||
| b6aae0fc5b | |||
| 68a3863961 | |||
| 7e17a2a4fc | |||
| bb6281a5c9 | |||
| 4745203bc0 | |||
| f1dd21a3c7 | |||
| 4d90df63d2 | |||
| 37397ed119 | |||
| b9792633c9 | |||
| f5677dadeb | |||
| 199ff6cc6b | |||
| 462002f1ec | |||
| 7fdcd5cb4d | |||
| ca63a87509 | |||
| 1fde9dea67 | |||
| 2359ec14f7 | |||
| 999bb6578b | |||
| 71f62ad8ce | |||
| 8ccb773f9b | |||
| 84a6421857 | |||
| c3fc5fca65 | |||
| bb9176eee4 | |||
| 5048dbbf06 | |||
| a2d619941f | |||
| 5df69d28ad | |||
| 95ecaf2d1e | |||
| 13caf292eb | |||
| 1fae040842 | |||
| 659795e8e8 | |||
| 5e3aad0090 | |||
| 934e913a97 | |||
| a10e70cf56 | |||
| c618f24c70 | |||
| fb13d3a118 | |||
| 90a3ce934e | |||
| 9dd517653c | |||
| 68b1fa37d5 | |||
| df55da0a4b | |||
| f179253ab7 | |||
| 82fa8144a5 | |||
| 103c623b83 | |||
| 9691980198 | |||
| 3ae5f9ce0c | |||
| 8973440f1f | |||
| 71b91e606b | |||
| b079a267b4 | |||
| 5016b08fcc | |||
| c58d4d4591 | |||
| ff3b9e1afa | |||
| 0d3c0e4377 | |||
| 7005e1c999 | |||
| bed550541b | |||
| ca9ff05eba | |||
| 2ff1e5d1fa | |||
| 5bd9eaaef0 | |||
| ede71aea73 | |||
| ab1939e5e6 | |||
| 045d69a61d | |||
| c6e2821be1 | |||
| 75c21efdea | |||
| c852a0d308 | |||
| f722f3ef15 | |||
| e47f0220a6 | |||
| 94e2bc3cac | |||
| 90ae31ecb1 | |||
| feb32ff597 | |||
| e5c2879063 | |||
| c3496f8599 | |||
| 7476acd514 | |||
| 8ad3593d0c | |||
| 7f697f7a44 | |||
| efcc2a7ac5 | |||
| d2ba630385 | |||
| b2dfaa5d3a | |||
| ccb5136b94 | |||
| 89a4d27f34 | |||
| b2e08b7caa | |||
| a946a9aac6 | |||
| ad5a2c9b07 | |||
| 54908ae907 | |||
| 36d2a83aa6 | |||
| e02fbba097 | |||
| d18b6693ad | |||
| 9b1e5ee508 | |||
| c11ad45a99 | |||
| c1ad1072da | |||
| 572c8c74cc | |||
| 4a87ffb336 | |||
| 0419687a7f | |||
| d8cc1761f1 | |||
| 1ff0018969 | |||
| 557a351daf | |||
| 916130a390 | |||
| c3c3d12462 | |||
| c385803441 | |||
| 912ba0f5d0 | |||
| b25bd34adf | |||
| 7163df9a93 | |||
| 1719110c23 | |||
| bb0a08a7b4 | |||
| c2f2919cf8 | |||
| ec2ea6c725 | |||
| 05a89b9b45 | |||
| 5b90fa9f73 | |||
| a2841e3af6 | |||
| de9c260296 | |||
| 2648ab76be | |||
| aa9f7001da | |||
| 5013ed92a2 | |||
| c538c5a1a3 | |||
| 2cb5e23006 | |||
| 85a035b1c5 | |||
| 1f3bc3164e | |||
| 7bfcb01b81 | |||
| 2abbcb4761 | |||
| 9afa3fc326 | |||
| b4466151b0 | |||
| e4ae433b3b | |||
| 7c7932aaa5 | |||
| 5977f2fc3d | |||
| f7355ebc2c | |||
| b80d4a6b26 | |||
| c4e6d74577 | |||
| ac32e20ada | |||
| 37821e0918 | |||
| b1e2c36330 | |||
| 4ea5ab220a | |||
| f2955fae89 | |||
| 0206502461 | |||
| b4779307a2 | |||
| 4ff40b48e7 | |||
| 830ef192ad | |||
| 8b466d2803 | |||
| bd290493b1 | |||
| 6e6b973f0a | |||
| 43b39b0ce9 | |||
| 7523e0c352 | |||
| 77234ad1ef | |||
| 132809e8ff | |||
| e53dadd3a1 | |||
| 7b6f691f3d | |||
| 596cbeb955 | |||
| 4a617ad4c6 | |||
| 9ef53aba3a | |||
| 288d4fc575 | |||
| 68fd5cfaf4 | |||
| df6160f8a1 | |||
| 7434f3abf3 | |||
| abaf1375c1 | |||
| 646e72349d | |||
| 7781e1e2bf | |||
| 41f06cfc92 | |||
| 903428a532 | |||
| 20c78476a3 | |||
| 63b7cd9927 | |||
| dc89ea0e88 | |||
| 52136af780 | |||
| 7b0a8e09c0 | |||
| 13a34d0aa7 | |||
| a6a79e25f4 | |||
| 511dc1cd96 | |||
| df7cacee50 | |||
| 994ec1690d | |||
| b92880fd4a | |||
| 27557f1a55 | |||
| 58c1af595e | |||
| 4c495c2aa3 | |||
| 86362a7fb2 | |||
| f8b9cc01c3 | |||
| 19e3cdbad9 | |||
| bcf7349371 | |||
| 36984d4d1d | |||
| bcb56b9071 | |||
| 82505a878b | |||
| 8e45839933 | |||
| e1eea0cf36 | |||
| cf32c9116b | |||
| cdf31c3d57 | |||
| 0c01f98410 | |||
| e7721e50c6 | |||
| 8f112b1064 | |||
| 65fcabe132 | |||
| 2c76c1893f | |||
| e27ac65f3d | |||
| 3d8f92e4be | |||
| 35fd550dc9 | |||
| 712b21c6e7 | |||
| b5410980aa | |||
| b2c6ab2d0a | |||
| 4fb25b5b6c | |||
| 3467ce09c8 | |||
| 83d4daee8d | |||
| e653281d4a | |||
| 03b372b804 | |||
| c1724722c8 | |||
| d1b3b4af95 | |||
| f9a80888bc | |||
| 0ebf9b16ea | |||
| 01fd8ac565 | |||
| 80a5640ed2 | |||
| decf6b62ae | |||
| 5b31655420 | |||
| 8e725dd819 | |||
| d0c48cd693 | |||
| b2f13181ec | |||
| fcdc318982 | |||
| c89d25a046 | |||
| 77be1e3fe3 | |||
| de7b3e37b3 | |||
| ffd8dfcf27 | |||
| b116f84589 | |||
| 36e70e664b | |||
| 0debc8f826 | |||
| 81801a0685 | |||
| 8a82f51043 | |||
| 68cab31dba | |||
| 3f50c24fc3 | |||
| 9973646ca7 | |||
| f14420c739 | |||
| 70868d8648 | |||
| 8f45c9b6a5 | |||
| 1f5c6c6b7a | |||
| 26999dda23 | |||
| 022206f2fe | |||
| f9e0f61467 | |||
| c07bc83480 | |||
| de2238985d | |||
| 7d7d3520a7 | |||
| f34f8131ba | |||
| 85ba5d1d88 | |||
| e0b8a3b17a | |||
| 5577e472e5 | |||
| 571f508d4e | |||
| 764ec8d7fb | |||
| eb04dc8792 | |||
| 2f19ac90c6 | |||
| 6fefb88819 | |||
| 1b2a8dad89 | |||
| ec63d3eab4 | |||
| 63052bffb0 | |||
| 5f4250dc43 | |||
| b024306e5c | |||
| 0d285e9334 | |||
| bed212f61b | |||
| 14d58697a3 | |||
| e98103354d | |||
| d8dfb98417 | |||
| 3684d9e03c | |||
| 629ef7772f | |||
| 9e57c09d7b | |||
| 949d3f1c02 | |||
| 11524653b0 | |||
| 74299cd64b | |||
| f9b2f6634d | |||
| dafe4c5a78 | |||
| 550c4e1c42 | |||
| 0287928c4b | |||
| 50bcb24e09 | |||
| 701d27e5d9 | |||
| 13d5d3edb1 | |||
| abc8f149ce | |||
| c3a6437220 | |||
| c910982c49 | |||
| 4d5849b0e4 | |||
| cc0b18ec0e | |||
| 3c7ebda9a5 | |||
| 19401adbdd | |||
| a7fb29a8ff | |||
| c53861fd66 | |||
| 88d4a01089 | |||
| b71e7b79e8 | |||
| 6c7199a426 | |||
| 827f3cd471 | |||
| 0260074ba1 | |||
| edab811944 | |||
| 348ceb4631 | |||
| 5544dc1cc7 | |||
| c02d168cd7 | |||
| 53fde6278a | |||
| 25221f73a6 | |||
| b901252f6d | |||
| be73a0ed4e | |||
| e654258446 | |||
| 8c14984028 | |||
| dd6bbf3962 | |||
| 01e84ac720 | |||
| b423c5ad38 | |||
| f70cc95a13 | |||
| d9887f7350 | |||
| f2db42f921 | |||
| b85896f7bc | |||
| 6350a758d7 | |||
| a3ea0db227 | |||
| cd62c520e2 | |||
| 86d4aca10d | |||
| 40ba9d9cfa | |||
| 5c510b92e2 | |||
| 754ecb5768 | |||
| e7a122bba9 | |||
| 13edb4f9f9 | |||
| db394d5b08 | |||
| d18802eb2f | |||
| ead3a41240 | |||
| ae7b1be0b6 | |||
| cca393d2f6 | |||
| 69430157a3 | |||
| f077d9fbea | |||
| 8c20e76d43 | |||
| a0ac535549 | |||
| 11260d5f3f | |||
| 67fc9943b9 | |||
| 29a5f6eaa2 | |||
| a6fd7ada34 | |||
| c86c872db3 | |||
| 7b4848ca80 | |||
| d9e3c11834 | |||
| 1437e35d2d | |||
| 914d5ab82d | |||
| fbf0c8810b | |||
| 25da0b49ea | |||
| 70e47fdc2a | |||
| 3bfdc05d3a | |||
| d64a94713e | |||
| 8d7f1fd428 | |||
| a7268b4f8a | |||
| 4792cc508e | |||
| 7bc625d846 | |||
| 6dc25cc165 | |||
| 747000a2fe | |||
| bec0808b18 | |||
| 7f40272535 | |||
| f9735bbde7 | |||
| 4dc1413cc7 | |||
| 79b6a4acac | |||
| cf50c5f1cb | |||
| fc74a6e6d2 | |||
| a48bd1c4e3 | |||
| ee215ddd43 | |||
| f3e7533b9d | |||
| 921469db8f | |||
| b99f2bce77 | |||
| 7df9eb36cd | |||
| aee09a6357 | |||
| 8ae7a4ad68 | |||
| d911548cf4 | |||
| 7a4b6df0fe | |||
| c8435454a7 | |||
| df2bda2f00 | |||
| bf52b2a3bc | |||
| f130c810f9 | |||
| f0a2800a3a | |||
| d294483e9c | |||
| ca89aca702 | |||
| 7e561cc4a8 | |||
| 57d601bc23 | |||
| 7cdc332930 | |||
| 484eab15e3 | |||
| 0a0d55a3e1 | |||
| 773b1f738f | |||
| 96475f49de | |||
| 9a64cc2b0b | |||
| 56c570f68a | |||
| ab2bb07f2e | |||
| d0aa6bf098 | |||
| 132e638209 | |||
| 6713912510 | |||
| 96c48a5058 | |||
| d3de95f446 | |||
| 6d98038c8f | |||
| 83e5983a88 | |||
| 0ddd9c54bc | |||
| 39bd01c11a | |||
| 1e58e180d4 | |||
| 163bff4a18 | |||
| 910c2ab7b4 | |||
| fcf199b0ec | |||
| bb511f30d6 | |||
| 481ad40e0e | |||
| 031f3a1d4b | |||
| 5548689ead | |||
| f751a8edbe | |||
| 0de719024c | |||
| ac084982cf | |||
| d3158895a8 | |||
| 0e58303ea3 | |||
| f0d380703a | |||
| baf264ebc8 | |||
| e61f7ba324 | |||
| afda296526 | |||
| 6d6b14e8a4 | |||
| 27d6bd5de1 | |||
| 907db7f06b | |||
| 33bfd161cd | |||
| f88b4e9f81 | |||
| 114f0b612f | |||
| a5f1dd6cb1 | |||
| 283cd6db67 | |||
| 3eac4851e3 | |||
| 006ec3c96f | |||
| cabc1dafa9 | |||
| 1b4c878f48 | |||
| 91286bb138 | |||
| 71af7accff | |||
| ce97501b2b | |||
| dadb3cb372 | |||
| 5cb05d8b77 | |||
| e29b355121 | |||
| 95ca6f656e | |||
| fbd1829594 | |||
| 0c91305856 | |||
| c42b488aaf | |||
| 5bde4eb35e | |||
| 1943c03bbb | |||
| 0da427c800 | |||
| 7045a22f3a | |||
| c7f9c7ae0f | |||
| 751e4964b1 | |||
| 5a108a3064 | |||
| 417ff4f035 | |||
| d3a5df14f7 | |||
| 82853489ca | |||
| 9eaea6a7bf | |||
| f11b82df7c | |||
| 5d2f8bd98d | |||
| 023109f1b1 | |||
| 51bedcee13 | |||
| 7cec76396f | |||
| daed88e656 | |||
| 212595bbd0 | |||
| 6cff678c08 | |||
| 980193f55b | |||
| b4f9c2e906 | |||
| 93ed2a597c | |||
| a2c6a69070 | |||
| 7a68a37fda | |||
| 3c1b6ed7bd | |||
| 8ff55e3b1b | |||
| 7d05f78363 | |||
| d0a9211f51 | |||
| e2a0a42003 | |||
| 7e24491f2c | |||
| 4619c8a7e3 | |||
| 2aff0046c1 |
@@ -0,0 +1,90 @@
|
||||
# Generated by "grunt ignorefiles"
|
||||
*/**/yui/src/*/meta/
|
||||
*/**/build/
|
||||
node_modules/
|
||||
vendor/
|
||||
admin/tool/policy/amd/src/jquery-eu-cookie-law-popup.js
|
||||
auth/cas/CAS/
|
||||
cache/stores/mongodb/MongoDB/
|
||||
enrol/lti/ims-blti/
|
||||
filter/algebra/AlgParser.pm
|
||||
filter/tex/mimetex.*
|
||||
lib/editor/atto/plugins/html/yui/src/codemirror/
|
||||
lib/editor/atto/plugins/html/yui/src/beautify/
|
||||
lib/editor/atto/yui/src/rangy/js/*.*
|
||||
lib/editor/tinymce/plugins/pdw/tinymce/
|
||||
lib/editor/tinymce/plugins/spellchecker/rpc.php
|
||||
lib/editor/tinymce/tiny_mce/
|
||||
lib/mlbackend/php/phpml/
|
||||
lib/adodb/
|
||||
lib/bennu/
|
||||
lib/evalmath/
|
||||
lib/phpspreadsheet/
|
||||
lib/google/
|
||||
lib/htmlpurifier/
|
||||
lib/jabber/
|
||||
lib/minify/matthiasmullie-minify/
|
||||
lib/minify/matthiasmullie-pathconverter/
|
||||
lib/pear/HTML/Common.php
|
||||
lib/pear/HTML/QuickForm.php
|
||||
lib/pear/HTML/QuickForm/
|
||||
lib/pear/PEAR.php
|
||||
lib/phpmailer/
|
||||
lib/simplepie/
|
||||
lib/tcpdf/
|
||||
lib/typo3/
|
||||
lib/yuilib/
|
||||
lib/yuilib/gallery/
|
||||
lib/jquery/
|
||||
lib/html2text/
|
||||
lib/markdown/
|
||||
lib/xhprof/
|
||||
lib/horde/
|
||||
lib/requirejs/
|
||||
lib/amd/src/loglevel.js
|
||||
lib/mustache/
|
||||
lib/amd/src/mustache.js
|
||||
lib/graphlib.php
|
||||
lib/php-css-parser/
|
||||
lib/rtlcss/
|
||||
lib/scssphp/
|
||||
lib/spout/
|
||||
lib/amd/src/chartjs-lazy.js
|
||||
lib/maxmind/GeoIp2/
|
||||
lib/maxmind/MaxMind/
|
||||
lib/ltiprovider/
|
||||
lib/amd/src/truncate.js
|
||||
lib/fonts/
|
||||
lib/amd/src/adapter.js
|
||||
lib/validateurlsyntax.php
|
||||
lib/amd/src/popper.js
|
||||
lib/geopattern-php/
|
||||
lib/php-jwt/
|
||||
lib/babel-polyfill/
|
||||
lib/mdn-polyfills/
|
||||
lib/emoji-data/
|
||||
lib/h5p/
|
||||
media/player/videojs/amd/src/video-lazy.js
|
||||
media/player/videojs/amd/src/Youtube-lazy.js
|
||||
media/player/videojs/videojs/
|
||||
media/player/videojs/amd/src/videojs-flash-lazy.js
|
||||
media/player/videojs/videojs/video-js.swf
|
||||
mod/assign/feedback/editpdf/fpdi/
|
||||
repository/s3/S3.php
|
||||
theme/boost/scss/bootstrap/
|
||||
theme/boost/amd/src/alert.js
|
||||
theme/boost/amd/src/button.js
|
||||
theme/boost/amd/src/carousel.js
|
||||
theme/boost/amd/src/collapse.js
|
||||
theme/boost/amd/src/dropdown.js
|
||||
theme/boost/amd/src/index.js
|
||||
theme/boost/amd/src/modal.js
|
||||
theme/boost/amd/src/popover.js
|
||||
theme/boost/amd/src/sanitizer.js
|
||||
theme/boost/amd/src/scrollspy.js
|
||||
theme/boost/amd/src/tab.js
|
||||
theme/boost/amd/src/toast.js
|
||||
theme/boost/amd/src/tooltip.js
|
||||
theme/boost/amd/src/util.js
|
||||
theme/boost/amd/src/tether.js
|
||||
theme/boost/scss/fontawesome/
|
||||
@@ -2,14 +2,9 @@
|
||||
'plugins': [
|
||||
'babel',
|
||||
'promise',
|
||||
'jsdoc',
|
||||
],
|
||||
'env': {
|
||||
'browser': true,
|
||||
// Enable ES6+ features by default.
|
||||
// See http://eslint.org/docs/user-guide/configuring#specifying-environments
|
||||
// Note: The YUI override must exactly match this when disabling the ES6+ version because those features are not supported by Shifter.
|
||||
'es2020': true,
|
||||
'amd': true
|
||||
},
|
||||
'globals': {
|
||||
@@ -87,7 +82,6 @@
|
||||
'no-octal-escape': 'error',
|
||||
'no-proto': 'error',
|
||||
'no-redeclare': 'warn',
|
||||
'no-restricted-globals': ['error', { 'name': 'Notification' }],
|
||||
'no-return-assign': 'error',
|
||||
'no-script-url': 'error',
|
||||
'no-self-assign': 'error',
|
||||
@@ -197,42 +191,27 @@
|
||||
'property': 'str',
|
||||
'message': 'Use AMD module "core/str" or M.util.get_string()'
|
||||
}],
|
||||
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ["**/yui/src/**/*.js"],
|
||||
'env': {
|
||||
// Disable ES6+ for YUI files.
|
||||
'es2020': false,
|
||||
},
|
||||
// Disable some rules which we can't safely define for YUI rollups.
|
||||
rules: {
|
||||
'no-undef': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'no-unused-expressions': 'off',
|
||||
|
||||
// === JSDocs ===
|
||||
"jsdoc/check-access": 'off',
|
||||
"jsdoc/check-alignment": 'off',
|
||||
"jsdoc/check-param-names": 'off',
|
||||
"jsdoc/check-property-names": 'off',
|
||||
"jsdoc/empty-tags": 'off',
|
||||
"jsdoc/implements-on-classes": 'off',
|
||||
"jsdoc/multiline-blocks": 'off',
|
||||
"jsdoc/require-jsdoc": 'off',
|
||||
"jsdoc/require-param": 'off',
|
||||
"jsdoc/require-param-name": 'off',
|
||||
"jsdoc/require-param-type": 'off',
|
||||
"jsdoc/require-property": 'off',
|
||||
"jsdoc/require-property-name": 'off',
|
||||
"jsdoc/require-property-type": 'off',
|
||||
'no-unused-expressions': 'off'
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ["**/amd/src/*.js", "**/amd/src/**/*.js", "Gruntfile.js", ".grunt/*.js", ".grunt/tasks/*.js", "jsdoc.conf.js"],
|
||||
files: ["**/amd/src/*.js", "**/amd/src/**/*.js", "Gruntfile*.js", "babel-plugin-add-module-to-define.js"],
|
||||
// We support es6 now. Woot!
|
||||
env: {
|
||||
es6: true
|
||||
},
|
||||
// We're using babel transpiling so use their parser
|
||||
// for linting.
|
||||
parser: '@babel/eslint-parser',
|
||||
parser: 'babel-eslint',
|
||||
// Check AMD with some slightly stricter rules.
|
||||
rules: {
|
||||
'no-unused-vars': 'error',
|
||||
@@ -259,53 +238,11 @@
|
||||
// === Promises ===
|
||||
// We have Promise now that we're using ES6.
|
||||
'promise/no-native': 'off',
|
||||
'promise/avoid-new': 'off',
|
||||
|
||||
// === JSDocs ===
|
||||
"jsdoc/check-access": [
|
||||
'error',
|
||||
],
|
||||
"jsdoc/check-alignment": 1, // Recommended.
|
||||
"jsdoc/check-param-names": [
|
||||
'error',
|
||||
],
|
||||
"jsdoc/check-property-names": [
|
||||
'error',
|
||||
],
|
||||
"jsdoc/empty-tags": [
|
||||
'error',
|
||||
],
|
||||
"jsdoc/implements-on-classes": [
|
||||
'error',
|
||||
],
|
||||
"jsdoc/multiline-blocks": [
|
||||
'error',
|
||||
],
|
||||
"jsdoc/require-jsdoc": [
|
||||
'error',
|
||||
],
|
||||
"jsdoc/require-param": [
|
||||
'error',
|
||||
],
|
||||
"jsdoc/require-param-name": [
|
||||
'error',
|
||||
],
|
||||
"jsdoc/require-param-type": [
|
||||
'error',
|
||||
],
|
||||
"jsdoc/require-property": [
|
||||
'error',
|
||||
],
|
||||
"jsdoc/require-property-name": [
|
||||
'error',
|
||||
],
|
||||
"jsdoc/require-property-type": [
|
||||
'error',
|
||||
],
|
||||
'promise/avoid-new': 'off'
|
||||
},
|
||||
parserOptions: {
|
||||
'sourceType': 'module',
|
||||
'requireConfigFile': false,
|
||||
'ecmaVersion': 9,
|
||||
'sourceType': 'module'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+3
-7
@@ -3,8 +3,6 @@
|
||||
"Feature": 0,
|
||||
"Background": 2,
|
||||
"Scenario": 2,
|
||||
"Examples": 4,
|
||||
"example": 6,
|
||||
"Step": 4,
|
||||
"given": 4,
|
||||
"and": 4
|
||||
@@ -16,9 +14,7 @@
|
||||
"no-multiple-empty-lines": "on",
|
||||
"no-partially-commented-tag-lines": "on",
|
||||
"no-trailing-spaces": "on",
|
||||
"no-unnamed-features": "on",
|
||||
"no-unnamed-scenarios": "on",
|
||||
"no-scenario-outlines-without-examples": "on",
|
||||
"no-examples-in-scenarios": "on",
|
||||
"new-line-at-eof": ["on", "yes"]
|
||||
"no-unamed-features": "on",
|
||||
"no-unamed-scenarios": "on",
|
||||
"no-scenario-outlines-without-examples": "on"
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Primary donations pages.
|
||||
custom: ["https://moodle.com/donations/", moodle.org]
|
||||
+16
-31
@@ -1,28 +1,21 @@
|
||||
name: Core
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- master
|
||||
- MOODLE_[0-9]+_STABLE
|
||||
tags-ignore:
|
||||
- v[0-9]+.[0-9]+.[0-9]+*
|
||||
on: [push]
|
||||
|
||||
env:
|
||||
php: 8.1
|
||||
php: 7.4
|
||||
|
||||
jobs:
|
||||
Grunt:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-18.04
|
||||
|
||||
steps:
|
||||
- name: Checking out code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Configuring node & npm
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
shell: bash -l {0}
|
||||
run: nvm install
|
||||
|
||||
- name: Installing node stuff
|
||||
run: npm install
|
||||
@@ -54,52 +47,45 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# MySQL builds always run with the lowest PHP supported version.
|
||||
- os: ubuntu-22.04
|
||||
php: 8.0
|
||||
extensions:
|
||||
- os: ubuntu-18.04
|
||||
php: 7.1
|
||||
db: mysqli
|
||||
# PostgreSQL builds always run with the highest PHP supported version.
|
||||
- os: ubuntu-22.04
|
||||
php: 8.1
|
||||
- os: ubuntu-18.04
|
||||
php: 7.4
|
||||
db: pgsql
|
||||
|
||||
steps:
|
||||
- name: Setting up DB mysql
|
||||
if: ${{ matrix.db == 'mysqli' }}
|
||||
uses: moodlehq/mysql-action@v1
|
||||
uses: johanmeiring/mysql-action@tmpfs-patch
|
||||
with:
|
||||
collation server: utf8mb4_bin
|
||||
mysql version: 8.0
|
||||
mysql version: 5.7
|
||||
mysql database: test
|
||||
mysql user: test
|
||||
mysql password: test
|
||||
use tmpfs: true
|
||||
tmpfs size: '1024M'
|
||||
extra conf: --skip-log-bin
|
||||
|
||||
- name: Setting up DB pgsql
|
||||
if: ${{ matrix.db == 'pgsql' }}
|
||||
uses: m4nu56/postgresql-action@v1
|
||||
with:
|
||||
postgresql version: 13
|
||||
postgresql version: 9.6
|
||||
postgresql db: test
|
||||
postgresql user: test
|
||||
postgresql password: test
|
||||
|
||||
- name: Configuring git vars
|
||||
uses: rlespinasse/github-slug-action@v4
|
||||
uses: rlespinasse/github-slug-action@v3.x
|
||||
|
||||
- name: Setting up PHP ${{ matrix.php }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: ${{ matrix.extensions }}
|
||||
ini-values: max_input_vars=5000
|
||||
coverage: none
|
||||
|
||||
- name: Checking out code from ${{ env.GITHUB_REF_SLUG }}
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setting up PHPUnit
|
||||
env:
|
||||
@@ -114,5 +100,4 @@ jobs:
|
||||
- name: Running PHPUnit tests
|
||||
env:
|
||||
dbtype: ${{ matrix.db }}
|
||||
phpunit_options: ${{ secrets.phpunit_options }}
|
||||
run: vendor/bin/phpunit $phpunit_options
|
||||
run: vendor/bin/phpunit -v
|
||||
|
||||
+1
-12
@@ -20,12 +20,12 @@
|
||||
*~
|
||||
#
|
||||
/config.php
|
||||
/lib/editor/tinymce/extra/tools/temp/
|
||||
/tags
|
||||
/TAGS
|
||||
/cscope.*
|
||||
/.patches/
|
||||
/.idea/
|
||||
.phpstorm.*
|
||||
/nbproject/
|
||||
CVS
|
||||
.DS_Store
|
||||
@@ -33,7 +33,6 @@ CVS
|
||||
/.project
|
||||
/.buildpath
|
||||
/.cache
|
||||
.phpunit.result.cache
|
||||
phpunit.xml
|
||||
# Composer support. Do not ignore composer.json, or composer.lock. These should be shipped by us.
|
||||
composer.phar
|
||||
@@ -45,13 +44,3 @@ composer.phar
|
||||
/lib/yuilib/*/*/*-coverage.js
|
||||
atlassian-ide-plugin.xml
|
||||
/node_modules/
|
||||
/.vscode/
|
||||
moodle-plugin-ci.phar
|
||||
.eslintignore
|
||||
.stylelintignore
|
||||
/jsdoc
|
||||
/admin/tool/componentlibrary/docs
|
||||
/admin/tool/componentlibrary/hugo/site/data/my-index.json
|
||||
.hugo_build.lock
|
||||
phpcs.xml
|
||||
jsconfig.json
|
||||
|
||||
@@ -1,169 +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/>.
|
||||
|
||||
/**
|
||||
* This is a babel plugin to add the Moodle module names to the AMD modules
|
||||
* as part of the transpiling process.
|
||||
*
|
||||
* In addition it will also add a return statement for the default export if the
|
||||
* module is using default exports. This is a highly specific Moodle thing because
|
||||
* we're transpiling to AMD and none of the existing Babel 7 plugins work correctly.
|
||||
*
|
||||
* This will fix the issue where an ES6 module using "export default Foo" will be
|
||||
* transpiled into an AMD module that returns {default: Foo}; Instead it will now
|
||||
* just simply return Foo.
|
||||
*
|
||||
* Note: This means all other named exports in that module are ignored and won't be
|
||||
* exported.
|
||||
*
|
||||
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
/* eslint-env node */
|
||||
|
||||
module.exports = ({template, types}) => {
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const cwd = process.cwd();
|
||||
const ComponentList = require(path.join(process.cwd(), '.grunt', 'components.js'));
|
||||
|
||||
/**
|
||||
* Search the list of components that match the given file name
|
||||
* and return the Moodle component for that file, if found.
|
||||
*
|
||||
* Throw an exception if no matching component is found.
|
||||
*
|
||||
* @throws {Error}
|
||||
* @param {string} searchFileName The file name to look for.
|
||||
* @return {string} Moodle component
|
||||
*/
|
||||
function getModuleNameFromFileName(searchFileName) {
|
||||
searchFileName = fs.realpathSync(searchFileName);
|
||||
const relativeFileName = searchFileName.replace(`${cwd}${path.sep}`, '').replace(/\\/g, '/');
|
||||
const [componentPath, file] = relativeFileName.split('/amd/src/');
|
||||
const fileName = file.replace('.js', '');
|
||||
|
||||
// Check subsystems first which require an exact match.
|
||||
const componentName = ComponentList.getComponentFromPath(componentPath);
|
||||
if (componentName) {
|
||||
return `${componentName}/${fileName}`;
|
||||
}
|
||||
|
||||
// This matches the previous PHP behaviour that would throw an exception
|
||||
// if it couldn't parse an AMD file.
|
||||
throw new Error(`Unable to find module name for ${searchFileName} (${componentPath}::${file}}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is heavily inspired by the babel-plugin-add-module-exports plugin.
|
||||
* See: https://github.com/59naga/babel-plugin-add-module-exports
|
||||
*
|
||||
* This is used when we detect a module using "export default Foo;" to make
|
||||
* sure the transpiled code just returns Foo directly rather than an object
|
||||
* with the default property (i.e. {default: Foo}).
|
||||
*
|
||||
* Note: This means that we can't support modules that combine named exports
|
||||
* with a default export.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {String} exportObjectName
|
||||
*/
|
||||
function addModuleExportsDefaults(path, exportObjectName) {
|
||||
const rootPath = path.findParent(path => {
|
||||
return path.key === 'body' || !path.parentPath;
|
||||
});
|
||||
|
||||
// HACK: `path.node.body.push` instead of path.pushContainer(due doesn't work in Plugin.post).
|
||||
// This is hardcoded to work specifically with AMD.
|
||||
rootPath.node.body.push(template(`return ${exportObjectName}.default`)());
|
||||
}
|
||||
|
||||
return {
|
||||
pre() {
|
||||
this.seenDefine = false;
|
||||
this.addedReturnForDefaultExport = false;
|
||||
},
|
||||
visitor: {
|
||||
// Plugin ordering is only respected if we visit the "Program" node.
|
||||
// See: https://babeljs.io/docs/en/plugins.html#plugin-preset-ordering
|
||||
//
|
||||
// We require this to run after the other AMD module transformation so
|
||||
// let's visit the "Program" node.
|
||||
Program: {
|
||||
exit(path) {
|
||||
path.traverse({
|
||||
CallExpression(path) {
|
||||
// If we find a "define" function call.
|
||||
if (!this.seenDefine && path.get('callee').isIdentifier({name: 'define'})) {
|
||||
// We only want to modify the first instance of define that we find.
|
||||
this.seenDefine = true;
|
||||
|
||||
// Get the Moodle component for the file being processed.
|
||||
var moduleName = getModuleNameFromFileName(this.file.opts.filename);
|
||||
|
||||
// The function signature of `define()` is:
|
||||
// define = function (name, deps, callback) {...}
|
||||
// Ensure that if the moduel supplied its own name that it is replaced.
|
||||
if (path.node.arguments.length > 0) {
|
||||
// Ensure that there is only one name.
|
||||
if (path.node.arguments[0].type === 'StringLiteral') {
|
||||
// eslint-disable-next-line
|
||||
console.log(`Replacing module name '${path.node.arguments[0].extra.rawValue}' with ${moduleName}`);
|
||||
path.node.arguments.shift();
|
||||
}
|
||||
}
|
||||
|
||||
// Add the module name as the first argument to the define function.
|
||||
path.node.arguments.unshift(types.stringLiteral(moduleName));
|
||||
// Add a space after the define function in the built file so that previous versions
|
||||
// of Moodle will not try to add the module name to the file when it's being served
|
||||
// by PHP. This forces the regex in PHP to not match for this file.
|
||||
path.node.callee.name = 'define ';
|
||||
}
|
||||
|
||||
// Check for any Object.defineProperty('exports', 'default') calls.
|
||||
if (!this.addedReturnForDefaultExport && path.get('callee').matchesPattern('Object.defineProperty')) {
|
||||
const [identifier, prop] = path.get('arguments');
|
||||
const objectName = identifier.get('name').node;
|
||||
const propertyName = prop.get('value').node;
|
||||
|
||||
if ((objectName === 'exports' || objectName === '_exports') && propertyName === 'default') {
|
||||
addModuleExportsDefaults(path, objectName);
|
||||
this.addedReturnForDefaultExport = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
AssignmentExpression(path) {
|
||||
// Check for an exports.default assignments.
|
||||
if (
|
||||
!this.addedReturnForDefaultExport &&
|
||||
(
|
||||
path.get('left').matchesPattern('exports.default') ||
|
||||
path.get('left').matchesPattern('_exports.default')
|
||||
)
|
||||
) {
|
||||
const objectName = path.get('left.object.name').node;
|
||||
addModuleExportsDefaults(path, objectName);
|
||||
this.addedReturnForDefaultExport = true;
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -1,394 +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/>.
|
||||
|
||||
/**
|
||||
* Helper functions for working with Moodle component names, directories, and sources.
|
||||
*
|
||||
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
/* eslint-env node */
|
||||
|
||||
/** @var {Object} A list of subsystems in Moodle */
|
||||
const componentData = {};
|
||||
|
||||
/**
|
||||
* Load details of all moodle modules.
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
const fetchComponentData = () => {
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const glob = require('glob');
|
||||
const gruntFilePath = process.cwd();
|
||||
|
||||
if (!Object.entries(componentData).length) {
|
||||
componentData.subsystems = {};
|
||||
componentData.pathList = [];
|
||||
|
||||
// Fetch the component definiitions from the distributed JSON file.
|
||||
const components = JSON.parse(fs.readFileSync(`${gruntFilePath}/lib/components.json`));
|
||||
|
||||
// Build the list of moodle subsystems.
|
||||
componentData.subsystems.lib = 'core';
|
||||
componentData.pathList.push(process.cwd() + path.sep + 'lib');
|
||||
for (const [component, thisPath] of Object.entries(components.subsystems)) {
|
||||
if (thisPath) {
|
||||
// Prefix "core_" to the front of the subsystems.
|
||||
componentData.subsystems[thisPath] = `core_${component}`;
|
||||
componentData.pathList.push(process.cwd() + path.sep + thisPath);
|
||||
}
|
||||
}
|
||||
|
||||
// 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]) => {
|
||||
// We don't allow any code in this place..?
|
||||
glob.sync(`${pluginTypePath}/*/version.php`).forEach(versionPath => {
|
||||
const componentPath = fs.realpathSync(path.dirname(versionPath));
|
||||
const componentName = path.basename(componentPath);
|
||||
const frankenstyleName = `${pluginType}_${componentName}`;
|
||||
componentData.components[`${pluginTypePath}/${componentName}`] = frankenstyleName;
|
||||
componentData.pathList.push(componentPath);
|
||||
|
||||
// Look for any subplugins.
|
||||
const subPluginConfigurationFile = `${componentPath}/db/subplugins.json`;
|
||||
if (fs.existsSync(subPluginConfigurationFile)) {
|
||||
const subpluginList = JSON.parse(fs.readFileSync(fs.realpathSync(subPluginConfigurationFile)));
|
||||
|
||||
Object.entries(subpluginList.plugintypes).forEach(([subpluginType, subpluginTypePath]) => {
|
||||
glob.sync(`${subpluginTypePath}/*/version.php`).forEach(versionPath => {
|
||||
const componentPath = fs.realpathSync(path.dirname(versionPath));
|
||||
const componentName = path.basename(componentPath);
|
||||
const frankenstyleName = `${subpluginType}_${componentName}`;
|
||||
|
||||
componentData.components[`${subpluginTypePath}/${componentName}`] = frankenstyleName;
|
||||
componentData.pathList.push(componentPath);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return componentData;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the list of component paths.
|
||||
*
|
||||
* @param {string} relativeTo
|
||||
* @returns {array}
|
||||
*/
|
||||
const getComponentPaths = (relativeTo = '') => fetchComponentData().pathList.map(componentPath => {
|
||||
return componentPath.replace(relativeTo, '');
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the list of paths to build AMD sources.
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
const getAmdSrcGlobList = () => {
|
||||
const globList = [];
|
||||
fetchComponentData().pathList.forEach(componentPath => {
|
||||
globList.push(`${componentPath}/amd/src/*.js`);
|
||||
globList.push(`${componentPath}/amd/src/**/*.js`);
|
||||
});
|
||||
|
||||
return globList;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the list of paths to build YUI sources.
|
||||
*
|
||||
* @param {String} relativeTo
|
||||
* @returns {Array}
|
||||
*/
|
||||
const getYuiSrcGlobList = relativeTo => {
|
||||
const globList = [];
|
||||
fetchComponentData().pathList.forEach(componentPath => {
|
||||
const relativeComponentPath = componentPath.replace(relativeTo, '');
|
||||
globList.push(`${relativeComponentPath}/yui/src/**/*.js`);
|
||||
});
|
||||
|
||||
return globList;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the list of paths to thirdpartylibs.xml.
|
||||
*
|
||||
* @param {String} relativeTo
|
||||
* @returns {Array}
|
||||
*/
|
||||
const getThirdPartyLibsList = relativeTo => {
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
return fetchComponentData().pathList
|
||||
.map(componentPath => path.relative(relativeTo, componentPath) + '/thirdpartylibs.xml')
|
||||
.map(componentPath => componentPath.replace(/\\/g, '/'))
|
||||
.filter(path => fs.existsSync(path))
|
||||
.sort();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the list of thirdparty library paths.
|
||||
*
|
||||
* @returns {array}
|
||||
*/
|
||||
const getThirdPartyPaths = () => {
|
||||
const DOMParser = require('@xmldom/xmldom').DOMParser;
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const xpath = require('xpath');
|
||||
|
||||
const thirdpartyfiles = getThirdPartyLibsList(fs.realpathSync('./'));
|
||||
const libs = ['node_modules/', 'vendor/'];
|
||||
|
||||
const addLibToList = lib => {
|
||||
if (!lib.match('\\*') && fs.statSync(lib).isDirectory()) {
|
||||
// Ensure trailing slash on dirs.
|
||||
lib = lib.replace(/\/?$/, '/');
|
||||
}
|
||||
|
||||
// Look for duplicate paths before adding to array.
|
||||
if (libs.indexOf(lib) === -1) {
|
||||
libs.push(lib);
|
||||
}
|
||||
};
|
||||
|
||||
thirdpartyfiles.forEach(function(file) {
|
||||
const dirname = path.dirname(file);
|
||||
|
||||
const xmlContent = fs.readFileSync(file, 'utf8');
|
||||
const doc = new DOMParser().parseFromString(xmlContent);
|
||||
const nodes = xpath.select("/libraries/library/location/text()", doc);
|
||||
|
||||
nodes.forEach(function(node) {
|
||||
let lib = path.posix.join(dirname, node.toString());
|
||||
addLibToList(lib);
|
||||
});
|
||||
});
|
||||
|
||||
return libs;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the name of the component matching the specified path.
|
||||
*
|
||||
* @param {String} path
|
||||
* @returns {String|null} Name of matching component.
|
||||
*/
|
||||
const getComponentFromPath = path => {
|
||||
const componentList = fetchComponentData().components;
|
||||
|
||||
if (componentList.hasOwnProperty(path)) {
|
||||
return componentList[path];
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether the supplied path, relative to the Gruntfile.js, is in a known component.
|
||||
*
|
||||
* @param {String} checkPath The path to check. This can be with either Windows, or Linux directory separators.
|
||||
* @returns {String|null}
|
||||
*/
|
||||
const getOwningComponentDirectory = checkPath => {
|
||||
const path = require('path');
|
||||
|
||||
// Fetch all components into a reverse sorted array.
|
||||
// This ensures that components which are within the directory of another component match first.
|
||||
const pathList = Object.keys(fetchComponentData().components).sort().reverse();
|
||||
for (const componentPath of pathList) {
|
||||
// If the componentPath is the directory being checked, it will be empty.
|
||||
// If the componentPath is a parent of the directory being checked, the relative directory will not start with ..
|
||||
if (!path.relative(componentPath, checkPath).startsWith('..')) {
|
||||
return componentPath;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the latest tag in a remote GitHub repository.
|
||||
*
|
||||
* @param {string} url The remote repository.
|
||||
* @returns {Array}
|
||||
*/
|
||||
const getRepositoryTags = async(url) => {
|
||||
const gtr = require('git-tags-remote');
|
||||
try {
|
||||
const tags = await gtr.get(url);
|
||||
if (tags !== undefined) {
|
||||
return tags;
|
||||
}
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the list of thirdparty libraries that could be upgraded.
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
const getThirdPartyLibsUpgradable = async() => {
|
||||
const libraries = getThirdPartyLibsData().filter((library) => !!library.repository);
|
||||
const upgradableLibraries = [];
|
||||
const versionCompare = (a, b) => {
|
||||
if (a === b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const aParts = a.split('.');
|
||||
const bParts = b.split('.');
|
||||
|
||||
for (let i = 0; i < Math.min(aParts.length, bParts.length); i++) {
|
||||
const aPart = parseInt(aParts[i], 10);
|
||||
const bPart = parseInt(bParts[i], 10);
|
||||
if (aPart > bPart) {
|
||||
// 1.1.0 > 1.0.9
|
||||
return 1;
|
||||
} else if (aPart < bPart) {
|
||||
// 1.0.9 < 1.1.0
|
||||
return -1;
|
||||
} else {
|
||||
// Same version.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (aParts.length > bParts.length) {
|
||||
// 1.0.1 > 1.0
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 1.0 < 1.0.1
|
||||
return -1;
|
||||
};
|
||||
|
||||
for (let library of libraries) {
|
||||
upgradableLibraries.push(
|
||||
getRepositoryTags(library.repository).then((tagMap) => {
|
||||
library.version = library.version.replace(/^v/, '');
|
||||
const currentVersion = library.version.replace(/moodle-/, '');
|
||||
const currentMajorVersion = library.version.split('.')[0];
|
||||
const tags = [...tagMap]
|
||||
.map((tagData) => tagData[0])
|
||||
.filter((tag) => !tag.match(/(alpha|beta|rc)/))
|
||||
.map((tag) => tag.replace(/^v/, ''))
|
||||
.sort((a, b) => versionCompare(b, a));
|
||||
if (!tags.length) {
|
||||
library.warning = "Unable to find any comparable tags.";
|
||||
return library;
|
||||
}
|
||||
|
||||
library.latestVersion = tags[0];
|
||||
tags.some((tag) => {
|
||||
if (!tag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// See if the version part matches.
|
||||
const majorVersion = tag.split('.')[0];
|
||||
if (majorVersion === currentMajorVersion) {
|
||||
library.latestSameMajorVersion = tag;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
if (versionCompare(currentVersion, library.latestVersion) > 0) {
|
||||
// Moodle somehow has a newer version than the latest version.
|
||||
library.warning = `Newer version found: ${currentVersion} > ${library.latestVersion} for ${library.name}`;
|
||||
return library;
|
||||
}
|
||||
|
||||
|
||||
if (library.version !== library.latestVersion) {
|
||||
// Delete version and add it again at the end of the array. That way, current and new will stay closer.
|
||||
delete library.version;
|
||||
library.version = currentVersion;
|
||||
return library;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return (await Promise.all(upgradableLibraries)).filter((library) => !!library);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the list of thirdparty libraries.
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
const getThirdPartyLibsData = () => {
|
||||
const DOMParser = require('@xmldom/xmldom').DOMParser;
|
||||
const fs = require('fs');
|
||||
const xpath = require('xpath');
|
||||
const path = require('path');
|
||||
|
||||
const libraryList = [];
|
||||
const libraryFields = [
|
||||
'location',
|
||||
'name',
|
||||
'version',
|
||||
'repository',
|
||||
];
|
||||
|
||||
const thirdpartyfiles = getThirdPartyLibsList(fs.realpathSync('./'));
|
||||
thirdpartyfiles.forEach(function(libraryPath) {
|
||||
const xmlContent = fs.readFileSync(libraryPath, 'utf8');
|
||||
const doc = new DOMParser().parseFromString(xmlContent);
|
||||
const libraries = xpath.select("/libraries/library", doc);
|
||||
for (const library of libraries) {
|
||||
const libraryData = [];
|
||||
for (const field of libraryFields) {
|
||||
libraryData[field] = xpath.select(`${field}/text()`, library)?.toString();
|
||||
}
|
||||
libraryData.location = path.join(path.dirname(libraryPath), libraryData.location);
|
||||
libraryList.push(libraryData);
|
||||
}
|
||||
});
|
||||
|
||||
return libraryList.sort((a, b) => a.location.localeCompare(b.location));
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
fetchComponentData,
|
||||
getAmdSrcGlobList,
|
||||
getComponentFromPath,
|
||||
getComponentPaths,
|
||||
getOwningComponentDirectory,
|
||||
getYuiSrcGlobList,
|
||||
getThirdPartyLibsList,
|
||||
getThirdPartyPaths,
|
||||
getThirdPartyLibsUpgradable,
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
# Moodle JavaScript Documentation
|
||||
|
||||
```
|
||||
.-..-.
|
||||
_____ | || |
|
||||
/____/-.---_ .---. .---. .-.| || | .---.
|
||||
| | _ _ |/ _ \/ _ \/ _ || |/ __ \
|
||||
* | | | | | || |_| || |_| || |_| || || |___/
|
||||
|_| |_| |_|\_____/\_____/\_____||_|\_____)
|
||||
|
||||
Moodle - the world's open source learning platform
|
||||
|
||||
```
|
||||
|
||||
## About
|
||||
This generated documentation includes API documentation for JavaScript written in the AMD and ES2015 module formats within Moodle.
|
||||
|
||||
## Related information
|
||||
See [https://docs.moodle.org/dev](https://docs.moodle.org/dev) for other related Developer Documentation.
|
||||
@@ -1,131 +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/>.
|
||||
|
||||
/**
|
||||
* Helper functions for working with Moodle component names, directories, and sources.
|
||||
*
|
||||
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
/* eslint-env node */
|
||||
|
||||
// Do not include any plugins as stanard.
|
||||
const plugins = [];
|
||||
|
||||
plugins.push('plugins/markdown');
|
||||
|
||||
/**
|
||||
* Get the source configuration.
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
const getSource = () => {
|
||||
const glob = require('glob');
|
||||
const path = require('path');
|
||||
const ComponentList = require(path.resolve('.grunt/components.js'));
|
||||
const thirdPartyPaths = ComponentList.getThirdPartyPaths();
|
||||
|
||||
const source = {
|
||||
include: [],
|
||||
includePattern: ".+\\.js$",
|
||||
};
|
||||
|
||||
let includeList = [];
|
||||
|
||||
ComponentList.getAmdSrcGlobList().forEach(async pattern => {
|
||||
includeList.push(...glob.sync(pattern));
|
||||
});
|
||||
|
||||
const cwdLength = process.cwd().length + 1;
|
||||
includeList.forEach(path => {
|
||||
if (source.include.indexOf(path) !== -1) {
|
||||
// Ensure no duplicates.
|
||||
return;
|
||||
}
|
||||
|
||||
const relPath = path.substring(cwdLength);
|
||||
if (thirdPartyPaths.indexOf(relPath) !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
source.include.push(path);
|
||||
});
|
||||
|
||||
source.include.push('.grunt/jsdoc/README.md');
|
||||
return source;
|
||||
};
|
||||
|
||||
const tags = {
|
||||
// Allow the use of unknown tags.
|
||||
// We have a lot of legacy uses of these.
|
||||
allowUnknownTags: true,
|
||||
|
||||
// We make use of jsdoc and closure dictionaries as standard.
|
||||
dictionaries: [
|
||||
'jsdoc',
|
||||
'closure',
|
||||
],
|
||||
};
|
||||
|
||||
// Template configuraiton.
|
||||
const templates = {
|
||||
cleverLinks: false,
|
||||
monospaceLinks: false,
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
opts: {
|
||||
destination: "./jsdoc/",
|
||||
template: "node_modules/docdash",
|
||||
},
|
||||
plugins,
|
||||
recurseDepth: 10,
|
||||
source: getSource(),
|
||||
sourceType: 'module',
|
||||
tags,
|
||||
templates,
|
||||
docdash: {
|
||||
collapse: true,
|
||||
search: true,
|
||||
sort: true,
|
||||
sectionOrder: [
|
||||
"Namespaces",
|
||||
"Modules",
|
||||
"Events",
|
||||
"Classes",
|
||||
"Externals",
|
||||
"Mixins",
|
||||
"Tutorials",
|
||||
"Interfaces"
|
||||
],
|
||||
"menu": {
|
||||
"Developer Docs": {
|
||||
href: "https://docs.moodle.org/dev",
|
||||
target: "_blank",
|
||||
"class": "menu-item",
|
||||
id: "devdocs"
|
||||
},
|
||||
"MDN Docs": {
|
||||
href: "https://developer.mozilla.org/en-US/docs/Web/JavaScript",
|
||||
target: "_blank",
|
||||
"class": "menu-item",
|
||||
id: "mdndocs",
|
||||
},
|
||||
},
|
||||
typedefs: true,
|
||||
},
|
||||
};
|
||||
@@ -1,142 +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/>.
|
||||
/* jshint node: true, browser: false */
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* Component Library build tasks.
|
||||
*
|
||||
* @copyright 2021 Andrew Nicols
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
module.exports = grunt => {
|
||||
|
||||
/**
|
||||
* Get a child path of the component library.
|
||||
*
|
||||
* @param {string} path
|
||||
* @returns {string}
|
||||
*/
|
||||
const getCLPath = path => `admin/tool/componentlibrary/${path}`;
|
||||
|
||||
/**
|
||||
* Get a spawn handler.
|
||||
*
|
||||
* This is a generic function to write the spawn output, and then to exit if required and mark the async task as
|
||||
* complete.
|
||||
*
|
||||
* @param {Promise} done
|
||||
* @returns {function}
|
||||
*/
|
||||
const getSpawnHandler = done => (error, result, code) => {
|
||||
grunt.log.write(result);
|
||||
|
||||
if (error) {
|
||||
grunt.log.error(result.stdout);
|
||||
process.exit(code);
|
||||
}
|
||||
done();
|
||||
};
|
||||
|
||||
/**
|
||||
* Spawn a function against Node with the provided args.
|
||||
*
|
||||
* @param {array} args
|
||||
* @returns {object}
|
||||
*/
|
||||
const spawnNodeCall = (args) => grunt.util.spawn({
|
||||
cmd: 'node',
|
||||
args,
|
||||
}, getSpawnHandler(grunt.task.current.async()));
|
||||
|
||||
/**
|
||||
* Build the docs using Hugo.
|
||||
*
|
||||
* @returns {Object} Reference to the spawned task
|
||||
*/
|
||||
const docsBuild = () => spawnNodeCall([
|
||||
'node_modules/hugo-bin/cli.js',
|
||||
'--config', getCLPath('config.yml'),
|
||||
'--cleanDestinationDir',
|
||||
]);
|
||||
|
||||
/**
|
||||
* Build the docs index using the hugo-lunr-indexer.
|
||||
*
|
||||
* @returns {Object} Reference to the spawned task
|
||||
*/
|
||||
const indexBuild = () => spawnNodeCall([
|
||||
'node_modules/hugo-lunr-indexer/bin/hli.js',
|
||||
'-i', getCLPath('content/**'),
|
||||
'-o', getCLPath('hugo/site/data/my-index.json'),
|
||||
'-l', 'yaml',
|
||||
'-d', '---',
|
||||
]);
|
||||
|
||||
/**
|
||||
* Build the hugo CSS.
|
||||
*
|
||||
* @returns {Object} Reference to the spawned task
|
||||
*/
|
||||
const cssBuild = () => spawnNodeCall([
|
||||
'node_modules/sass/sass.js',
|
||||
'--style', 'expanded',
|
||||
'--source-map',
|
||||
'--embed-sources',
|
||||
'--precision', 6,
|
||||
'--load-path', process.cwd(),
|
||||
getCLPath('hugo/scss/docs.scss'),
|
||||
getCLPath('hugo/dist/css/docs.css'),
|
||||
]);
|
||||
|
||||
// Register the various component library tasks.
|
||||
grunt.registerTask('componentlibrary:docsBuild', 'Build the component library', docsBuild);
|
||||
grunt.registerTask('componentlibrary:cssBuild', 'Build the component library', cssBuild);
|
||||
grunt.registerTask('componentlibrary:indexBuild', 'Build the component library', indexBuild);
|
||||
grunt.registerTask('componentlibrary', 'Build the component library', [
|
||||
'componentlibrary:docsBuild',
|
||||
'componentlibrary:cssBuild',
|
||||
'componentlibrary:indexBuild',
|
||||
]);
|
||||
|
||||
grunt.config.merge({
|
||||
watch: {
|
||||
componentLibraryDocs: {
|
||||
files: [
|
||||
getCLPath('content/**/*.md'),
|
||||
getCLPath('hugo'),
|
||||
],
|
||||
tasks: ['componentlibrary:docsBuild', 'componentlibrary:indexBuild'],
|
||||
},
|
||||
componentLibraryCSS: {
|
||||
files: [
|
||||
getCLPath('hugo/scss/**/*.scss'),
|
||||
'hugo',
|
||||
],
|
||||
tasks: ['componentlibrary:cssBuild'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Add the 'componentlibrary' task as a startup task.
|
||||
grunt.moodleEnv.startupTasks.push('componentlibrary');
|
||||
|
||||
return {
|
||||
docsBuild,
|
||||
cssBuild,
|
||||
indexBuild,
|
||||
};
|
||||
};
|
||||
@@ -1,64 +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/>.
|
||||
/* jshint node: true, browser: false */
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* @copyright 2021 Andrew Nicols
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
module.exports = grunt => {
|
||||
const files = grunt.moodleEnv.files;
|
||||
|
||||
// Project configuration.
|
||||
grunt.config.merge({
|
||||
eslint: {
|
||||
// Even though warnings dont stop the build we don't display warnings by default because
|
||||
// at this moment we've got too many core warnings.
|
||||
// To display warnings call: grunt eslint --show-lint-warnings
|
||||
// To fail on warnings call: grunt eslint --max-lint-warnings=0
|
||||
// Also --max-lint-warnings=-1 can be used to display warnings but not fail.
|
||||
options: {
|
||||
quiet: (!grunt.option('show-lint-warnings')) && (typeof grunt.option('max-lint-warnings') === 'undefined'),
|
||||
maxWarnings: ((typeof grunt.option('max-lint-warnings') !== 'undefined') ? grunt.option('max-lint-warnings') : -1)
|
||||
},
|
||||
|
||||
// Check AMD src files.
|
||||
amd: {src: files ? files : grunt.moodleEnv.amdSrc},
|
||||
|
||||
// Check YUI module source files.
|
||||
yui: {src: files ? files : grunt.moodleEnv.yuiSrc},
|
||||
},
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-eslint');
|
||||
|
||||
// On watch, we dynamically modify config to build only affected files. This
|
||||
// method is slightly complicated to deal with multiple changed files at once (copied
|
||||
// from the grunt-contrib-watch readme).
|
||||
let changedFiles = Object.create(null);
|
||||
const onChange = grunt.util._.debounce(function() {
|
||||
const files = Object.keys(changedFiles);
|
||||
grunt.config('eslint.amd.src', files);
|
||||
grunt.config('eslint.yui.src', files);
|
||||
changedFiles = Object.create(null);
|
||||
}, 200);
|
||||
|
||||
grunt.event.on('watch', (action, filepath) => {
|
||||
changedFiles[filepath] = action;
|
||||
onChange();
|
||||
});
|
||||
};
|
||||
@@ -1,92 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
/* jshint node: true, browser: false */
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* @copyright 2021 Andrew Nicols
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
module.exports = grunt => {
|
||||
/**
|
||||
* Get the list of feature files to pass to the gherkin linter.
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
const getGherkinLintTargets = () => {
|
||||
if (grunt.moodleEnv.files) {
|
||||
// Specific files were requested. Only check these.
|
||||
return grunt.moodleEnv.files;
|
||||
}
|
||||
|
||||
if (grunt.moodleEnv.inComponent) {
|
||||
return [`${grunt.moodleEnv.runDir}/tests/behat/*.feature`];
|
||||
}
|
||||
|
||||
return ['**/tests/behat/*.feature'];
|
||||
};
|
||||
|
||||
const handler = function() {
|
||||
const done = this.async();
|
||||
const options = grunt.config('gherkinlint.options');
|
||||
|
||||
// Grab the gherkin-lint linter and required scaffolding.
|
||||
const linter = require('gherkin-lint/dist/linter.js');
|
||||
const featureFinder = require('gherkin-lint/dist/feature-finder.js');
|
||||
const configParser = require('gherkin-lint/dist/config-parser.js');
|
||||
const formatter = require('gherkin-lint/dist/formatters/stylish.js');
|
||||
|
||||
// Run the linter.
|
||||
return linter.lint(
|
||||
featureFinder.getFeatureFiles(grunt.file.expand(options.files)),
|
||||
configParser.getConfiguration(configParser.defaultConfigFileName)
|
||||
)
|
||||
.then(results => {
|
||||
// Print the results out uncondtionally.
|
||||
formatter.printResults(results);
|
||||
|
||||
return results;
|
||||
})
|
||||
.then(results => {
|
||||
// Report on the results.
|
||||
// The done function takes a bool whereby a falsey statement causes the task to fail.
|
||||
return results.every(result => result.errors.length === 0);
|
||||
})
|
||||
.then(done); // eslint-disable-line promise/no-callback-in-promise
|
||||
};
|
||||
|
||||
grunt.registerTask('gherkinlint', 'Run gherkinlint against the current directory', handler);
|
||||
|
||||
grunt.config.set('gherkinlint', {
|
||||
options: {
|
||||
files: getGherkinLintTargets(),
|
||||
}
|
||||
});
|
||||
|
||||
grunt.config.merge({
|
||||
watch: {
|
||||
gherkinlint: {
|
||||
files: [grunt.moodleEnv.inComponent ? 'tests/behat/*.feature' : '**/tests/behat/*.feature'],
|
||||
tasks: ['gherkinlint'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Add the 'gherkinlint' task as a startup task.
|
||||
grunt.moodleEnv.startupTasks.push('gherkinlint');
|
||||
|
||||
return handler;
|
||||
};
|
||||
@@ -1,100 +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/>.
|
||||
/* jshint node: true, browser: false */
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* @copyright 2021 Andrew Nicols
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
module.exports = grunt => {
|
||||
|
||||
/**
|
||||
* Generate the PHPCS configuration.
|
||||
*
|
||||
* @param {Object} thirdPartyPaths
|
||||
*/
|
||||
const phpcsIgnore = (thirdPartyPaths) => {
|
||||
const {toXML} = require('jstoxml');
|
||||
|
||||
const config = {
|
||||
_name: 'ruleset',
|
||||
_attrs: {
|
||||
name: "MoodleCore",
|
||||
},
|
||||
_content: [
|
||||
{
|
||||
rule: {
|
||||
_attrs: {
|
||||
ref: './phpcs.xml.dist',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
thirdPartyPaths.forEach(library => {
|
||||
config._content.push({
|
||||
'exclude-pattern': library,
|
||||
});
|
||||
});
|
||||
|
||||
grunt.file.write('phpcs.xml', toXML(config, {
|
||||
header: true,
|
||||
indent: ' ',
|
||||
}) + "\n");
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate ignore files (utilising thirdpartylibs.xml data)
|
||||
*/
|
||||
const handler = function() {
|
||||
const path = require('path');
|
||||
const ComponentList = require(path.join(process.cwd(), '.grunt', 'components.js'));
|
||||
|
||||
// An array of paths to third party directories.
|
||||
const thirdPartyPaths = ComponentList.getThirdPartyPaths();
|
||||
|
||||
// Generate .eslintignore.
|
||||
const eslintIgnores = [
|
||||
'# Generated by "grunt ignorefiles"',
|
||||
// Do not ignore the .grunt directory.
|
||||
'!/.grunt',
|
||||
|
||||
// Ignore all yui/src meta directories and build directories.
|
||||
'*/**/yui/src/*/meta/',
|
||||
'*/**/build/',
|
||||
].concat(thirdPartyPaths);
|
||||
grunt.file.write('.eslintignore', eslintIgnores.join('\n') + '\n');
|
||||
|
||||
// Generate .stylelintignore.
|
||||
const stylelintIgnores = [
|
||||
'# Generated by "grunt ignorefiles"',
|
||||
'**/yui/build/*',
|
||||
'theme/boost/style/moodle.css',
|
||||
'theme/classic/style/moodle.css',
|
||||
'jsdoc/styles/*.css',
|
||||
'admin/tool/componentlibrary/hugo/dist/css/docs.css',
|
||||
].concat(thirdPartyPaths);
|
||||
grunt.file.write('.stylelintignore', stylelintIgnores.join('\n') + '\n');
|
||||
|
||||
phpcsIgnore(thirdPartyPaths);
|
||||
};
|
||||
|
||||
grunt.registerTask('ignorefiles', 'Generate ignore files for linters', handler);
|
||||
|
||||
return handler;
|
||||
};
|
||||
@@ -1,228 +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/>.
|
||||
/* jshint node: true, browser: false */
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* @copyright 2021 Andrew Nicols
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Function to generate the destination for the uglify task
|
||||
* (e.g. build/file.min.js). This function will be passed to
|
||||
* the rename property of files array when building dynamically:
|
||||
* http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically
|
||||
*
|
||||
* @param {String} destPath the current destination
|
||||
* @param {String} srcPath the matched src path
|
||||
* @return {String} The rewritten destination path.
|
||||
*/
|
||||
|
||||
const babelRename = function(destPath, srcPath) {
|
||||
destPath = srcPath.replace('src', 'build');
|
||||
destPath = destPath.replace('.js', '.min.js');
|
||||
return destPath;
|
||||
};
|
||||
|
||||
module.exports = grunt => {
|
||||
// Load the Ignorefiles tasks.
|
||||
require('./ignorefiles')(grunt);
|
||||
|
||||
// Load the Shifter tasks.
|
||||
require('./shifter')(grunt);
|
||||
|
||||
// Load ESLint.
|
||||
require('./eslint')(grunt);
|
||||
|
||||
// Load jsconfig.
|
||||
require('./jsconfig')(grunt);
|
||||
|
||||
// Load JSDoc.
|
||||
require('./jsdoc')(grunt);
|
||||
|
||||
const path = require('path');
|
||||
|
||||
// Register JS tasks.
|
||||
grunt.registerTask('yui', ['eslint:yui', 'shifter']);
|
||||
grunt.registerTask('amd', ['ignorefiles', 'eslint:amd', 'rollup']);
|
||||
grunt.registerTask('js', ['amd', 'yui']);
|
||||
|
||||
// Register NPM tasks.
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-rollup');
|
||||
|
||||
const babelTransform = require('@babel/core').transform;
|
||||
const babel = (options = {}) => {
|
||||
return {
|
||||
name: 'babel',
|
||||
|
||||
transform: (code, id) => {
|
||||
grunt.log.debug(`Transforming ${id}`);
|
||||
options.filename = id;
|
||||
const transformed = babelTransform(code, options);
|
||||
|
||||
return {
|
||||
code: transformed.code,
|
||||
map: transformed.map
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Note: We have to use a rate limit plugin here because rollup runs all tasks asynchronously and in parallel.
|
||||
// When we kick off a full run, if we kick off a rollup of every file this will fork-bomb the machine.
|
||||
// To work around this we use a concurrent Promise queue based on the number of available processors.
|
||||
const rateLimit = () => {
|
||||
const queue = [];
|
||||
let queueRunner;
|
||||
|
||||
const startQueue = () => {
|
||||
if (queueRunner) {
|
||||
return;
|
||||
}
|
||||
|
||||
queueRunner = setTimeout(() => {
|
||||
const limit = Math.max(1, require('os').cpus().length / 2);
|
||||
grunt.log.debug(`Starting rollup with queue size of ${limit}`);
|
||||
runQueue(limit);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
// The queue runner will run the next `size` items in the queue.
|
||||
const runQueue = (size = 1) => {
|
||||
queue.splice(0, size).forEach(resolve => {
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
name: 'ratelimit',
|
||||
|
||||
// The options hook is run in parallel.
|
||||
// We can return an unresolved Promise which is queued for later resolution.
|
||||
options: async() => {
|
||||
return new Promise(resolve => {
|
||||
queue.push(resolve);
|
||||
startQueue();
|
||||
});
|
||||
},
|
||||
|
||||
// When an item in the queue completes, start the next item in the queue.
|
||||
buildEnd: () => {
|
||||
runQueue();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const terser = require('rollup-plugin-terser').terser;
|
||||
grunt.config.merge({
|
||||
rollup: {
|
||||
options: {
|
||||
format: 'esm',
|
||||
dir: 'output',
|
||||
sourcemap: true,
|
||||
treeshake: false,
|
||||
context: 'window',
|
||||
plugins: [
|
||||
rateLimit(),
|
||||
babel({
|
||||
sourceMaps: true,
|
||||
comments: false,
|
||||
compact: false,
|
||||
plugins: [
|
||||
'transform-es2015-modules-amd-lazy',
|
||||
'system-import-transformer',
|
||||
// This plugin modifies the Babel transpiling for "export default"
|
||||
// so that if it's used then only the exported value is returned
|
||||
// by the generated AMD module.
|
||||
//
|
||||
// It also adds the Moodle plugin name to the AMD module definition
|
||||
// so that it can be imported as expected in other modules.
|
||||
path.resolve('.grunt/babel-plugin-add-module-to-define.js'),
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'@babel/plugin-syntax-import-meta',
|
||||
['@babel/plugin-proposal-class-properties', {'loose': false}],
|
||||
'@babel/plugin-proposal-json-strings'
|
||||
],
|
||||
presets: [
|
||||
['@babel/preset-env', {
|
||||
targets: {
|
||||
browsers: [
|
||||
">0.3%",
|
||||
"last 2 versions",
|
||||
"not ie >= 0",
|
||||
"not op_mini all",
|
||||
"not Opera > 0",
|
||||
"not dead"
|
||||
]
|
||||
},
|
||||
modules: false,
|
||||
useBuiltIns: false
|
||||
}]
|
||||
]
|
||||
}),
|
||||
|
||||
terser({
|
||||
// Do not mangle variables.
|
||||
// Makes debugging easier.
|
||||
mangle: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
dist: {
|
||||
files: [{
|
||||
expand: true,
|
||||
src: grunt.moodleEnv.files ? grunt.moodleEnv.files : grunt.moodleEnv.amdSrc,
|
||||
rename: babelRename
|
||||
}],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
grunt.config.merge({
|
||||
watch: {
|
||||
amd: {
|
||||
files: grunt.moodleEnv.inComponent
|
||||
? ['amd/src/*.js', 'amd/src/**/*.js']
|
||||
: ['**/amd/src/**/*.js'],
|
||||
tasks: ['amd']
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Add the 'js' task as a startup task.
|
||||
grunt.moodleEnv.startupTasks.push('js');
|
||||
|
||||
// On watch, we dynamically modify config to build only affected files. This
|
||||
// method is slightly complicated to deal with multiple changed files at once (copied
|
||||
// from the grunt-contrib-watch readme).
|
||||
let changedFiles = Object.create(null);
|
||||
const onChange = grunt.util._.debounce(function() {
|
||||
const files = Object.keys(changedFiles);
|
||||
grunt.config('rollup.dist.files', [{expand: true, src: files, rename: babelRename}]);
|
||||
changedFiles = Object.create(null);
|
||||
}, 200);
|
||||
|
||||
grunt.event.on('watch', function(action, filepath) {
|
||||
changedFiles[filepath] = action;
|
||||
onChange();
|
||||
});
|
||||
|
||||
return {
|
||||
babelRename,
|
||||
};
|
||||
};
|
||||
@@ -1,53 +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/>.
|
||||
/* jshint node: true, browser: false */
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
const configuration = {
|
||||
compilerOptions: {
|
||||
baseUrl: ".",
|
||||
paths: {
|
||||
},
|
||||
target: "es2015",
|
||||
allowSyntheticDefaultImports: false,
|
||||
},
|
||||
exclude: [
|
||||
"node_modules",
|
||||
],
|
||||
include: [],
|
||||
};
|
||||
|
||||
module.exports = (grunt) => {
|
||||
const handler = () => {
|
||||
const jsconfigData = Object.assign({}, configuration);
|
||||
|
||||
const path = require('path');
|
||||
const {fetchComponentData} = require(path.join(process.cwd(), '.grunt', 'components.js'));
|
||||
|
||||
const componentData = fetchComponentData().components;
|
||||
for (const [thisPath, component] of Object.entries(componentData)) {
|
||||
jsconfigData.compilerOptions.paths[`${component}/*`] = [`${thisPath}/amd/src/*`];
|
||||
jsconfigData.include.push(`${thisPath}/amd/src/**/*`);
|
||||
}
|
||||
|
||||
grunt.file.write('jsconfig.json', JSON.stringify(jsconfigData, null, " ") + "\n");
|
||||
};
|
||||
grunt.registerTask('jsconfig', 'Generate a jsconfig configuration compatible with the LSP', handler);
|
||||
};
|
||||
@@ -1,36 +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/>.
|
||||
/* jshint node: true, browser: false */
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* @copyright 2021 Andrew Nicols
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
module.exports = grunt => {
|
||||
// Project configuration.
|
||||
grunt.config.merge({
|
||||
jsdoc: {
|
||||
dist: {
|
||||
options: {
|
||||
configure: ".grunt/jsdoc/jsdoc.conf.js",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-jsdoc');
|
||||
};
|
||||
@@ -1,40 +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/>.
|
||||
/* jshint node: true, browser: false */
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* @copyright 2021 Andrew Nicols
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
module.exports = grunt => {
|
||||
grunt.loadNpmTasks('grunt-sass');
|
||||
|
||||
grunt.config.merge({
|
||||
sass: {
|
||||
dist: {
|
||||
files: {
|
||||
"theme/boost/style/moodle.css": "theme/boost/scss/preset/default.scss",
|
||||
"theme/classic/style/moodle.css": "theme/classic/scss/classicgrunt.scss"
|
||||
}
|
||||
},
|
||||
options: {
|
||||
implementation: require('sass'),
|
||||
includePaths: ["theme/boost/scss/", "theme/classic/scss/"]
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,155 +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/>.
|
||||
/* jshint node: true, browser: false */
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* @copyright 2021 Andrew Nicols
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/* eslint-env node */
|
||||
|
||||
module.exports = grunt => {
|
||||
/**
|
||||
* Shifter task. Is configured with a path to a specific file or a directory,
|
||||
* in the case of a specific file it will work out the right module to be built.
|
||||
*
|
||||
* Note that this task runs the invidiaul shifter jobs async (becase it spawns
|
||||
* so be careful to to call done().
|
||||
*/
|
||||
const handler = function() {
|
||||
const done = this.async();
|
||||
const options = grunt.config('shifter.options');
|
||||
const async = require('async');
|
||||
const path = require('path');
|
||||
|
||||
// Run the shifter processes one at a time to avoid confusing output.
|
||||
async.eachSeries(options.paths, function(src, filedone) {
|
||||
var args = [];
|
||||
args.push(path.normalize(process.cwd() + '/node_modules/shifter/bin/shifter'));
|
||||
|
||||
// Always ignore the node_modules directory.
|
||||
args.push('--excludes', 'node_modules');
|
||||
|
||||
// Determine the most appropriate options to run with based upon the current location.
|
||||
if (grunt.file.isMatch('**/yui/**/*.js', src)) {
|
||||
// When passed a JS file, build our containing module (this happen with
|
||||
// watch).
|
||||
grunt.log.debug('Shifter passed a specific JS file');
|
||||
src = path.dirname(path.dirname(src));
|
||||
options.recursive = false;
|
||||
} else if (grunt.file.isMatch('**/yui/src', src)) {
|
||||
// When in a src directory --walk all modules.
|
||||
grunt.log.debug('In a src directory');
|
||||
args.push('--walk');
|
||||
options.recursive = false;
|
||||
} else if (grunt.file.isMatch('**/yui/src/*', src)) {
|
||||
// When in module, only build our module.
|
||||
grunt.log.debug('In a module directory');
|
||||
options.recursive = false;
|
||||
} else if (grunt.file.isMatch('**/yui/src/*/js', src)) {
|
||||
// When in module src, only build our module.
|
||||
grunt.log.debug('In a source directory');
|
||||
src = path.dirname(src);
|
||||
options.recursive = false;
|
||||
}
|
||||
|
||||
if (grunt.option('watch')) {
|
||||
grunt.fail.fatal('The --watch option has been removed, please use `grunt watch` instead');
|
||||
}
|
||||
|
||||
// Add the stderr option if appropriate
|
||||
if (grunt.option('verbose')) {
|
||||
args.push('--lint-stderr');
|
||||
}
|
||||
|
||||
if (grunt.option('no-color')) {
|
||||
args.push('--color=false');
|
||||
}
|
||||
|
||||
var execShifter = function() {
|
||||
|
||||
grunt.log.ok("Running shifter on " + src);
|
||||
grunt.util.spawn({
|
||||
cmd: "node",
|
||||
args: args,
|
||||
opts: {cwd: src, stdio: 'inherit', env: process.env}
|
||||
}, function(error, result, code) {
|
||||
if (code) {
|
||||
grunt.fail.fatal('Shifter failed with code: ' + code);
|
||||
} else {
|
||||
grunt.log.ok('Shifter build complete.');
|
||||
filedone();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Actually run shifter.
|
||||
if (!options.recursive) {
|
||||
execShifter();
|
||||
} else {
|
||||
// Check that there are yui modules otherwise shifter ends with exit code 1.
|
||||
if (grunt.file.expand({cwd: src}, '**/yui/src/**/*.js').length > 0) {
|
||||
args.push('--recursive');
|
||||
execShifter();
|
||||
} else {
|
||||
grunt.log.ok('No YUI modules to build.');
|
||||
filedone();
|
||||
}
|
||||
}
|
||||
}, done);
|
||||
};
|
||||
|
||||
// Register the shifter task.
|
||||
grunt.registerTask('shifter', 'Run Shifter against the current directory', handler);
|
||||
|
||||
// Configure it.
|
||||
grunt.config.set('shifter', {
|
||||
options: {
|
||||
recursive: true,
|
||||
// Shifter takes a relative path.
|
||||
paths: grunt.moodleEnv.files ? grunt.moodleEnv.files : [grunt.moodleEnv.runDir]
|
||||
}
|
||||
});
|
||||
|
||||
grunt.config.merge({
|
||||
watch: {
|
||||
yui: {
|
||||
files: grunt.moodleEnv.inComponent
|
||||
? ['yui/src/*.json', 'yui/src/**/*.js']
|
||||
: ['**/yui/src/**/*.js'],
|
||||
tasks: ['yui']
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// On watch, we dynamically modify config to build only affected files. This
|
||||
// method is slightly complicated to deal with multiple changed files at once (copied
|
||||
// from the grunt-contrib-watch readme).
|
||||
let changedFiles = Object.create(null);
|
||||
const onChange = grunt.util._.debounce(function() {
|
||||
const files = Object.keys(changedFiles);
|
||||
grunt.config('shifter.options.paths', files);
|
||||
changedFiles = Object.create(null);
|
||||
}, 200);
|
||||
|
||||
grunt.event.on('watch', (action, filepath) => {
|
||||
changedFiles[filepath] = action;
|
||||
onChange();
|
||||
});
|
||||
|
||||
return handler;
|
||||
};
|
||||
@@ -1,46 +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/>.
|
||||
/* jshint node: true, browser: false */
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* @copyright 2021 Andrew Nicols
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
module.exports = grunt => {
|
||||
/**
|
||||
* Generate ignore files (utilising thirdpartylibs.xml data)
|
||||
*/
|
||||
const handler = function() {
|
||||
const path = require('path');
|
||||
|
||||
// Are we in a YUI directory?
|
||||
if (path.basename(path.resolve(grunt.moodleEnv.cwd, '../../')) == 'yui') {
|
||||
grunt.task.run('yui');
|
||||
// Are we in an AMD directory?
|
||||
} else if (grunt.moodleEnv.inAMD) {
|
||||
grunt.task.run('amd');
|
||||
} else {
|
||||
// Run all of the requested startup tasks.
|
||||
grunt.moodleEnv.startupTasks.forEach(taskName => grunt.task.run(taskName));
|
||||
}
|
||||
};
|
||||
|
||||
// Register the startup task.
|
||||
grunt.registerTask('startup', 'Run the correct tasks for the current directory', handler);
|
||||
|
||||
return handler;
|
||||
};
|
||||
@@ -1,35 +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/>.
|
||||
/* jshint node: true, browser: false */
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* @copyright 2021 Andrew Nicols
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
module.exports = grunt => {
|
||||
// Load the ignorefiles tasks.
|
||||
require('./ignorefiles')(grunt);
|
||||
|
||||
// Load the Style Lint tasks.
|
||||
require('./stylelint')(grunt);
|
||||
|
||||
// Load the SASS tasks.
|
||||
require('./sass')(grunt);
|
||||
|
||||
// Add the 'css' task as a startup task.
|
||||
grunt.moodleEnv.startupTasks.push('css');
|
||||
};
|
||||
@@ -1,183 +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/>.
|
||||
/* jshint node: true, browser: false */
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* @copyright 2021 Andrew Nicols
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
module.exports = grunt => {
|
||||
|
||||
const getCssConfigForFiles = files => {
|
||||
return {
|
||||
stylelint: {
|
||||
css: {
|
||||
// Use a fully-qualified path.
|
||||
src: files,
|
||||
options: {
|
||||
configOverrides: {
|
||||
rules: {
|
||||
// These rules have to be disabled in .stylelintrc for scss compat.
|
||||
"at-rule-no-unknown": true,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const getScssConfigForFiles = files => {
|
||||
return {
|
||||
stylelint: {
|
||||
scss: {
|
||||
options: {syntax: 'scss'},
|
||||
src: files,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Register any stylelint tasks.
|
||||
*
|
||||
* @param {Object} grunt
|
||||
* @param {Array} files
|
||||
* @param {String} fullRunDir
|
||||
*/
|
||||
const registerStyleLintTasks = () => {
|
||||
const glob = require('glob');
|
||||
|
||||
// The stylelinters do not handle the case where a configuration was provided but no files were included.
|
||||
// Keep track of whether any files were found.
|
||||
let hasCss = false;
|
||||
let hasScss = false;
|
||||
|
||||
// The stylelint processors do not take a path argument. They always check all provided values.
|
||||
// As a result we must check through each glob and determine if any files match the current directory.
|
||||
const scssFiles = [];
|
||||
const cssFiles = [];
|
||||
|
||||
const requestedFiles = grunt.moodleEnv.files;
|
||||
if (requestedFiles) {
|
||||
// Grunt was called with a files argument.
|
||||
// Check whether each of the requested files matches either the CSS or SCSS source file list.
|
||||
|
||||
requestedFiles.forEach(changedFilePath => {
|
||||
let matchesGlob;
|
||||
|
||||
// Check whether this watched path matches any watched SCSS file.
|
||||
matchesGlob = grunt.moodleEnv.scssSrc.some(watchedPathGlob => {
|
||||
return glob.sync(watchedPathGlob).indexOf(changedFilePath) !== -1;
|
||||
});
|
||||
if (matchesGlob) {
|
||||
scssFiles.push(changedFilePath);
|
||||
hasScss = true;
|
||||
}
|
||||
|
||||
// Check whether this watched path matches any watched CSS file.
|
||||
matchesGlob = grunt.moodleEnv.cssSrc.some(watchedPathGlob => {
|
||||
return glob.sync(watchedPathGlob).indexOf(changedFilePath) !== -1;
|
||||
});
|
||||
if (matchesGlob) {
|
||||
cssFiles.push(changedFilePath);
|
||||
hasCss = true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Grunt was called without a list of files.
|
||||
// The start directory (runDir) may be a child dir of the project.
|
||||
// Check each scssSrc file to see if it's in the start directory.
|
||||
// This means that we can lint just mod/*/styles.css if started in the mod directory.
|
||||
|
||||
grunt.moodleEnv.scssSrc.forEach(path => {
|
||||
if (path.startsWith(grunt.moodleEnv.runDir)) {
|
||||
scssFiles.push(path);
|
||||
hasScss = true;
|
||||
}
|
||||
});
|
||||
|
||||
grunt.moodleEnv.cssSrc.forEach(path => {
|
||||
if (path.startsWith(grunt.moodleEnv.runDir)) {
|
||||
cssFiles.push(path);
|
||||
hasCss = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Register the tasks.
|
||||
const scssTasks = ['sass'];
|
||||
if (hasScss) {
|
||||
grunt.config.merge(getScssConfigForFiles(scssFiles));
|
||||
scssTasks.unshift('stylelint:scss');
|
||||
}
|
||||
scssTasks.unshift('ignorefiles');
|
||||
|
||||
const cssTasks = ['ignorefiles'];
|
||||
if (hasCss) {
|
||||
grunt.config.merge(getCssConfigForFiles(cssFiles));
|
||||
cssTasks.push('stylelint:css');
|
||||
}
|
||||
|
||||
// The tasks must be registered, even if empty to ensure a consistent command list.
|
||||
// They jsut won't run anything.
|
||||
grunt.registerTask('scss', scssTasks);
|
||||
grunt.registerTask('rawcss', cssTasks);
|
||||
};
|
||||
|
||||
// Register CSS tasks.
|
||||
grunt.loadNpmTasks('grunt-stylelint');
|
||||
|
||||
// Register the style lint tasks.
|
||||
registerStyleLintTasks();
|
||||
grunt.registerTask('css', ['scss', 'rawcss']);
|
||||
|
||||
const getCoreThemeMatches = () => {
|
||||
const scssMatch = 'scss/**/*.scss';
|
||||
|
||||
if (grunt.moodleEnv.inTheme) {
|
||||
return [scssMatch];
|
||||
}
|
||||
|
||||
if (grunt.moodleEnv.runDir.startsWith('theme')) {
|
||||
return [`*/${scssMatch}`];
|
||||
}
|
||||
|
||||
return [`theme/*/${scssMatch}`];
|
||||
};
|
||||
|
||||
// Add the watch configuration for rawcss, and scss.
|
||||
grunt.config.merge({
|
||||
watch: {
|
||||
rawcss: {
|
||||
files: [
|
||||
'**/*.css',
|
||||
],
|
||||
excludes: [
|
||||
'**/moodle.css',
|
||||
'**/editor.css',
|
||||
'jsdoc/styles/*.css',
|
||||
],
|
||||
tasks: ['rawcss']
|
||||
},
|
||||
scss: {
|
||||
files: getCoreThemeMatches(),
|
||||
tasks: ['scss']
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -1,42 +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/>.
|
||||
/* jshint node: true, browser: false */
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* @copyright 2023 Sara Arjona <sara@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
module.exports = grunt => {
|
||||
/**
|
||||
* Generate upgradable third-party libraries (utilising thirdpartylibs.xml data)
|
||||
*/
|
||||
grunt.registerTask('upgradablelibs', 'Generate upgradable third-party libraries', async function() {
|
||||
const done = this.async();
|
||||
|
||||
const path = require('path');
|
||||
const ComponentList = require(path.join(process.cwd(), '.grunt', 'components.js'));
|
||||
|
||||
// An array of third party libraries that have a newer version in their repositories.
|
||||
const thirdPartyLibs = await ComponentList.getThirdPartyLibsUpgradable({progress: true});
|
||||
for (let library of thirdPartyLibs) {
|
||||
grunt.log.ok(JSON.stringify(Object.assign({}, library), null, 4));
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
};
|
||||
@@ -1,272 +0,0 @@
|
||||
/**
|
||||
* This is a wrapper task to handle the grunt watch command. It attempts to use
|
||||
* Watchman to monitor for file changes, if it's installed, because it's much faster.
|
||||
*
|
||||
* If Watchman isn't installed then it falls back to the grunt-contrib-watch file
|
||||
* watcher for backwards compatibility.
|
||||
*/
|
||||
|
||||
/* eslint-env node */
|
||||
|
||||
module.exports = grunt => {
|
||||
/**
|
||||
* This is a wrapper task to handle the grunt watch command. It attempts to use
|
||||
* Watchman to monitor for file changes, if it's installed, because it's much faster.
|
||||
*
|
||||
* If Watchman isn't installed then it falls back to the grunt-contrib-watch file
|
||||
* watcher for backwards compatibility.
|
||||
*/
|
||||
const watchHandler = function() {
|
||||
const async = require('async');
|
||||
const watchTaskDone = this.async();
|
||||
let watchInitialised = false;
|
||||
let watchTaskQueue = {};
|
||||
let processingQueue = false;
|
||||
|
||||
const watchman = require('fb-watchman');
|
||||
const watchmanClient = new watchman.Client();
|
||||
|
||||
// Grab the tasks and files that have been queued up and execute them.
|
||||
var processWatchTaskQueue = function() {
|
||||
if (!Object.keys(watchTaskQueue).length || processingQueue) {
|
||||
// If there is nothing in the queue or we're already processing then wait.
|
||||
return;
|
||||
}
|
||||
|
||||
processingQueue = true;
|
||||
|
||||
// Grab all tasks currently in the queue.
|
||||
var queueToProcess = watchTaskQueue;
|
||||
// Reset the queue.
|
||||
watchTaskQueue = {};
|
||||
|
||||
async.forEachSeries(
|
||||
Object.keys(queueToProcess),
|
||||
function(task, next) {
|
||||
var files = queueToProcess[task];
|
||||
var filesOption = '--files=' + files.join(',');
|
||||
grunt.log.ok('Running task ' + task + ' for files ' + filesOption);
|
||||
|
||||
// Spawn the task in a child process so that it doesn't kill this one
|
||||
// if it failed.
|
||||
grunt.util.spawn(
|
||||
{
|
||||
// Spawn with the grunt bin.
|
||||
grunt: true,
|
||||
// Run from current working dir and inherit stdio from process.
|
||||
opts: {
|
||||
cwd: grunt.moodleEnv.fullRunDir,
|
||||
stdio: 'inherit'
|
||||
},
|
||||
args: [task, filesOption]
|
||||
},
|
||||
function(err, res, code) {
|
||||
if (code !== 0) {
|
||||
// The grunt task failed.
|
||||
grunt.log.error(err);
|
||||
}
|
||||
|
||||
// Move on to the next task.
|
||||
next();
|
||||
}
|
||||
);
|
||||
},
|
||||
function() {
|
||||
// No longer processing.
|
||||
processingQueue = false;
|
||||
// Once all of the tasks are done then recurse just in case more tasks
|
||||
// were queued while we were processing.
|
||||
processWatchTaskQueue();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const originalWatchConfig = grunt.config.get(['watch']);
|
||||
const watchConfig = Object.keys(originalWatchConfig).reduce(function(carry, key) {
|
||||
if (key == 'options') {
|
||||
return carry;
|
||||
}
|
||||
|
||||
const value = originalWatchConfig[key];
|
||||
|
||||
const taskNames = value.tasks;
|
||||
const files = value.files;
|
||||
let excludes = [];
|
||||
if (value.excludes) {
|
||||
excludes = value.excludes;
|
||||
}
|
||||
|
||||
taskNames.forEach(function(taskName) {
|
||||
carry[taskName] = {
|
||||
files,
|
||||
excludes,
|
||||
};
|
||||
});
|
||||
|
||||
return carry;
|
||||
}, {});
|
||||
|
||||
watchmanClient.on('error', function(error) {
|
||||
// We have to add an error handler here and parse the error string because the
|
||||
// example way from the docs to check if Watchman is installed doesn't actually work!!
|
||||
// See: https://github.com/facebook/watchman/issues/509
|
||||
if (error.message.match('Watchman was not found')) {
|
||||
// If watchman isn't installed then we should fallback to the other watch task.
|
||||
grunt.log.ok('It is recommended that you install Watchman for better performance using the "watch" command.');
|
||||
|
||||
// Fallback to the old grunt-contrib-watch task.
|
||||
grunt.renameTask('watch-grunt', 'watch');
|
||||
grunt.task.run(['watch']);
|
||||
// This task is finished.
|
||||
watchTaskDone(0);
|
||||
} else {
|
||||
grunt.log.error(error);
|
||||
// Fatal error.
|
||||
watchTaskDone(1);
|
||||
}
|
||||
});
|
||||
|
||||
watchmanClient.on('subscription', function(resp) {
|
||||
if (resp.subscription !== 'grunt-watch') {
|
||||
return;
|
||||
}
|
||||
|
||||
resp.files.forEach(function(file) {
|
||||
grunt.log.ok('File changed: ' + file.name);
|
||||
|
||||
var fullPath = grunt.moodleEnv.fullRunDir + '/' + file.name;
|
||||
Object.keys(watchConfig).forEach(function(task) {
|
||||
|
||||
const fileGlobs = watchConfig[task].files;
|
||||
var match = fileGlobs.some(function(fileGlob) {
|
||||
return grunt.file.isMatch(`**/${fileGlob}`, fullPath);
|
||||
});
|
||||
|
||||
if (match) {
|
||||
// If we are watching a subdirectory then the file.name will be relative
|
||||
// to that directory. However the grunt tasks expect the file paths to be
|
||||
// relative to the Gruntfile.js location so let's normalise them before
|
||||
// adding them to the queue.
|
||||
var relativePath = fullPath.replace(grunt.moodleEnv.gruntFilePath + '/', '');
|
||||
if (task in watchTaskQueue) {
|
||||
if (!watchTaskQueue[task].includes(relativePath)) {
|
||||
watchTaskQueue[task] = watchTaskQueue[task].concat(relativePath);
|
||||
}
|
||||
} else {
|
||||
watchTaskQueue[task] = [relativePath];
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
processWatchTaskQueue();
|
||||
});
|
||||
|
||||
process.on('SIGINT', function() {
|
||||
// Let the user know that they may need to manually stop the Watchman daemon if they
|
||||
// no longer want it running.
|
||||
if (watchInitialised) {
|
||||
grunt.log.ok('The Watchman daemon may still be running and may need to be stopped manually.');
|
||||
}
|
||||
|
||||
process.exit();
|
||||
});
|
||||
|
||||
// Initiate the watch on the current directory.
|
||||
watchmanClient.command(['watch-project', grunt.moodleEnv.fullRunDir], function(watchError, watchResponse) {
|
||||
if (watchError) {
|
||||
grunt.log.error('Error initiating watch:', watchError);
|
||||
watchTaskDone(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if ('warning' in watchResponse) {
|
||||
grunt.log.error('warning: ', watchResponse.warning);
|
||||
}
|
||||
|
||||
var watch = watchResponse.watch;
|
||||
var relativePath = watchResponse.relative_path;
|
||||
watchInitialised = true;
|
||||
|
||||
watchmanClient.command(['clock', watch], function(clockError, clockResponse) {
|
||||
if (clockError) {
|
||||
grunt.log.error('Failed to query clock:', clockError);
|
||||
watchTaskDone(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate the expression query used by watchman.
|
||||
// Documentation is limited, but see https://facebook.github.io/watchman/docs/expr/allof.html for examples.
|
||||
// We generate an expression to match any value in the files list of all of our tasks, but excluding
|
||||
// all value in the excludes list of that task.
|
||||
//
|
||||
// [anyof, [
|
||||
// [allof, [
|
||||
// [anyof, [
|
||||
// ['match', validPath, 'wholename'],
|
||||
// ['match', validPath, 'wholename'],
|
||||
// ],
|
||||
// [not,
|
||||
// [anyof, [
|
||||
// ['match', invalidPath, 'wholename'],
|
||||
// ['match', invalidPath, 'wholename'],
|
||||
// ],
|
||||
// ],
|
||||
// ],
|
||||
var matchWholeName = fileGlob => ['match', fileGlob, 'wholename'];
|
||||
var matches = Object.keys(watchConfig).map(function(task) {
|
||||
const matchAll = [];
|
||||
matchAll.push(['anyof'].concat(watchConfig[task].files.map(matchWholeName)));
|
||||
|
||||
if (watchConfig[task].excludes.length) {
|
||||
matchAll.push(['not', ['anyof'].concat(watchConfig[task].excludes.map(matchWholeName))]);
|
||||
}
|
||||
|
||||
return ['allof'].concat(matchAll);
|
||||
});
|
||||
|
||||
matches = ['anyof'].concat(matches);
|
||||
|
||||
var sub = {
|
||||
expression: matches,
|
||||
// Which fields we're interested in.
|
||||
fields: ["name", "size", "type"],
|
||||
// Add our time constraint.
|
||||
since: clockResponse.clock
|
||||
};
|
||||
|
||||
if (relativePath) {
|
||||
/* eslint-disable camelcase */
|
||||
sub.relative_root = relativePath;
|
||||
}
|
||||
|
||||
watchmanClient.command(['subscribe', watch, 'grunt-watch', sub], function(subscribeError) {
|
||||
if (subscribeError) {
|
||||
// Probably an error in the subscription criteria.
|
||||
grunt.log.error('failed to subscribe: ', subscribeError);
|
||||
watchTaskDone(1);
|
||||
return;
|
||||
}
|
||||
|
||||
grunt.log.ok('Listening for changes to files in ' + grunt.moodleEnv.fullRunDir);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Rename the grunt-contrib-watch "watch" task because we're going to wrap it.
|
||||
grunt.renameTask('watch', 'watch-grunt');
|
||||
|
||||
// Register the new watch handler.
|
||||
grunt.registerTask('watch', 'Run tasks on file changes', watchHandler);
|
||||
|
||||
grunt.config.merge({
|
||||
watch: {
|
||||
options: {
|
||||
nospawn: true // We need not to spawn so config can be changed dynamically.
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return watchHandler;
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
# Generated by "grunt ignorefiles"
|
||||
**/yui/build/*
|
||||
theme/boost/style/moodle.css
|
||||
theme/classic/style/moodle.css
|
||||
node_modules/
|
||||
vendor/
|
||||
admin/tool/policy/amd/src/jquery-eu-cookie-law-popup.js
|
||||
auth/cas/CAS/
|
||||
cache/stores/mongodb/MongoDB/
|
||||
enrol/lti/ims-blti/
|
||||
filter/algebra/AlgParser.pm
|
||||
filter/tex/mimetex.*
|
||||
lib/editor/atto/plugins/html/yui/src/codemirror/
|
||||
lib/editor/atto/plugins/html/yui/src/beautify/
|
||||
lib/editor/atto/yui/src/rangy/js/*.*
|
||||
lib/editor/tinymce/plugins/pdw/tinymce/
|
||||
lib/editor/tinymce/plugins/spellchecker/rpc.php
|
||||
lib/editor/tinymce/tiny_mce/
|
||||
lib/mlbackend/php/phpml/
|
||||
lib/adodb/
|
||||
lib/bennu/
|
||||
lib/evalmath/
|
||||
lib/phpspreadsheet/
|
||||
lib/google/
|
||||
lib/htmlpurifier/
|
||||
lib/jabber/
|
||||
lib/minify/matthiasmullie-minify/
|
||||
lib/minify/matthiasmullie-pathconverter/
|
||||
lib/pear/HTML/Common.php
|
||||
lib/pear/HTML/QuickForm.php
|
||||
lib/pear/HTML/QuickForm/
|
||||
lib/pear/PEAR.php
|
||||
lib/phpmailer/
|
||||
lib/simplepie/
|
||||
lib/tcpdf/
|
||||
lib/typo3/
|
||||
lib/yuilib/
|
||||
lib/yuilib/gallery/
|
||||
lib/jquery/
|
||||
lib/html2text/
|
||||
lib/markdown/
|
||||
lib/xhprof/
|
||||
lib/horde/
|
||||
lib/requirejs/
|
||||
lib/amd/src/loglevel.js
|
||||
lib/mustache/
|
||||
lib/amd/src/mustache.js
|
||||
lib/graphlib.php
|
||||
lib/php-css-parser/
|
||||
lib/rtlcss/
|
||||
lib/scssphp/
|
||||
lib/spout/
|
||||
lib/amd/src/chartjs-lazy.js
|
||||
lib/maxmind/GeoIp2/
|
||||
lib/maxmind/MaxMind/
|
||||
lib/ltiprovider/
|
||||
lib/amd/src/truncate.js
|
||||
lib/fonts/
|
||||
lib/amd/src/adapter.js
|
||||
lib/validateurlsyntax.php
|
||||
lib/amd/src/popper.js
|
||||
lib/geopattern-php/
|
||||
lib/php-jwt/
|
||||
lib/babel-polyfill/
|
||||
lib/mdn-polyfills/
|
||||
lib/emoji-data/
|
||||
lib/h5p/
|
||||
media/player/videojs/amd/src/video-lazy.js
|
||||
media/player/videojs/amd/src/Youtube-lazy.js
|
||||
media/player/videojs/videojs/
|
||||
media/player/videojs/amd/src/videojs-flash-lazy.js
|
||||
media/player/videojs/videojs/video-js.swf
|
||||
mod/assign/feedback/editpdf/fpdi/
|
||||
repository/s3/S3.php
|
||||
theme/boost/scss/bootstrap/
|
||||
theme/boost/amd/src/alert.js
|
||||
theme/boost/amd/src/button.js
|
||||
theme/boost/amd/src/carousel.js
|
||||
theme/boost/amd/src/collapse.js
|
||||
theme/boost/amd/src/dropdown.js
|
||||
theme/boost/amd/src/index.js
|
||||
theme/boost/amd/src/modal.js
|
||||
theme/boost/amd/src/popover.js
|
||||
theme/boost/amd/src/sanitizer.js
|
||||
theme/boost/amd/src/scrollspy.js
|
||||
theme/boost/amd/src/tab.js
|
||||
theme/boost/amd/src/toast.js
|
||||
theme/boost/amd/src/tooltip.js
|
||||
theme/boost/amd/src/util.js
|
||||
theme/boost/amd/src/tether.js
|
||||
theme/boost/scss/fontawesome/
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
# PLEASE NOTE: Travis is not currently utilised by the Moodle core integration
|
||||
# process (which uses our internal CI system) this file is here for the benefit
|
||||
# of community developers git clones - see MDL-51458.
|
||||
|
||||
notifications:
|
||||
email:
|
||||
if: env(MOODLE_EMAIL) != no
|
||||
|
||||
language: php
|
||||
|
||||
os: linux
|
||||
|
||||
dist: xenial
|
||||
|
||||
services:
|
||||
- mysql
|
||||
- docker
|
||||
|
||||
addons:
|
||||
postgresql: "9.6"
|
||||
|
||||
jobs:
|
||||
# Enable fast finish.
|
||||
# This will fail the build if a single job fails (except those in allow_failures).
|
||||
# It will not stop the jobs from running.
|
||||
fast_finish: true
|
||||
|
||||
include:
|
||||
# First all the lowest php ones (7.1.30)
|
||||
# Make this sticky because current default version (7.1.11) has a bug with redis-extension output (MDL-66062)
|
||||
- php: 7.1.30
|
||||
env: DB=none TASK=CITEST
|
||||
- php: 7.1.30
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='lts/carbon'
|
||||
|
||||
- if: env(MOODLE_DATABASE) = "pgsql" OR env(MOODLE_DATABASE) = "all" OR env(MOODLE_DATABASE) IS NOT present
|
||||
php: 7.1.30
|
||||
env: DB=pgsql TASK=PHPUNIT
|
||||
|
||||
- if: env(MOODLE_DATABASE) = "mysqli" OR env(MOODLE_DATABASE) = "all"
|
||||
php: 7.1.30
|
||||
env: DB=mysqli TASK=PHPUNIT
|
||||
|
||||
# Then, conditionally, all the highest php ones (7.4)
|
||||
- if: env(MOODLE_PHP) = "all"
|
||||
php: 7.4
|
||||
env: DB=none TASK=CITEST
|
||||
- if: env(MOODLE_PHP) = "all"
|
||||
php: 7.4
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='lts/carbon'
|
||||
|
||||
- if: env(MOODLE_PHP) = "all" AND (env(MOODLE_DATABASE) = "pgsql" OR env(MOODLE_DATABASE) = "all" OR env(MOODLE_DATABASE) IS NOT present)
|
||||
php: 7.4
|
||||
env: DB=pgsql TASK=PHPUNIT
|
||||
|
||||
- if: env(MOODLE_PHP) = "all" AND (env(MOODLE_DATABASE) = "mysqli" OR env(MOODLE_DATABASE) = "all")
|
||||
php: 7.4
|
||||
env: DB=mysqli TASK=PHPUNIT
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
- $HOME/.npm
|
||||
|
||||
before_install:
|
||||
- docker run -d -p 127.0.0.1:8080:80 --name exttests moodlehq/moodle-exttests
|
||||
# Avoid IPv6 default binding as service (causes redis not to start).
|
||||
- sudo service redis-server start --bind 127.0.0.1
|
||||
|
||||
install:
|
||||
- >
|
||||
if [ "$DB" = 'mysqli' ];
|
||||
then
|
||||
sudo mkdir /mnt/ramdisk
|
||||
sudo mount -t tmpfs -o size=1024m tmpfs /mnt/ramdisk
|
||||
sudo service mysql stop
|
||||
sudo mv /var/lib/mysql /mnt/ramdisk
|
||||
sudo ln -s /mnt/ramdisk/mysql /var/lib/mysql
|
||||
sudo service mysql restart
|
||||
fi
|
||||
- >
|
||||
if [ "$DB" = 'pgsql' ];
|
||||
then
|
||||
sudo mkdir /mnt/ramdisk
|
||||
sudo mount -t tmpfs -o size=1024m tmpfs /mnt/ramdisk
|
||||
sudo service postgresql stop
|
||||
sudo mv /var/lib/postgresql /mnt/ramdisk
|
||||
sudo ln -s /mnt/ramdisk/postgresql /var/lib/postgresql
|
||||
sudo service postgresql start 9.6
|
||||
fi
|
||||
- >
|
||||
if [ "$TASK" = 'PHPUNIT' ];
|
||||
then
|
||||
if [ -n "$GITHUB_APITOKEN" ]; then
|
||||
composer config github-oauth.github.com $GITHUB_APITOKEN;
|
||||
echo 'auth.json' >> .git/info/exclude
|
||||
fi
|
||||
|
||||
echo 'extension="redis.so"' > /tmp/redis.ini
|
||||
phpenv config-add /tmp/redis.ini
|
||||
|
||||
# Install composer dependencies.
|
||||
# We need --no-interaction in case we hit API limits for composer. This causes it to fall back to a standard clone.
|
||||
# Typically it should be able to use the Composer cache if any other job has already completed before we started here.
|
||||
travis_retry composer install --prefer-dist --no-interaction;
|
||||
fi
|
||||
|
||||
- >
|
||||
if [ "$TASK" = 'GRUNT' ];
|
||||
then
|
||||
nvm install $NVM_VERSION ;
|
||||
nvm use $NVM_VERSION ;
|
||||
fi
|
||||
|
||||
before_script:
|
||||
- phpenv config-rm xdebug.ini
|
||||
- >
|
||||
if [ "$TASK" = 'PHPUNIT' ];
|
||||
then
|
||||
# Copy generic configuration in place.
|
||||
cp config-dist.php config.php ;
|
||||
|
||||
# Create the moodledata directory.
|
||||
mkdir -p "$HOME"/roots/base
|
||||
|
||||
# The database name and password.
|
||||
sed -i \
|
||||
-e "s%= 'moodle'%= 'travis_ci_test'%" \
|
||||
-e "s%= 'password'%= ''%" \
|
||||
config.php ;
|
||||
|
||||
# The wwwroot and dataroot.
|
||||
sed -i \
|
||||
-e "s%http://example.com/moodle%https://localhost%" \
|
||||
-e "s%/home/example/moodledata%/home/travis/roots/base%" \
|
||||
config.php ;
|
||||
|
||||
if [ "$DB" = 'pgsql' ];
|
||||
then
|
||||
# Postgres-specific setup.
|
||||
sed -i \
|
||||
-e "s%= 'username'%= 'postgres'%" \
|
||||
config.php ;
|
||||
|
||||
psql -c 'CREATE DATABASE travis_ci_test;' -U postgres;
|
||||
fi
|
||||
|
||||
if [ "$DB" = 'mysqli' ];
|
||||
then
|
||||
# MySQL-specific setup.
|
||||
sed -i \
|
||||
-e "s%= 'pgsql'%= 'mysqli'%" \
|
||||
-e "s%= 'username'%= 'travis'%" \
|
||||
-e "s%=> 'utf8mb4_unicode_ci'%=> 'utf8mb4_bin'%" \
|
||||
config.php;
|
||||
|
||||
mysql -u root -e 'SET GLOBAL innodb_file_format=barracuda;' ;
|
||||
mysql -u root -e 'SET GLOBAL innodb_file_per_table=ON;' ;
|
||||
mysql -u root -e 'SET GLOBAL innodb_large_prefix=ON;' ;
|
||||
mysql -e 'CREATE DATABASE travis_ci_test DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_bin;' ;
|
||||
fi
|
||||
fi
|
||||
|
||||
- >
|
||||
if [ "$TASK" = 'PHPUNIT' ];
|
||||
then
|
||||
# Create a directory for the phpunit dataroot.
|
||||
mkdir -p "$HOME"/roots/phpunit
|
||||
|
||||
# The phpunit dataroot and prefix..
|
||||
sed -i \
|
||||
-e "/require_once/i \\\$CFG->phpunit_dataroot = '\/home\/travis\/roots\/phpunit';" \
|
||||
-e "/require_once/i \\\$CFG->phpunit_prefix = 'p_';" \
|
||||
config.php ;
|
||||
# Enable test external resources
|
||||
sed -i \
|
||||
-e "/require_once/i \\define('TEST_EXTERNAL_FILES_HTTP_URL', 'http://127.0.0.1:8080');" \
|
||||
-e "/require_once/i \\define('TEST_EXTERNAL_FILES_HTTPS_URL', 'http://127.0.0.1:8080');" \
|
||||
config.php ;
|
||||
|
||||
# Redis cache store tests
|
||||
sed -i \
|
||||
-e "/require_once/i \\define('TEST_CACHESTORE_REDIS_TESTSERVERS', '127.0.0.1');" \
|
||||
config.php ;
|
||||
# Redis session tests, but not for PHP 7.2 and up. See MDL-60978 for more info.
|
||||
redissession="define('TEST_SESSION_REDIS_HOST', '127.0.0.1');"
|
||||
sed -i \
|
||||
-e "/require_once/i \\${redissession}" \
|
||||
config.php ;
|
||||
|
||||
# Initialise PHPUnit for Moodle.
|
||||
php admin/tool/phpunit/cli/init.php
|
||||
fi
|
||||
|
||||
- >
|
||||
if [ "$TASK" = 'GRUNT' ];
|
||||
then
|
||||
npm install --no-spin;
|
||||
npm install --no-spin -g grunt ;
|
||||
fi
|
||||
|
||||
########################################################################
|
||||
# CI Tests
|
||||
########################################################################
|
||||
- >
|
||||
if [ "$TASK" = 'CITEST' ];
|
||||
then
|
||||
# Note - this is deliberately placed in the script section as we
|
||||
# should not add any code until after phpunit has run.
|
||||
|
||||
# The following repositories are required.
|
||||
# The local_ci repository does the actual checking.
|
||||
git clone https://github.com/moodlehq/moodle-local_ci.git local/ci
|
||||
|
||||
# We need the official upstream for comparison
|
||||
git remote add upstream https://github.com/moodle/moodle.git;
|
||||
|
||||
git fetch upstream MOODLE_38_STABLE;
|
||||
export GIT_PREVIOUS_COMMIT="`git merge-base FETCH_HEAD $TRAVIS_COMMIT`";
|
||||
export GIT_COMMIT="$TRAVIS_COMMIT";
|
||||
export UPSTREAM_FETCH_HEAD=`git rev-parse FETCH_HEAD`
|
||||
|
||||
# Variables required by our linter.
|
||||
export gitcmd=`which git`;
|
||||
export gitdir="$TRAVIS_BUILD_DIR";
|
||||
export phpcmd=`which php`;
|
||||
fi
|
||||
|
||||
script:
|
||||
- >
|
||||
if [ "$TASK" = 'PHPUNIT' ];
|
||||
then
|
||||
vendor/bin/phpunit --fail-on-risky --disallow-test-output --verbose;
|
||||
fi
|
||||
|
||||
- >
|
||||
if [ "$TASK" = 'CITEST' ];
|
||||
then
|
||||
bash local/ci/php_lint/php_lint.sh;
|
||||
fi
|
||||
|
||||
- >
|
||||
if [ "$TASK" = 'GRUNT' ];
|
||||
then
|
||||
grunt ;
|
||||
# Add all files to the git index and then run diff --cached to see all changes.
|
||||
# This ensures that we get the status of all files, including new files.
|
||||
# We ignore npm-shrinkwrap.json to make the tasks immune to npm changes.
|
||||
git add . ;
|
||||
git reset -- npm-shrinkwrap.json ;
|
||||
git diff --cached --exit-code ;
|
||||
fi
|
||||
|
||||
after_script:
|
||||
- >
|
||||
if [ "$TASK" = 'PHPUNIT' ];
|
||||
then
|
||||
EXTTESTS_HITS=$(docker logs exttests 2>&1 | grep -Fv -e 'AH00558' -e '[pid 1]' | wc -l)
|
||||
echo -e "\nTest local resources number of hits: ${EXTTESTS_HITS}.\n"
|
||||
fi
|
||||
+613
-219
@@ -16,200 +16,26 @@
|
||||
/* eslint-env node */
|
||||
|
||||
/**
|
||||
* Grunt configuration for Moodle.
|
||||
*
|
||||
* @copyright 2014 Andrew Nicols
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Setup the Grunt Moodle environment.
|
||||
*
|
||||
* @param {Grunt} grunt
|
||||
* @returns {Object}
|
||||
* Grunt configuration
|
||||
*/
|
||||
const setupMoodleEnvironment = grunt => {
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const ComponentList = require(path.join(process.cwd(), '.grunt', 'components.js'));
|
||||
|
||||
const getAmdConfiguration = () => {
|
||||
// If the cwd is the amd directory in the current component then it will be empty.
|
||||
// If the cwd is a child of the component's AMD directory, the relative directory will not start with ..
|
||||
let inAMD = !path.relative(`${componentDirectory}/amd`, cwd).startsWith('..');
|
||||
|
||||
// Globbing pattern for matching all AMD JS source files.
|
||||
let amdSrc = [];
|
||||
if (inComponent) {
|
||||
amdSrc.push(
|
||||
componentDirectory + "/amd/src/*.js",
|
||||
componentDirectory + "/amd/src/**/*.js"
|
||||
);
|
||||
} else {
|
||||
amdSrc = ComponentList.getAmdSrcGlobList();
|
||||
}
|
||||
|
||||
return {
|
||||
inAMD,
|
||||
amdSrc,
|
||||
};
|
||||
};
|
||||
|
||||
const getYuiConfiguration = () => {
|
||||
let yuiSrc = [];
|
||||
if (inComponent) {
|
||||
yuiSrc.push(componentDirectory + "/yui/src/**/*.js");
|
||||
} else {
|
||||
yuiSrc = ComponentList.getYuiSrcGlobList(gruntFilePath + '/');
|
||||
}
|
||||
|
||||
return {
|
||||
yuiSrc,
|
||||
};
|
||||
};
|
||||
|
||||
const getStyleConfiguration = () => {
|
||||
const ComponentList = require(path.join(process.cwd(), '.grunt', 'components.js'));
|
||||
// Build the cssSrc and scssSrc.
|
||||
// Valid paths are:
|
||||
// [component]/styles.css; and either
|
||||
// [theme/[themename]]/scss/**/*.scss; or
|
||||
// [theme/[themename]]/style/*.css.
|
||||
//
|
||||
// If a theme has scss, then it is assumed that the style directory contains generated content.
|
||||
let cssSrc = [];
|
||||
let scssSrc = [];
|
||||
|
||||
const checkComponentDirectory = componentDirectory => {
|
||||
const isTheme = componentDirectory.startsWith('theme/');
|
||||
if (isTheme) {
|
||||
const scssDirectory = `${componentDirectory}/scss`;
|
||||
|
||||
if (fs.existsSync(scssDirectory)) {
|
||||
// This theme has an SCSS directory.
|
||||
// Include all scss files within it recursively, but do not check for css files.
|
||||
scssSrc.push(`${scssDirectory}/*.scss`);
|
||||
scssSrc.push(`${scssDirectory}/**/*.scss`);
|
||||
} else {
|
||||
// This theme has no SCSS directory.
|
||||
// Only hte CSS files in the top-level directory are checked.
|
||||
cssSrc.push(`${componentDirectory}/style/*.css`);
|
||||
}
|
||||
} else {
|
||||
// This is not a theme.
|
||||
// All other plugin types are restricted to a single styles.css in their top level.
|
||||
cssSrc.push(`${componentDirectory}/styles.css`);
|
||||
}
|
||||
};
|
||||
|
||||
if (inComponent) {
|
||||
checkComponentDirectory(componentDirectory);
|
||||
} else {
|
||||
ComponentList.getComponentPaths(`${gruntFilePath}/`).forEach(componentPath => {
|
||||
checkComponentDirectory(componentPath);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
cssSrc,
|
||||
scssSrc,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the cwd, taking into consideration the `root` option (for Windows).
|
||||
*
|
||||
* @param {Object} grunt
|
||||
* @returns {String} The current directory as best we can determine
|
||||
*/
|
||||
const getCwd = grunt => {
|
||||
let cwd = fs.realpathSync(process.env.PWD || process.cwd());
|
||||
|
||||
// Windows users can't run grunt in a subdirectory, so allow them to set
|
||||
// the root by passing --root=path/to/dir.
|
||||
if (grunt.option('root')) {
|
||||
const root = grunt.option('root');
|
||||
if (grunt.file.exists(__dirname, root)) {
|
||||
cwd = fs.realpathSync(path.join(__dirname, root));
|
||||
grunt.log.ok('Setting root to ' + cwd);
|
||||
} else {
|
||||
grunt.fail.fatal('Setting root to ' + root + ' failed - path does not exist');
|
||||
}
|
||||
}
|
||||
|
||||
return cwd;
|
||||
};
|
||||
|
||||
// Detect directories:
|
||||
// * gruntFilePath The real path on disk to this Gruntfile.js
|
||||
// * cwd The current working directory, which can be overridden by the `root` option
|
||||
// * relativeCwd The cwd, relative to the Gruntfile.js
|
||||
// * componentDirectory The root directory of the component if the cwd is in a valid component
|
||||
// * inComponent Whether the cwd is in a valid component
|
||||
// * runDir The componentDirectory or cwd if not in a component, relative to Gruntfile.js
|
||||
// * fullRunDir The full path to the runDir
|
||||
const gruntFilePath = fs.realpathSync(process.cwd());
|
||||
const cwd = getCwd(grunt);
|
||||
const relativeCwd = path.relative(gruntFilePath, cwd);
|
||||
const componentDirectory = ComponentList.getOwningComponentDirectory(relativeCwd);
|
||||
const inComponent = !!componentDirectory;
|
||||
const inTheme = !!componentDirectory && componentDirectory.startsWith('theme/');
|
||||
const runDir = inComponent ? componentDirectory : relativeCwd;
|
||||
const fullRunDir = fs.realpathSync(gruntFilePath + path.sep + runDir);
|
||||
const {inAMD, amdSrc} = getAmdConfiguration();
|
||||
const {yuiSrc} = getYuiConfiguration();
|
||||
const {cssSrc, scssSrc} = getStyleConfiguration();
|
||||
|
||||
let files = null;
|
||||
if (grunt.option('files')) {
|
||||
// Accept a comma separated list of files to process.
|
||||
files = grunt.option('files').split(',');
|
||||
}
|
||||
|
||||
grunt.log.debug('============================================================================');
|
||||
grunt.log.debug(`= Node version: ${process.versions.node}`);
|
||||
grunt.log.debug(`= grunt version: ${grunt.package.version}`);
|
||||
grunt.log.debug(`= process.cwd: '` + process.cwd() + `'`);
|
||||
grunt.log.debug(`= process.env.PWD: '${process.env.PWD}'`);
|
||||
grunt.log.debug(`= path.sep '${path.sep}'`);
|
||||
grunt.log.debug('============================================================================');
|
||||
grunt.log.debug(`= gruntFilePath: '${gruntFilePath}'`);
|
||||
grunt.log.debug(`= relativeCwd: '${relativeCwd}'`);
|
||||
grunt.log.debug(`= componentDirectory: '${componentDirectory}'`);
|
||||
grunt.log.debug(`= inComponent: '${inComponent}'`);
|
||||
grunt.log.debug(`= runDir: '${runDir}'`);
|
||||
grunt.log.debug(`= fullRunDir: '${fullRunDir}'`);
|
||||
grunt.log.debug('============================================================================');
|
||||
|
||||
if (inComponent) {
|
||||
grunt.log.ok(`Running tasks for component directory ${componentDirectory}`);
|
||||
}
|
||||
|
||||
return {
|
||||
amdSrc,
|
||||
componentDirectory,
|
||||
cwd,
|
||||
cssSrc,
|
||||
files,
|
||||
fullRunDir,
|
||||
gruntFilePath,
|
||||
inAMD,
|
||||
inComponent,
|
||||
inTheme,
|
||||
relativeCwd,
|
||||
runDir,
|
||||
scssSrc,
|
||||
yuiSrc,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify tha tthe current NodeJS version matches the required version in package.json.
|
||||
*
|
||||
* @param {Grunt} grunt
|
||||
*/
|
||||
const verifyNodeVersion = grunt => {
|
||||
const semver = require('semver');
|
||||
/* eslint-env node */
|
||||
module.exports = function(grunt) {
|
||||
var path = require('path'),
|
||||
tasks = {},
|
||||
cwd = process.env.PWD || process.cwd(),
|
||||
async = require('async'),
|
||||
DOMParser = require('xmldom').DOMParser,
|
||||
xpath = require('xpath'),
|
||||
semver = require('semver'),
|
||||
watchman = require('fb-watchman'),
|
||||
watchmanClient = new watchman.Client(),
|
||||
gruntFilePath = process.cwd();
|
||||
|
||||
// Verify the node version is new enough.
|
||||
var expected = semver.validRange(grunt.file.readJSON('package.json').engines.node);
|
||||
@@ -217,50 +43,618 @@ const verifyNodeVersion = grunt => {
|
||||
if (!semver.satisfies(actual, expected)) {
|
||||
grunt.fail.fatal('Node version not satisfied. Require ' + expected + ', version installed: ' + actual);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Grunt configuration.
|
||||
*
|
||||
* @param {Grunt} grunt
|
||||
*/
|
||||
module.exports = function(grunt) {
|
||||
// Verify that the Node version meets our requirements.
|
||||
verifyNodeVersion(grunt);
|
||||
// Windows users can't run grunt in a subdirectory, so allow them to set
|
||||
// the root by passing --root=path/to/dir.
|
||||
if (grunt.option('root')) {
|
||||
var root = grunt.option('root');
|
||||
if (grunt.file.exists(__dirname, root)) {
|
||||
cwd = path.join(__dirname, root);
|
||||
grunt.log.ok('Setting root to ' + cwd);
|
||||
} else {
|
||||
grunt.fail.fatal('Setting root to ' + root + ' failed - path does not exist');
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the Moodle environemnt within the Grunt object.
|
||||
grunt.moodleEnv = setupMoodleEnvironment(grunt);
|
||||
var files = null;
|
||||
if (grunt.option('files')) {
|
||||
// Accept a comma separated list of files to process.
|
||||
files = grunt.option('files').split(',');
|
||||
}
|
||||
|
||||
var inAMD = path.basename(cwd) == 'amd';
|
||||
|
||||
// Globbing pattern for matching all AMD JS source files.
|
||||
var amdSrc = [];
|
||||
if (inAMD) {
|
||||
amdSrc.push(cwd + "/src/*.js");
|
||||
amdSrc.push(cwd + "/src/**/*.js");
|
||||
} else {
|
||||
amdSrc.push("**/amd/src/*.js");
|
||||
amdSrc.push("**/amd/src/**/*.js");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the named task.
|
||||
* Function to generate the destination for the uglify task
|
||||
* (e.g. build/file.min.js). This function will be passed to
|
||||
* the rename property of files array when building dynamically:
|
||||
* http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {Grunt} grunt
|
||||
* @param {String} destPath the current destination
|
||||
* @param {String} srcPath the matched src path
|
||||
* @return {String} The rewritten destination path.
|
||||
*/
|
||||
const addTask = (name, grunt) => {
|
||||
const path = require('path');
|
||||
const taskPath = path.resolve(`./.grunt/tasks/${name}.js`);
|
||||
|
||||
grunt.log.debug(`Including tasks for ${name} from ${taskPath}`);
|
||||
|
||||
require(path.resolve(`./.grunt/tasks/${name}.js`))(grunt);
|
||||
var babelRename = function(destPath, srcPath) {
|
||||
destPath = srcPath.replace('src', 'build');
|
||||
destPath = destPath.replace('.js', '.min.js');
|
||||
return destPath;
|
||||
};
|
||||
|
||||
// Startup tasks.
|
||||
grunt.moodleEnv.startupTasks = [];
|
||||
/**
|
||||
* Find thirdpartylibs.xml and generate an array of paths contained within
|
||||
* them (used to generate ignore files and so on).
|
||||
*
|
||||
* @return {array} The list of thirdparty paths.
|
||||
*/
|
||||
var getThirdPartyPathsFromXML = function() {
|
||||
var thirdpartyfiles = grunt.file.expand('*/**/thirdpartylibs.xml');
|
||||
var libs = ['node_modules/', 'vendor/'];
|
||||
|
||||
// Add Moodle task configuration.
|
||||
addTask('gherkinlint', grunt);
|
||||
addTask('ignorefiles', grunt);
|
||||
thirdpartyfiles.forEach(function(file) {
|
||||
var dirname = path.dirname(file);
|
||||
|
||||
addTask('javascript', grunt);
|
||||
addTask('style', grunt);
|
||||
addTask('componentlibrary', grunt);
|
||||
var doc = new DOMParser().parseFromString(grunt.file.read(file));
|
||||
var nodes = xpath.select("/libraries/library/location/text()", doc);
|
||||
|
||||
addTask('watch', grunt);
|
||||
addTask('startup', grunt);
|
||||
nodes.forEach(function(node) {
|
||||
var lib = path.join(dirname, node.toString());
|
||||
if (grunt.file.isDir(lib)) {
|
||||
// Ensure trailing slash on dirs.
|
||||
lib = lib.replace(/\/?$/, '/');
|
||||
}
|
||||
|
||||
addTask('upgradablelibs', grunt);
|
||||
// Look for duplicate paths before adding to array.
|
||||
if (libs.indexOf(lib) === -1) {
|
||||
libs.push(lib);
|
||||
}
|
||||
});
|
||||
});
|
||||
return libs;
|
||||
};
|
||||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
eslint: {
|
||||
// Even though warnings dont stop the build we don't display warnings by default because
|
||||
// at this moment we've got too many core warnings.
|
||||
// To display warnings call: grunt eslint --show-lint-warnings
|
||||
// To fail on warnings call: grunt eslint --max-lint-warnings=0
|
||||
// Also --max-lint-warnings=-1 can be used to display warnings but not fail.
|
||||
options: {
|
||||
quiet: (!grunt.option('show-lint-warnings')) && (typeof grunt.option('max-lint-warnings') === 'undefined'),
|
||||
maxWarnings: ((typeof grunt.option('max-lint-warnings') !== 'undefined') ? grunt.option('max-lint-warnings') : -1)
|
||||
},
|
||||
amd: {src: files ? files : amdSrc},
|
||||
// Check YUI module source files.
|
||||
yui: {src: files ? files : ['**/yui/src/**/*.js', '!*/**/yui/src/*/meta/*.js']}
|
||||
},
|
||||
babel: {
|
||||
options: {
|
||||
sourceMaps: true,
|
||||
comments: false,
|
||||
plugins: [
|
||||
'transform-es2015-modules-amd-lazy',
|
||||
'system-import-transformer',
|
||||
// This plugin modifies the Babel transpiling for "export default"
|
||||
// so that if it's used then only the exported value is returned
|
||||
// by the generated AMD module.
|
||||
//
|
||||
// It also adds the Moodle plugin name to the AMD module definition
|
||||
// so that it can be imported as expected in other modules.
|
||||
path.resolve('babel-plugin-add-module-to-define.js'),
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'@babel/plugin-syntax-import-meta',
|
||||
['@babel/plugin-proposal-class-properties', {'loose': false}],
|
||||
'@babel/plugin-proposal-json-strings'
|
||||
],
|
||||
presets: [
|
||||
['minify', {
|
||||
// This minification plugin needs to be disabled because it breaks the
|
||||
// source map generation and causes invalid source maps to be output.
|
||||
simplify: false,
|
||||
builtIns: false
|
||||
}],
|
||||
['@babel/preset-env', {
|
||||
targets: {
|
||||
browsers: [
|
||||
">0.25%",
|
||||
"last 2 versions",
|
||||
"not ie <= 10",
|
||||
"not op_mini all",
|
||||
"not Opera > 0",
|
||||
"not dead"
|
||||
]
|
||||
},
|
||||
modules: false,
|
||||
useBuiltIns: false
|
||||
}]
|
||||
]
|
||||
},
|
||||
dist: {
|
||||
files: [{
|
||||
expand: true,
|
||||
src: files ? files : amdSrc,
|
||||
rename: babelRename
|
||||
}]
|
||||
}
|
||||
},
|
||||
sass: {
|
||||
dist: {
|
||||
files: {
|
||||
"theme/boost/style/moodle.css": "theme/boost/scss/preset/default.scss",
|
||||
"theme/classic/style/moodle.css": "theme/classic/scss/classicgrunt.scss"
|
||||
}
|
||||
},
|
||||
options: {
|
||||
includePaths: ["theme/boost/scss/", "theme/classic/scss/"]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
options: {
|
||||
nospawn: true // We need not to spawn so config can be changed dynamically.
|
||||
},
|
||||
amd: {
|
||||
files: ['**/amd/src/**/*.js'],
|
||||
tasks: ['amd']
|
||||
},
|
||||
boost: {
|
||||
files: ['**/theme/boost/scss/**/*.scss'],
|
||||
tasks: ['scss']
|
||||
},
|
||||
rawcss: {
|
||||
files: ['**/*.css', '**/theme/**/!(moodle.css|editor.css)'],
|
||||
tasks: ['rawcss']
|
||||
},
|
||||
yui: {
|
||||
files: ['**/yui/src/**/*.js'],
|
||||
tasks: ['yui']
|
||||
},
|
||||
gherkinlint: {
|
||||
files: ['**/tests/behat/*.feature'],
|
||||
tasks: ['gherkinlint']
|
||||
}
|
||||
},
|
||||
shifter: {
|
||||
options: {
|
||||
recursive: true,
|
||||
paths: files ? files : [cwd]
|
||||
}
|
||||
},
|
||||
gherkinlint: {
|
||||
options: {
|
||||
files: files ? files : ['**/tests/behat/*.feature'],
|
||||
}
|
||||
},
|
||||
stylelint: {
|
||||
scss: {
|
||||
options: {syntax: 'scss'},
|
||||
src: files ? files : ['*/**/*.scss']
|
||||
},
|
||||
css: {
|
||||
src: files ? files : ['*/**/*.css'],
|
||||
options: {
|
||||
configOverrides: {
|
||||
rules: {
|
||||
// These rules have to be disabled in .stylelintrc for scss compat.
|
||||
"at-rule-no-unknown": true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Generate ignore files (utilising thirdpartylibs.xml data)
|
||||
*/
|
||||
tasks.ignorefiles = function() {
|
||||
// An array of paths to third party directories.
|
||||
var thirdPartyPaths = getThirdPartyPathsFromXML();
|
||||
// Generate .eslintignore.
|
||||
var eslintIgnores = ['# Generated by "grunt ignorefiles"', '*/**/yui/src/*/meta/', '*/**/build/'].concat(thirdPartyPaths);
|
||||
grunt.file.write('.eslintignore', eslintIgnores.join('\n'));
|
||||
// Generate .stylelintignore.
|
||||
var stylelintIgnores = [
|
||||
'# Generated by "grunt ignorefiles"',
|
||||
'**/yui/build/*',
|
||||
'theme/boost/style/moodle.css',
|
||||
'theme/classic/style/moodle.css',
|
||||
].concat(thirdPartyPaths);
|
||||
grunt.file.write('.stylelintignore', stylelintIgnores.join('\n'));
|
||||
};
|
||||
|
||||
/**
|
||||
* Shifter task. Is configured with a path to a specific file or a directory,
|
||||
* in the case of a specific file it will work out the right module to be built.
|
||||
*
|
||||
* Note that this task runs the invidiaul shifter jobs async (becase it spawns
|
||||
* so be careful to to call done().
|
||||
*/
|
||||
tasks.shifter = function() {
|
||||
var done = this.async(),
|
||||
options = grunt.config('shifter.options');
|
||||
|
||||
// Run the shifter processes one at a time to avoid confusing output.
|
||||
async.eachSeries(options.paths, function(src, filedone) {
|
||||
var args = [];
|
||||
args.push(path.normalize(__dirname + '/node_modules/shifter/bin/shifter'));
|
||||
|
||||
// Always ignore the node_modules directory.
|
||||
args.push('--excludes', 'node_modules');
|
||||
|
||||
// Determine the most appropriate options to run with based upon the current location.
|
||||
if (grunt.file.isMatch('**/yui/**/*.js', src)) {
|
||||
// When passed a JS file, build our containing module (this happen with
|
||||
// watch).
|
||||
grunt.log.debug('Shifter passed a specific JS file');
|
||||
src = path.dirname(path.dirname(src));
|
||||
options.recursive = false;
|
||||
} else if (grunt.file.isMatch('**/yui/src', src)) {
|
||||
// When in a src directory --walk all modules.
|
||||
grunt.log.debug('In a src directory');
|
||||
args.push('--walk');
|
||||
options.recursive = false;
|
||||
} else if (grunt.file.isMatch('**/yui/src/*', src)) {
|
||||
// When in module, only build our module.
|
||||
grunt.log.debug('In a module directory');
|
||||
options.recursive = false;
|
||||
} else if (grunt.file.isMatch('**/yui/src/*/js', src)) {
|
||||
// When in module src, only build our module.
|
||||
grunt.log.debug('In a source directory');
|
||||
src = path.dirname(src);
|
||||
options.recursive = false;
|
||||
}
|
||||
|
||||
if (grunt.option('watch')) {
|
||||
grunt.fail.fatal('The --watch option has been removed, please use `grunt watch` instead');
|
||||
}
|
||||
|
||||
// Add the stderr option if appropriate
|
||||
if (grunt.option('verbose')) {
|
||||
args.push('--lint-stderr');
|
||||
}
|
||||
|
||||
if (grunt.option('no-color')) {
|
||||
args.push('--color=false');
|
||||
}
|
||||
|
||||
var execShifter = function() {
|
||||
|
||||
grunt.log.ok("Running shifter on " + src);
|
||||
grunt.util.spawn({
|
||||
cmd: "node",
|
||||
args: args,
|
||||
opts: {cwd: src, stdio: 'inherit', env: process.env}
|
||||
}, function(error, result, code) {
|
||||
if (code) {
|
||||
grunt.fail.fatal('Shifter failed with code: ' + code);
|
||||
} else {
|
||||
grunt.log.ok('Shifter build complete.');
|
||||
filedone();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Actually run shifter.
|
||||
if (!options.recursive) {
|
||||
execShifter();
|
||||
} else {
|
||||
// Check that there are yui modules otherwise shifter ends with exit code 1.
|
||||
if (grunt.file.expand({cwd: src}, '**/yui/src/**/*.js').length > 0) {
|
||||
args.push('--recursive');
|
||||
execShifter();
|
||||
} else {
|
||||
grunt.log.ok('No YUI modules to build.');
|
||||
filedone();
|
||||
}
|
||||
}
|
||||
}, done);
|
||||
};
|
||||
|
||||
tasks.gherkinlint = function() {
|
||||
const done = this.async();
|
||||
const options = grunt.config('gherkinlint.options');
|
||||
|
||||
// Grab the gherkin-lint linter and required scaffolding.
|
||||
const linter = require('gherkin-lint/src/linter.js');
|
||||
const featureFinder = require('gherkin-lint/src/feature-finder.js');
|
||||
const configParser = require('gherkin-lint/src/config-parser.js');
|
||||
const formatter = require('gherkin-lint/src/formatters/stylish.js');
|
||||
|
||||
// Run the linter.
|
||||
const results = linter.lint(
|
||||
featureFinder.getFeatureFiles(grunt.file.expand(options.files)),
|
||||
configParser.getConfiguration(configParser.defaultConfigFileName)
|
||||
);
|
||||
|
||||
// Print the results out uncondtionally.
|
||||
formatter.printResults(results);
|
||||
|
||||
// Report on the results.
|
||||
// The done function takes a bool whereby a falsey statement causes the task to fail.
|
||||
done(results.every(result => result.errors.length === 0));
|
||||
};
|
||||
|
||||
tasks.startup = function() {
|
||||
// Are we in a YUI directory?
|
||||
if (path.basename(path.resolve(cwd, '../../')) == 'yui') {
|
||||
grunt.task.run('yui');
|
||||
// Are we in an AMD directory?
|
||||
} else if (inAMD) {
|
||||
grunt.task.run('amd');
|
||||
} else {
|
||||
// Run them all!.
|
||||
grunt.task.run('css');
|
||||
grunt.task.run('js');
|
||||
grunt.task.run('gherkinlint');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a wrapper task to handle the grunt watch command. It attempts to use
|
||||
* Watchman to monitor for file changes, if it's installed, because it's much faster.
|
||||
*
|
||||
* If Watchman isn't installed then it falls back to the grunt-contrib-watch file
|
||||
* watcher for backwards compatibility.
|
||||
*/
|
||||
tasks.watch = function() {
|
||||
var watchTaskDone = this.async();
|
||||
var watchInitialised = false;
|
||||
var watchTaskQueue = {};
|
||||
var processingQueue = false;
|
||||
|
||||
// Grab the tasks and files that have been queued up and execute them.
|
||||
var processWatchTaskQueue = function() {
|
||||
if (!Object.keys(watchTaskQueue).length || processingQueue) {
|
||||
// If there is nothing in the queue or we're already processing then wait.
|
||||
return;
|
||||
}
|
||||
|
||||
processingQueue = true;
|
||||
|
||||
// Grab all tasks currently in the queue.
|
||||
var queueToProcess = watchTaskQueue;
|
||||
// Reset the queue.
|
||||
watchTaskQueue = {};
|
||||
|
||||
async.forEachSeries(
|
||||
Object.keys(queueToProcess),
|
||||
function(task, next) {
|
||||
var files = queueToProcess[task];
|
||||
var filesOption = '--files=' + files.join(',');
|
||||
grunt.log.ok('Running task ' + task + ' for files ' + filesOption);
|
||||
|
||||
// Spawn the task in a child process so that it doesn't kill this one
|
||||
// if it failed.
|
||||
grunt.util.spawn(
|
||||
{
|
||||
// Spawn with the grunt bin.
|
||||
grunt: true,
|
||||
// Run from current working dir and inherit stdio from process.
|
||||
opts: {
|
||||
cwd: cwd,
|
||||
stdio: 'inherit'
|
||||
},
|
||||
args: [task, filesOption]
|
||||
},
|
||||
function(err, res, code) {
|
||||
if (code !== 0) {
|
||||
// The grunt task failed.
|
||||
grunt.log.error(err);
|
||||
}
|
||||
|
||||
// Move on to the next task.
|
||||
next();
|
||||
}
|
||||
);
|
||||
},
|
||||
function() {
|
||||
// No longer processing.
|
||||
processingQueue = false;
|
||||
// Once all of the tasks are done then recurse just in case more tasks
|
||||
// were queued while we were processing.
|
||||
processWatchTaskQueue();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
var watchConfig = grunt.config.get(['watch']);
|
||||
watchConfig = Object.keys(watchConfig).reduce(function(carry, key) {
|
||||
if (key == 'options') {
|
||||
return carry;
|
||||
}
|
||||
|
||||
var value = watchConfig[key];
|
||||
var fileGlobs = value.files;
|
||||
var taskNames = value.tasks;
|
||||
|
||||
taskNames.forEach(function(taskName) {
|
||||
carry[taskName] = fileGlobs;
|
||||
});
|
||||
|
||||
return carry;
|
||||
}, {});
|
||||
|
||||
watchmanClient.on('error', function(error) {
|
||||
// We have to add an error handler here and parse the error string because the
|
||||
// example way from the docs to check if Watchman is installed doesn't actually work!!
|
||||
// See: https://github.com/facebook/watchman/issues/509
|
||||
if (error.message.match('Watchman was not found')) {
|
||||
// If watchman isn't installed then we should fallback to the other watch task.
|
||||
grunt.log.ok('It is recommended that you install Watchman for better performance using the "watch" command.');
|
||||
|
||||
// Fallback to the old grunt-contrib-watch task.
|
||||
grunt.renameTask('watch-grunt', 'watch');
|
||||
grunt.task.run(['watch']);
|
||||
// This task is finished.
|
||||
watchTaskDone(0);
|
||||
} else {
|
||||
grunt.log.error(error);
|
||||
// Fatal error.
|
||||
watchTaskDone(1);
|
||||
}
|
||||
});
|
||||
|
||||
watchmanClient.on('subscription', function(resp) {
|
||||
if (resp.subscription !== 'grunt-watch') {
|
||||
return;
|
||||
}
|
||||
|
||||
resp.files.forEach(function(file) {
|
||||
grunt.log.ok('File changed: ' + file.name);
|
||||
|
||||
var fullPath = cwd + '/' + file.name;
|
||||
Object.keys(watchConfig).forEach(function(task) {
|
||||
var fileGlobs = watchConfig[task];
|
||||
var match = fileGlobs.every(function(fileGlob) {
|
||||
return grunt.file.isMatch(fileGlob, fullPath);
|
||||
});
|
||||
if (match) {
|
||||
// If we are watching a subdirectory then the file.name will be relative
|
||||
// to that directory. However the grunt tasks expect the file paths to be
|
||||
// relative to the Gruntfile.js location so let's normalise them before
|
||||
// adding them to the queue.
|
||||
var relativePath = fullPath.replace(gruntFilePath + '/', '');
|
||||
if (task in watchTaskQueue) {
|
||||
if (!watchTaskQueue[task].includes(relativePath)) {
|
||||
watchTaskQueue[task] = watchTaskQueue[task].concat(relativePath);
|
||||
}
|
||||
} else {
|
||||
watchTaskQueue[task] = [relativePath];
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
processWatchTaskQueue();
|
||||
});
|
||||
|
||||
process.on('SIGINT', function() {
|
||||
// Let the user know that they may need to manually stop the Watchman daemon if they
|
||||
// no longer want it running.
|
||||
if (watchInitialised) {
|
||||
grunt.log.ok('The Watchman daemon may still be running and may need to be stopped manually.');
|
||||
}
|
||||
|
||||
process.exit();
|
||||
});
|
||||
|
||||
// Initiate the watch on the current directory.
|
||||
watchmanClient.command(['watch-project', cwd], function(watchError, watchResponse) {
|
||||
if (watchError) {
|
||||
grunt.log.error('Error initiating watch:', watchError);
|
||||
watchTaskDone(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if ('warning' in watchResponse) {
|
||||
grunt.log.error('warning: ', watchResponse.warning);
|
||||
}
|
||||
|
||||
var watch = watchResponse.watch;
|
||||
var relativePath = watchResponse.relative_path;
|
||||
watchInitialised = true;
|
||||
|
||||
watchmanClient.command(['clock', watch], function(clockError, clockResponse) {
|
||||
if (clockError) {
|
||||
grunt.log.error('Failed to query clock:', clockError);
|
||||
watchTaskDone(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the matching patterns specified in the watch config.
|
||||
var matches = Object.keys(watchConfig).map(function(task) {
|
||||
var fileGlobs = watchConfig[task];
|
||||
var fileGlobMatches = fileGlobs.map(function(fileGlob) {
|
||||
return ['match', fileGlob, 'wholename'];
|
||||
});
|
||||
|
||||
return ['allof'].concat(fileGlobMatches);
|
||||
});
|
||||
|
||||
var sub = {
|
||||
expression: ["anyof"].concat(matches),
|
||||
// Which fields we're interested in.
|
||||
fields: ["name", "size", "type"],
|
||||
// Add our time constraint.
|
||||
since: clockResponse.clock
|
||||
};
|
||||
|
||||
if (relativePath) {
|
||||
/* eslint-disable camelcase */
|
||||
sub.relative_root = relativePath;
|
||||
}
|
||||
|
||||
watchmanClient.command(['subscribe', watch, 'grunt-watch', sub], function(subscribeError) {
|
||||
if (subscribeError) {
|
||||
// Probably an error in the subscription criteria.
|
||||
grunt.log.error('failed to subscribe: ', subscribeError);
|
||||
watchTaskDone(1);
|
||||
return;
|
||||
}
|
||||
|
||||
grunt.log.ok('Listening for changes to files in ' + cwd);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// On watch, we dynamically modify config to build only affected files. This
|
||||
// method is slightly complicated to deal with multiple changed files at once (copied
|
||||
// from the grunt-contrib-watch readme).
|
||||
var changedFiles = Object.create(null);
|
||||
var onChange = grunt.util._.debounce(function() {
|
||||
var files = Object.keys(changedFiles);
|
||||
grunt.config('eslint.amd.src', files);
|
||||
grunt.config('eslint.yui.src', files);
|
||||
grunt.config('shifter.options.paths', files);
|
||||
grunt.config('gherkinlint.options.files', files);
|
||||
grunt.config('babel.dist.files', [{expand: true, src: files, rename: babelRename}]);
|
||||
changedFiles = Object.create(null);
|
||||
}, 200);
|
||||
|
||||
grunt.event.on('watch', function(action, filepath) {
|
||||
changedFiles[filepath] = action;
|
||||
onChange();
|
||||
});
|
||||
|
||||
// Register NPM tasks.
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-sass');
|
||||
grunt.loadNpmTasks('grunt-eslint');
|
||||
grunt.loadNpmTasks('grunt-stylelint');
|
||||
grunt.loadNpmTasks('grunt-babel');
|
||||
|
||||
// Rename the grunt-contrib-watch "watch" task because we're going to wrap it.
|
||||
grunt.renameTask('watch', 'watch-grunt');
|
||||
|
||||
// Register JS tasks.
|
||||
grunt.registerTask('shifter', 'Run Shifter against the current directory', tasks.shifter);
|
||||
grunt.registerTask('gherkinlint', 'Run gherkinlint against the current directory', tasks.gherkinlint);
|
||||
grunt.registerTask('ignorefiles', 'Generate ignore files for linters', tasks.ignorefiles);
|
||||
grunt.registerTask('watch', 'Run tasks on file changes', tasks.watch);
|
||||
grunt.registerTask('yui', ['eslint:yui', 'shifter']);
|
||||
grunt.registerTask('amd', ['eslint:amd', 'babel']);
|
||||
grunt.registerTask('js', ['amd', 'yui']);
|
||||
|
||||
// Register CSS taks.
|
||||
grunt.registerTask('css', ['stylelint:scss', 'sass', 'stylelint:css']);
|
||||
grunt.registerTask('scss', ['stylelint:scss', 'sass']);
|
||||
grunt.registerTask('rawcss', ['stylelint:css']);
|
||||
|
||||
// Register the startup task.
|
||||
grunt.registerTask('startup', 'Run the correct tasks for the current directory', tasks.startup);
|
||||
|
||||
// Register the default task.
|
||||
grunt.registerTask('default', ['startup']);
|
||||
|
||||
+35
-53
@@ -1,66 +1,48 @@
|
||||
MOODLE TRADEMARKS POLICY
|
||||
========================
|
||||
|
||||
The Moodle Trademark
|
||||
--------------------
|
||||
The word "Moodle" is trademarked in many countries around the world. The word
|
||||
was originally an acronym: Modular Object-Oriented Dynamic Learning
|
||||
Environment. The trademark is owned by Martin Dougiamas, Founder and Director
|
||||
of Moodle.
|
||||
|
||||
The brand and word "Moodle" has trade mark protection in many countries around
|
||||
the world. Various Moodle™ trade marks are owned by either Moodle Pty Ltd or
|
||||
one of its associated entities. The law obligates trade mark owners to police
|
||||
their trade marks and prevent the use of confusingly similar names by
|
||||
third parties.
|
||||
The law obligates trademark owners to police their marks and prevent the use of
|
||||
confusingly similar names by third parties. Through this policy we’d like to
|
||||
make it clear how Moodle-related projects, organisations, and people can use
|
||||
the Moodle trademark. We’d also like to be clear about how use of the word is
|
||||
restricted when used to promote commercial Moodle services. We do this to
|
||||
protect the very business model that allows us to continue developing Moodle
|
||||
for you.
|
||||
|
||||
We have created a more concise and thorough set of Trademark Guidelines
|
||||
(https://moodle.com/wp-content/uploads/2023/02/Moodle-Trademark-Guidelines-2023-4.pdf)
|
||||
to help you understand how to comply with Moodle's trade mark requirements.
|
||||
The Trademark Guidelines will also assist our Moodle Certified Partners and the
|
||||
community to understand some of the principles that inform Moodle's stance
|
||||
whenever potential infringement has been detected by us or our community.
|
||||
Allowed uses
|
||||
------------
|
||||
|
||||
We need to be clear about use of the word "Moodle" and how we restrict its use
|
||||
when promoting commercial Moodle™ branded services. The proper use of
|
||||
trade marks reliably assures consumers about the quality of the associated
|
||||
products or services and where they have been derived from. Because the
|
||||
Moodle LMS is made available under the open source ‘GNU General Public License'
|
||||
that permits you to modify the copyrighted software, the distribution of such
|
||||
modified software in combination with Moodle trade marks can potentially
|
||||
mislead others in the community. To be clear, the GNU GPL does not include an
|
||||
implied right or licence to use Moodle's trade marks.
|
||||
The following uses don’t require any permission at all:
|
||||
|
||||
We do all this to protect the very business model that allows us to continue
|
||||
developing various Moodle software solutions for you.
|
||||
|
||||
Allowed uses of "Moodle"
|
||||
------------------------
|
||||
|
||||
The following uses don't require any permission at all:
|
||||
|
||||
* Referring to the software or the Moodle project as the "Moodle™ software"
|
||||
or the "Moodle™ project"
|
||||
* Describing your own implementation of the Moodle™ software platform
|
||||
(including within corporate settings).
|
||||
* Referring to the software or the Moodle project.
|
||||
* Describing your own Moodle implementation (including within corporate
|
||||
settings).
|
||||
* Describing a Moodle-based community hub.
|
||||
* Describing certain software you've made that integrates with Moodle™
|
||||
branded software (e.g. a Moodle™ integration feature on another system).
|
||||
* Describing some software you’ve made that integrates with Moodle
|
||||
(eg a Moodle integration feature on another system).
|
||||
|
||||
Restricted uses of "Moodle"
|
||||
---------------------------
|
||||
Restricted uses
|
||||
---------------
|
||||
|
||||
The following uses are generally prohibited without explicit and direct
|
||||
permission being granted to you by Moodle Pty Ltd. We do this to protect the
|
||||
Moodle™ project from software and sites which could confuse people. If in
|
||||
doubt, reach out for clarification at <trademarks@moodle.com>
|
||||
Moodle project from software and sites which could confuse people. Please
|
||||
contact us to ask for permission in writing.
|
||||
|
||||
* You can't use the word "Moodle" or any Moodle logos for commercial purposes
|
||||
without prior written permission from Moodle HQ.
|
||||
* You can't use "Moodle" in the name of your software (including Mobile apps).
|
||||
* You can't use "Moodle" in your company name.
|
||||
* You can't use "Moodle" in your domain name.
|
||||
* You can't use "Moodle" in advertising-related keywords (such as Adsense).
|
||||
* You can't use "Moodle" to describe services around Moodle
|
||||
(such as hosting, training, support, consulting, course creation services,
|
||||
theme development, customisation, installation, integration, analytics
|
||||
and certification) that cause consumers to be confused that you are
|
||||
associated with Moodle HQ if you are not.
|
||||
This applies even if you do not charge for the services.
|
||||
Note that usually only Moodle Partners have this permission.
|
||||
* You can’t use "Moodle" in the name of your software (including Mobile apps)
|
||||
* You can’t use "Moodle" in your company name
|
||||
* You can’t use "Moodle" in your domain name
|
||||
* You can’t use "Moodle" in advertising-related keywords (such as Adsense)
|
||||
* You can’t use "Moodle" to describe services around Moodle (such as hosting,
|
||||
training, support, consulting, course creation services, theme development,
|
||||
customisation, installation, integration and certification). This applies
|
||||
even if you do not charge for the services. Note that usually only Moodle
|
||||
Partners have this permission.
|
||||
|
||||
For information about the Moodle Partner Certification Mark as well as for how
|
||||
to contact us, please see <https://moodle.com/trademarks/>.
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Admin settings search form
|
||||
*
|
||||
* @package admin
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once $CFG->libdir.'/formslib.php';
|
||||
|
||||
/**
|
||||
* Admin settings search form
|
||||
*
|
||||
* @package admin
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class admin_settings_search_form extends moodleform {
|
||||
function definition () {
|
||||
$mform = $this->_form;
|
||||
|
||||
//$mform->addElement('header', 'settingsheader', get_string('search', 'admin'));
|
||||
$elements = [];
|
||||
$elements[] = $mform->createElement('text', 'query', get_string('query', 'admin'));
|
||||
$elements[] = $mform->createElement('submit', 'search', get_string('search'));
|
||||
$mform->addGroup($elements);
|
||||
$mform->setType('query', PARAM_RAW);
|
||||
$mform->setDefault('query', optional_param('query', '', PARAM_RAW));
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
define("core_admin/block_management_table",["exports","./plugin_management_table","core_table/dynamic","core/ajax","core/pending","core/notification"],(function(_exports,_plugin_management_table,_dynamic,_ajax,_pending,_notification){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_plugin_management_table=_interopRequireDefault(_plugin_management_table),_pending=_interopRequireDefault(_pending);class _default extends _plugin_management_table.default{constructor(){super(),this.addClickHandler(this.handleBlockProtectToggle)}setBlockProtectState(plugin,state){return(0,_ajax.call)([{methodname:"core_admin_set_block_protection",args:{plugin:plugin,state:state}}])[0]}async handleBlockProtectToggle(tableRoot,e){const stateToggle=e.target.closest('[data-action="toggleprotectstate"]');if(stateToggle){e.preventDefault();const pendingPromise=new _pending.default("core_table/dynamic:processAction");await this.setBlockProtectState(stateToggle.dataset.plugin,"1"===stateToggle.dataset.targetState?1:0);const[updatedRoot]=await Promise.all([(0,_dynamic.refreshTableContent)(tableRoot),(0,_notification.fetchNotifications)()]);updatedRoot.querySelector('[data-action="toggleprotectstate"][data-plugin="'.concat(stateToggle.dataset.plugin,'"]')).focus(),pendingPromise.resolve()}}}return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=block_management_table.min.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"block_management_table.min.js","sources":["../src/block_management_table.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\nimport PluginManagementTable from './plugin_management_table';\nimport {refreshTableContent} from 'core_table/dynamic';\nimport {call as fetchMany} from 'core/ajax';\nimport Pending from 'core/pending';\nimport {fetchNotifications} from 'core/notification';\n\nexport default class extends PluginManagementTable {\n constructor() {\n super();\n this.addClickHandler(this.handleBlockProtectToggle);\n }\n\n /**\n * Set the block protection state.\n *\n * @param {string} plugin\n * @param {number} state\n * @returns {Promise}\n */\n setBlockProtectState(plugin, state) {\n return fetchMany([{\n methodname: 'core_admin_set_block_protection',\n args: {\n plugin,\n state,\n },\n }])[0];\n }\n\n /**\n * Handle toggling of block protection.\n *\n * @param {HTMLElement} tableRoot\n * @param {Event} e\n */\n async handleBlockProtectToggle(tableRoot, e) {\n const stateToggle = e.target.closest('[data-action=\"toggleprotectstate\"]');\n if (stateToggle) {\n e.preventDefault();\n const pendingPromise = new Pending('core_table/dynamic:processAction');\n\n await this.setBlockProtectState(\n stateToggle.dataset.plugin,\n stateToggle.dataset.targetState === '1' ? 1 : 0\n );\n\n const [updatedRoot] = await Promise.all([\n refreshTableContent(tableRoot),\n fetchNotifications(),\n ]);\n\n // Refocus on the link that as pressed in the first place.\n updatedRoot.querySelector(`[data-action=\"toggleprotectstate\"][data-plugin=\"${stateToggle.dataset.plugin}\"]`).focus();\n pendingPromise.resolve();\n }\n }\n}\n"],"names":["PluginManagementTable","constructor","addClickHandler","this","handleBlockProtectToggle","setBlockProtectState","plugin","state","methodname","args","tableRoot","e","stateToggle","target","closest","preventDefault","pendingPromise","Pending","dataset","targetState","updatedRoot","Promise","all","querySelector","focus","resolve"],"mappings":"uhBAqB6BA,iCACzBC,2BAESC,gBAAgBC,KAAKC,0BAU9BC,qBAAqBC,OAAQC,cAClB,cAAU,CAAC,CACdC,WAAY,kCACZC,KAAM,CACFH,OAAAA,OACAC,MAAAA,UAEJ,kCASuBG,UAAWC,SAChCC,YAAcD,EAAEE,OAAOC,QAAQ,yCACjCF,YAAa,CACbD,EAAEI,uBACIC,eAAiB,IAAIC,iBAAQ,0CAE7Bd,KAAKE,qBACPO,YAAYM,QAAQZ,OACgB,MAApCM,YAAYM,QAAQC,YAAsB,EAAI,SAG3CC,mBAAqBC,QAAQC,IAAI,EACpC,gCAAoBZ,YACpB,wCAIJU,YAAYG,wEAAiEX,YAAYM,QAAQZ,cAAYkB,QAC7GR,eAAeS"}
|
||||
@@ -1,3 +0,0 @@
|
||||
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
@@ -1,72 +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 PluginManagementTable from './plugin_management_table';
|
||||
import {refreshTableContent} from 'core_table/dynamic';
|
||||
import {call as fetchMany} from 'core/ajax';
|
||||
import Pending from 'core/pending';
|
||||
import {fetchNotifications} from 'core/notification';
|
||||
|
||||
export default class extends PluginManagementTable {
|
||||
constructor() {
|
||||
super();
|
||||
this.addClickHandler(this.handleBlockProtectToggle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the block protection state.
|
||||
*
|
||||
* @param {string} plugin
|
||||
* @param {number} state
|
||||
* @returns {Promise}
|
||||
*/
|
||||
setBlockProtectState(plugin, state) {
|
||||
return fetchMany([{
|
||||
methodname: 'core_admin_set_block_protection',
|
||||
args: {
|
||||
plugin,
|
||||
state,
|
||||
},
|
||||
}])[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle toggling of block protection.
|
||||
*
|
||||
* @param {HTMLElement} tableRoot
|
||||
* @param {Event} e
|
||||
*/
|
||||
async handleBlockProtectToggle(tableRoot, e) {
|
||||
const stateToggle = e.target.closest('[data-action="toggleprotectstate"]');
|
||||
if (stateToggle) {
|
||||
e.preventDefault();
|
||||
const pendingPromise = new Pending('core_table/dynamic:processAction');
|
||||
|
||||
await this.setBlockProtectState(
|
||||
stateToggle.dataset.plugin,
|
||||
stateToggle.dataset.targetState === '1' ? 1 : 0
|
||||
);
|
||||
|
||||
const [updatedRoot] = await Promise.all([
|
||||
refreshTableContent(tableRoot),
|
||||
fetchNotifications(),
|
||||
]);
|
||||
|
||||
// Refocus on the link that as pressed in the first place.
|
||||
updatedRoot.querySelector(`[data-action="toggleprotectstate"][data-plugin="${stateToggle.dataset.plugin}"]`).focus();
|
||||
pendingPromise.resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,179 +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 {refreshTableContent} from 'core_table/dynamic';
|
||||
import * as Selectors from 'core_table/local/dynamic/selectors';
|
||||
import {call as fetchMany} from 'core/ajax';
|
||||
import Pending from 'core/pending';
|
||||
import {fetchNotifications} from 'core/notification';
|
||||
|
||||
let watching = false;
|
||||
|
||||
export default class {
|
||||
/**
|
||||
* @property {function[]} clickHandlers a list of handlers to call on click.
|
||||
*/
|
||||
clickHandlers = [];
|
||||
|
||||
constructor() {
|
||||
this.addClickHandler(this.handleStateToggle);
|
||||
this.addClickHandler(this.handleMoveUpDown);
|
||||
this.registerEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise an instance of the class.
|
||||
*
|
||||
* This is just a way of making it easier to initialise an instance of the class from PHP.
|
||||
*/
|
||||
static init() {
|
||||
if (watching) {
|
||||
return;
|
||||
}
|
||||
watching = true;
|
||||
new this();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a click handler to the list of handlers.
|
||||
*
|
||||
* @param {Function} handler A handler to call on a click event
|
||||
*/
|
||||
addClickHandler(handler) {
|
||||
this.clickHandlers.push(handler.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the event listeners for this instance.
|
||||
*/
|
||||
registerEventListeners() {
|
||||
document.addEventListener('click', function(e) {
|
||||
const tableRoot = this.getTableRoot(e);
|
||||
|
||||
if (!tableRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.clickHandlers.forEach((handler) => handler(tableRoot, e));
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the table root from an event.
|
||||
*
|
||||
* @param {Event} e
|
||||
* @returns {HTMLElement|bool}
|
||||
*/
|
||||
getTableRoot(e) {
|
||||
const tableRoot = e.target.closest(Selectors.main.region);
|
||||
if (!tableRoot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return tableRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the plugin state (enabled or disabled)
|
||||
*
|
||||
* @param {string} methodname The web service to call
|
||||
* @param {string} plugin The name of the plugin to set the state for
|
||||
* @param {number} state The state to set
|
||||
* @returns {Promise}
|
||||
*/
|
||||
setPluginState(methodname, plugin, state) {
|
||||
return fetchMany([{
|
||||
methodname,
|
||||
args: {
|
||||
plugin,
|
||||
state,
|
||||
},
|
||||
}])[0];
|
||||
}
|
||||
|
||||
setPluginOrder(methodname, plugin, direction) {
|
||||
return fetchMany([{
|
||||
methodname,
|
||||
args: {
|
||||
plugin,
|
||||
direction,
|
||||
},
|
||||
}])[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle state toggling.
|
||||
*
|
||||
* @param {HTMLElement} tableRoot
|
||||
* @param {Event} e
|
||||
*/
|
||||
async handleStateToggle(tableRoot, e) {
|
||||
const stateToggle = e.target.closest('[data-action="togglestate"][data-toggle-method]');
|
||||
if (stateToggle) {
|
||||
e.preventDefault();
|
||||
const pendingPromise = new Pending('core_table/dynamic:togglestate');
|
||||
|
||||
await this.setPluginState(
|
||||
stateToggle.dataset.toggleMethod,
|
||||
stateToggle.dataset.plugin,
|
||||
stateToggle.dataset.state === '1' ? 0 : 1
|
||||
);
|
||||
|
||||
const [updatedRoot] = await Promise.all([
|
||||
refreshTableContent(tableRoot),
|
||||
fetchNotifications(),
|
||||
]);
|
||||
|
||||
// Refocus on the link that as pressed in the first place.
|
||||
updatedRoot.querySelector(`[data-action="togglestate"][data-plugin="${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('core_table/dynamic:processAction');
|
||||
|
||||
await this.setPluginOrder(
|
||||
actionLink.dataset.method,
|
||||
actionLink.dataset.plugin,
|
||||
actionLink.dataset.direction === 'up' ? -1 : 1,
|
||||
);
|
||||
|
||||
const [updatedRoot] = await Promise.all([
|
||||
refreshTableContent(tableRoot),
|
||||
fetchNotifications(),
|
||||
]);
|
||||
|
||||
// Refocus on the link that as pressed in the first place.
|
||||
const exactMatch = updatedRoot.querySelector(
|
||||
`[data-action="move"][data-plugin="${actionLink.dataset.plugin}"][data-direction="${actionLink.dataset.direction}"]`
|
||||
);
|
||||
if (exactMatch) {
|
||||
exactMatch.focus();
|
||||
} else {
|
||||
// The move link is not present anymore, so we need to focus on the other one.
|
||||
updatedRoot.querySelector(`[data-action="move"][data-plugin="${actionLink.dataset.plugin}"]`)?.focus();
|
||||
}
|
||||
|
||||
pendingPromise.resolve();
|
||||
}
|
||||
}
|
||||
+7
-15
@@ -54,19 +54,18 @@ if (!confirm_sesskey()) {
|
||||
redirect($returnurl);
|
||||
}
|
||||
|
||||
$needsupdate = false;
|
||||
switch ($action) {
|
||||
case 'disable':
|
||||
// Remove from enabled list.
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('antivirus');
|
||||
$class::enable_plugin($antivirus, false);
|
||||
$key = array_search($antivirus, $activeantiviruses);
|
||||
unset($activeantiviruses[$key]);
|
||||
break;
|
||||
|
||||
case 'enable':
|
||||
// Add to enabled list.
|
||||
if (!in_array($antivirus, $activeantiviruses)) {
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('antivirus');
|
||||
$class::enable_plugin($antivirus, true);
|
||||
$activeantiviruses[] = $antivirus;
|
||||
$activeantiviruses = array_unique($activeantiviruses);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -79,7 +78,6 @@ switch ($action) {
|
||||
$fsave = $activeantiviruses[$key];
|
||||
$activeantiviruses[$key] = $activeantiviruses[$key + 1];
|
||||
$activeantiviruses[$key + 1] = $fsave;
|
||||
$needsupdate = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -93,7 +91,6 @@ switch ($action) {
|
||||
$fsave = $activeantiviruses[$key];
|
||||
$activeantiviruses[$key] = $activeantiviruses[$key - 1];
|
||||
$activeantiviruses[$key - 1] = $fsave;
|
||||
$needsupdate = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -102,12 +99,7 @@ switch ($action) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($needsupdate) {
|
||||
$new = implode(',', $activeantiviruses);
|
||||
add_to_config_log('antiviruses', $CFG->antiviruses, $new, 'core');
|
||||
set_config('antiviruses', $new);
|
||||
core_plugin_manager::reset_caches();
|
||||
}
|
||||
set_config('antiviruses', implode(',', $activeantiviruses));
|
||||
core_plugin_manager::reset_caches();
|
||||
|
||||
|
||||
redirect ($returnurl);
|
||||
redirect ($returnurl);
|
||||
+27
-15
@@ -28,7 +28,7 @@ if (empty($CFG->auth)) {
|
||||
}
|
||||
|
||||
if (!empty($auth) and !exists_auth_plugin($auth)) {
|
||||
throw new \moodle_exception('pluginnotinstalled', 'auth', $returnurl, $auth);
|
||||
print_error('pluginnotinstalled', 'auth', $returnurl, $auth);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -40,31 +40,43 @@ if (!confirm_sesskey()) {
|
||||
|
||||
switch ($action) {
|
||||
case 'disable':
|
||||
// Remove from enabled list.
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('auth');
|
||||
$class::enable_plugin($auth, false);
|
||||
// remove from enabled list
|
||||
$key = array_search($auth, $authsenabled);
|
||||
if ($key !== false) {
|
||||
unset($authsenabled[$key]);
|
||||
set_config('auth', implode(',', $authsenabled));
|
||||
}
|
||||
|
||||
if ($auth == $CFG->registerauth) {
|
||||
set_config('registerauth', '');
|
||||
}
|
||||
\core\session\manager::gc(); // Remove stale sessions.
|
||||
core_plugin_manager::reset_caches();
|
||||
break;
|
||||
|
||||
case 'enable':
|
||||
// Add to enabled list.
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('auth');
|
||||
$class::enable_plugin($auth, true);
|
||||
// add to enabled list
|
||||
if (!in_array($auth, $authsenabled)) {
|
||||
$authsenabled[] = $auth;
|
||||
$authsenabled = array_unique($authsenabled);
|
||||
set_config('auth', implode(',', $authsenabled));
|
||||
}
|
||||
\core\session\manager::gc(); // Remove stale sessions.
|
||||
core_plugin_manager::reset_caches();
|
||||
break;
|
||||
|
||||
case 'down':
|
||||
$key = array_search($auth, $authsenabled);
|
||||
// check auth plugin is valid
|
||||
if ($key === false) {
|
||||
throw new \moodle_exception('pluginnotenabled', 'auth', $returnurl, $auth);
|
||||
print_error('pluginnotenabled', 'auth', $returnurl, $auth);
|
||||
}
|
||||
// move down the list
|
||||
if ($key < (count($authsenabled) - 1)) {
|
||||
$fsave = $authsenabled[$key];
|
||||
$authsenabled[$key] = $authsenabled[$key + 1];
|
||||
$authsenabled[$key + 1] = $fsave;
|
||||
$value = implode(',', $authsenabled);
|
||||
add_to_config_log('auth', $CFG->auth, $value, 'core');
|
||||
set_config('auth', $value);
|
||||
set_config('auth', implode(',', $authsenabled));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -72,16 +84,14 @@ switch ($action) {
|
||||
$key = array_search($auth, $authsenabled);
|
||||
// check auth is valid
|
||||
if ($key === false) {
|
||||
throw new \moodle_exception('pluginnotenabled', 'auth', $returnurl, $auth);
|
||||
print_error('pluginnotenabled', 'auth', $returnurl, $auth);
|
||||
}
|
||||
// move up the list
|
||||
if ($key >= 1) {
|
||||
$fsave = $authsenabled[$key];
|
||||
$authsenabled[$key] = $authsenabled[$key - 1];
|
||||
$authsenabled[$key - 1] = $fsave;
|
||||
$value = implode(',', $authsenabled);
|
||||
add_to_config_log('auth', $CFG->auth, $value, 'core');
|
||||
set_config('auth', $value);
|
||||
set_config('auth', implode(',', $authsenabled));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -90,3 +100,5 @@ switch ($action) {
|
||||
}
|
||||
|
||||
redirect($returnurl);
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
/**
|
||||
* Edit configuration for an individual auth plugin
|
||||
*/
|
||||
|
||||
require_once '../config.php';
|
||||
require_once $CFG->libdir.'/adminlib.php';
|
||||
|
||||
$auth = required_param('auth', PARAM_PLUGIN);
|
||||
$PAGE->set_pagetype('admin-auth-' . $auth);
|
||||
|
||||
admin_externalpage_setup('authsetting'.$auth);
|
||||
|
||||
$authplugin = get_auth_plugin($auth);
|
||||
$err = array();
|
||||
|
||||
$returnurl = "$CFG->wwwroot/$CFG->admin/settings.php?section=manageauths";
|
||||
|
||||
debugging("Use of config.html files in authentication plugins have been deprecated. " .
|
||||
" Please migrate your plugin to use the admin settings API", DEBUG_DEVELOPER);
|
||||
|
||||
// save configuration changes
|
||||
if ($frm = data_submitted() and confirm_sesskey()) {
|
||||
|
||||
$authplugin->validate_form($frm, $err);
|
||||
|
||||
if (count($err) == 0) {
|
||||
|
||||
// save plugin config
|
||||
if ($authplugin->process_config($frm)) {
|
||||
|
||||
// save field lock configuration
|
||||
foreach ($frm as $name => $value) {
|
||||
if (preg_match('/^lockconfig_(.+?)$/', $name, $matches)) {
|
||||
$plugin = "auth/$auth";
|
||||
$name = $matches[1];
|
||||
set_config($name, $value, $plugin);
|
||||
}
|
||||
}
|
||||
redirect($returnurl);
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
foreach ($err as $key => $value) {
|
||||
$focus = "form.$key";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$frmlegacystyle = get_config('auth/'.$auth);
|
||||
$frmnewstyle = get_config('auth_'.$auth);
|
||||
$frm = (object)array_merge((array)$frmlegacystyle, (array)$frmnewstyle);
|
||||
}
|
||||
|
||||
$user_fields = $authplugin->userfields;
|
||||
//$user_fields = array("firstname", "lastname", "email", "phone1", "phone2", "institution", "department", "address", "city", "country", "description", "idnumber", "lang");
|
||||
|
||||
/// Get the auth title (from core or own auth lang files)
|
||||
$authtitle = $authplugin->get_title();
|
||||
/// Get the auth descriptions (from core or own auth lang files)
|
||||
$authdescription = $authplugin->get_description();
|
||||
|
||||
// output configuration form
|
||||
echo $OUTPUT->header();
|
||||
|
||||
// choose an authentication method
|
||||
echo "<form id=\"authmenu\" method=\"post\" action=\"auth_config.php\">\n";
|
||||
echo "<div>\n";
|
||||
echo "<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />\n";
|
||||
echo "<input type=\"hidden\" name=\"auth\" value=\"".$auth."\" />\n";
|
||||
|
||||
// auth plugin description
|
||||
echo $OUTPUT->box_start();
|
||||
echo $OUTPUT->heading($authtitle);
|
||||
echo $OUTPUT->box_start('informationbox');
|
||||
echo $authdescription;
|
||||
echo $OUTPUT->box_end();
|
||||
echo "<hr />\n";
|
||||
$authplugin->config_form($frm, $err, $user_fields);
|
||||
echo $OUTPUT->box_end();
|
||||
echo '<p style="text-align: center"><input type="submit" value="' . get_string("savechanges") . "\" /></p>\n";
|
||||
echo "</div>\n";
|
||||
echo "</form>\n";
|
||||
|
||||
$PAGE->requires->string_for_js('unmaskpassword', 'core_form');
|
||||
$PAGE->requires->yui_module('moodle-auth-passwordunmask', 'M.auth.passwordunmask');
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
exit;
|
||||
|
||||
/// Functions /////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* auth field locking
|
||||
* Good enough for most auth plugins
|
||||
* but some may want a custom one if they are offering
|
||||
* other options
|
||||
* Note: lockconfig_ fields have special handling.
|
||||
*
|
||||
* @param string $auth authentication plugin shortname
|
||||
* @param array $user_fields user profile fields
|
||||
* @param string $helptext help text to be displayed at top of form
|
||||
* @param boolean $retrieveopts Map fields or lock only.
|
||||
* @param boolean $updateopts Allow remote updates
|
||||
* @param array $customfields list of custom profile fields
|
||||
* @deprecated since Moodle 3.3
|
||||
*/
|
||||
function print_auth_lock_options($auth, $user_fields, $helptext, $retrieveopts, $updateopts, $customfields = array()) {
|
||||
global $DB, $OUTPUT;
|
||||
debugging("The function 'print_auth_lock_options' has been deprecated, " .
|
||||
"Please migrate your code to use the admin settings API and use the function 'display_auth_lock_options' instead. ",
|
||||
DEBUG_DEVELOPER);
|
||||
|
||||
echo '<tr><td colspan="3">';
|
||||
if ($retrieveopts) {
|
||||
echo $OUTPUT->heading(get_string('auth_data_mapping', 'auth'));
|
||||
} else {
|
||||
echo $OUTPUT->heading(get_string('auth_fieldlocks', 'auth'));
|
||||
}
|
||||
echo '</td></tr>';
|
||||
|
||||
$lockoptions = array ('unlocked' => get_string('unlocked', 'auth'),
|
||||
'unlockedifempty' => get_string('unlockedifempty', 'auth'),
|
||||
'locked' => get_string('locked', 'auth'));
|
||||
$updatelocaloptions = array('oncreate' => get_string('update_oncreate', 'auth'),
|
||||
'onlogin' => get_string('update_onlogin', 'auth'));
|
||||
$updateextoptions = array('0' => get_string('update_never', 'auth'),
|
||||
'1' => get_string('update_onupdate', 'auth'));
|
||||
|
||||
$pluginconfig = get_config("auth/$auth");
|
||||
|
||||
// Helptext is on a field with rowspan.
|
||||
if (empty($helptext)) {
|
||||
$helptext = ' ';
|
||||
}
|
||||
|
||||
// If we have custom fields then merge them with user fields.
|
||||
if (!empty($customfields)) {
|
||||
$user_fields = array_merge($user_fields, $customfields);
|
||||
}
|
||||
|
||||
if (!empty($customfields)) {
|
||||
$customfieldname = $DB->get_records('user_info_field', null, '', 'shortname, name');
|
||||
}
|
||||
foreach ($user_fields as $field) {
|
||||
// Define some vars we'll work with.
|
||||
if (!isset($pluginconfig->{"field_map_$field"})) {
|
||||
$pluginconfig->{"field_map_$field"} = '';
|
||||
}
|
||||
if (!isset($pluginconfig->{"field_updatelocal_$field"})) {
|
||||
$pluginconfig->{"field_updatelocal_$field"} = '';
|
||||
}
|
||||
if (!isset($pluginconfig->{"field_updateremote_$field"})) {
|
||||
$pluginconfig->{"field_updateremote_$field"} = '';
|
||||
}
|
||||
if (!isset($pluginconfig->{"field_lock_$field"})) {
|
||||
$pluginconfig->{"field_lock_$field"} = '';
|
||||
}
|
||||
|
||||
// Define the fieldname we display to the user.
|
||||
$fieldname = $field;
|
||||
if ($fieldname === 'lang') {
|
||||
$fieldname = get_string('language');
|
||||
} elseif (!empty($customfields) && in_array($field, $customfields)) {
|
||||
// If custom field then pick name from database.
|
||||
$fieldshortname = str_replace('profile_field_', '', $fieldname);
|
||||
$fieldname = $customfieldname[$fieldshortname]->name;
|
||||
if (core_text::strlen($fieldshortname) > 67) {
|
||||
// If custom profile field name is longer than 67 characters we will not be able to store the setting
|
||||
// such as 'field_updateremote_profile_field_NOTSOSHORTSHORTNAME' in the database because the character
|
||||
// limit for the setting name is 100.
|
||||
continue;
|
||||
}
|
||||
} elseif ($fieldname == 'url') {
|
||||
$fieldname = get_string('webpage');
|
||||
} else {
|
||||
$fieldname = get_string($fieldname);
|
||||
}
|
||||
if ($retrieveopts) {
|
||||
$varname = 'field_map_' . $field;
|
||||
|
||||
echo '<tr valign="top"><td align="right">';
|
||||
echo '<label for="lockconfig_'.$varname.'">'.$fieldname.'</label>';
|
||||
echo '</td><td>';
|
||||
|
||||
echo "<input id=\"lockconfig_{$varname}\" name=\"lockconfig_{$varname}\" type=\"text\" size=\"30\" value=\"{$pluginconfig->$varname}\" />";
|
||||
echo '<div style="text-align: right">';
|
||||
echo '<label for="menulockconfig_field_updatelocal_'.$field.'">'.get_string('auth_updatelocal', 'auth') . '</label> ';
|
||||
echo html_writer::select($updatelocaloptions, "lockconfig_field_updatelocal_{$field}", $pluginconfig->{"field_updatelocal_$field"}, false);
|
||||
echo '<br />';
|
||||
if ($updateopts) {
|
||||
echo '<label for="menulockconfig_field_updateremote_'.$field.'">'.get_string('auth_updateremote', 'auth') . '</label> ';
|
||||
echo html_writer::select($updateextoptions, "lockconfig_field_updateremote_{$field}", $pluginconfig->{"field_updateremote_$field"}, false);
|
||||
echo '<br />';
|
||||
}
|
||||
echo '<label for="menulockconfig_field_lock_'.$field.'">'.get_string('auth_fieldlock', 'auth') . '</label> ';
|
||||
echo html_writer::select($lockoptions, "lockconfig_field_lock_{$field}", $pluginconfig->{"field_lock_$field"}, false);
|
||||
echo '</div>';
|
||||
} else {
|
||||
echo '<tr valign="top"><td align="right">';
|
||||
echo '<label for="menulockconfig_field_lock_'.$field.'">'.$fieldname.'</label>';
|
||||
echo '</td><td>';
|
||||
echo html_writer::select($lockoptions, "lockconfig_field_lock_{$field}", $pluginconfig->{"field_lock_$field"}, false);
|
||||
}
|
||||
echo '</td>';
|
||||
if (!empty($helptext)) {
|
||||
echo '<td rowspan="' . count($user_fields) . '">' . $helptext . '</td>';
|
||||
$helptext = '';
|
||||
}
|
||||
echo '</tr>';
|
||||
}
|
||||
}
|
||||
+223
-79
@@ -1,94 +1,238 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Allows the admin to configure blocks (hide/show, uninstall and configure)
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
// Allows the admin to configure blocks (hide/show, uninstall and configure)
|
||||
|
||||
require_once('../config.php');
|
||||
require_once("{$CFG->libdir}/adminlib.php");
|
||||
require_once("{$CFG->libdir}/blocklib.php");
|
||||
require_once("{$CFG->libdir}/tablelib.php");
|
||||
require_once('../config.php');
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
require_once($CFG->libdir.'/blocklib.php');
|
||||
require_once($CFG->libdir.'/tablelib.php');
|
||||
|
||||
admin_externalpage_setup('manageblocks');
|
||||
admin_externalpage_setup('manageblocks');
|
||||
|
||||
$plugin = optional_param('plugin', '', PARAM_PLUGIN);
|
||||
$action = optional_param('action', '', PARAM_ALPHA);
|
||||
$unprotect = optional_param('unprotect', 0, PARAM_PLUGIN);
|
||||
$protect = optional_param('protect', 0, PARAM_PLUGIN);
|
||||
$confirm = optional_param('confirm', 0, PARAM_BOOL);
|
||||
$hide = optional_param('hide', 0, PARAM_INT);
|
||||
$show = optional_param('show', 0, PARAM_INT);
|
||||
$unprotect = optional_param('unprotect', 0, PARAM_INT);
|
||||
$protect = optional_param('protect', 0, PARAM_INT);
|
||||
|
||||
$strmanageblocks = get_string('manageblocks');
|
||||
/// Print headings
|
||||
|
||||
// If data submitted, then process and store.
|
||||
if (!empty($plugin) && !empty($action) && confirm_sesskey()) {
|
||||
$manager = \core_plugin_manager::resolve_plugininfo_class('block');
|
||||
$pluginname = get_string('pluginname', "block_{$plugin}");
|
||||
$strmanageblocks = get_string('manageblocks');
|
||||
$struninstall = get_string('uninstallplugin', 'core_admin');
|
||||
$strversion = get_string('version');
|
||||
$strhide = get_string('hide');
|
||||
$strshow = get_string('show');
|
||||
$strsettings = get_string('settings');
|
||||
$strcourses = get_string('blockinstances', 'admin');
|
||||
$strname = get_string('name');
|
||||
$strshowblockcourse = get_string('showblockcourse');
|
||||
$strprotecthdr = get_string('blockprotect', 'admin'). $OUTPUT->help_icon('blockprotect','admin');
|
||||
$strprotect = get_string('blockprotect', 'admin');
|
||||
$strunprotect = get_string('blockunprotect', 'admin');
|
||||
|
||||
if ($action === 'disable' && $manager::enable_plugin($plugin, 0)) {
|
||||
\core\notification::add(
|
||||
get_string('plugin_disabled', 'core_admin', $pluginname),
|
||||
\core\notification::SUCCESS
|
||||
);
|
||||
// Settings not required - only pages.
|
||||
admin_get_root(true, false);
|
||||
} else if ($action === 'enable' && $manager::enable_plugin($plugin, 1)) {
|
||||
\core\notification::add(
|
||||
get_string('plugin_enabled', 'core_admin', $pluginname),
|
||||
\core\notification::SUCCESS
|
||||
);
|
||||
/// If data submitted, then process and store.
|
||||
|
||||
// Settings not required - only pages.
|
||||
admin_get_root(true, false);
|
||||
if (!empty($hide) && confirm_sesskey()) {
|
||||
if (!$block = $DB->get_record('block', array('id'=>$hide))) {
|
||||
print_error('blockdoesnotexist', 'error');
|
||||
}
|
||||
$DB->set_field('block', 'visible', '0', array('id'=>$block->id)); // Hide block
|
||||
add_to_config_log('block_visibility', $block->visible, '0', $block->name);
|
||||
core_plugin_manager::reset_caches();
|
||||
admin_get_root(true, false); // settings not required - only pages
|
||||
}
|
||||
|
||||
// Redirect back to the page with out any params.
|
||||
redirect(new moodle_url('/admin/blocks.php'));
|
||||
}
|
||||
if (!empty($show) && confirm_sesskey() ) {
|
||||
if (!$block = $DB->get_record('block', array('id'=>$show))) {
|
||||
print_error('blockdoesnotexist', 'error');
|
||||
}
|
||||
$DB->set_field('block', 'visible', '1', array('id'=>$block->id)); // Show block
|
||||
add_to_config_log('block_visibility', $block->visible, '1', $block->name);
|
||||
core_plugin_manager::reset_caches();
|
||||
admin_get_root(true, false); // settings not required - only pages
|
||||
}
|
||||
|
||||
if (!empty($protect) && confirm_sesskey()) {
|
||||
block_manager::protect_block($protect);
|
||||
$pluginname = get_string('pluginname', "block_{$protect}");
|
||||
\core\notification::add(
|
||||
get_string('blockprotected', 'core_admin', $pluginname),
|
||||
\core\notification::SUCCESS
|
||||
);
|
||||
// Settings not required - only pages.
|
||||
admin_get_root(true, false);
|
||||
}
|
||||
if (!empty($protect) && confirm_sesskey()) {
|
||||
block_manager::protect_block((int)$protect);
|
||||
admin_get_root(true, false); // settings not required - only pages
|
||||
}
|
||||
|
||||
if (!empty($unprotect) && confirm_sesskey()) {
|
||||
block_manager::unprotect_block($unprotect);
|
||||
$pluginname = get_string('pluginname', "block_{$unprotect}");
|
||||
\core\notification::add(
|
||||
get_string('blockunprotected', 'core_admin', $pluginname),
|
||||
\core\notification::SUCCESS
|
||||
);
|
||||
// Settings not required - only pages.
|
||||
admin_get_root(true, false);
|
||||
}
|
||||
if (!empty($unprotect) && confirm_sesskey()) {
|
||||
block_manager::unprotect_block((int)$unprotect);
|
||||
admin_get_root(true, false); // settings not required - only pages
|
||||
}
|
||||
|
||||
$undeletableblocktypes = block_manager::get_undeletable_block_types();
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading($strmanageblocks);
|
||||
|
||||
/// Main display starts here
|
||||
|
||||
/// Get and sort the existing blocks
|
||||
|
||||
if (!$blocks = $DB->get_records('block', array(), 'name ASC')) {
|
||||
print_error('noblocks', 'error'); // Should never happen
|
||||
}
|
||||
|
||||
$incompatible = array();
|
||||
|
||||
/// Print the table of all blocks
|
||||
|
||||
$table = new flexible_table('admin-blocks-compatible');
|
||||
|
||||
$table->define_columns(array('name', 'instances', 'version', 'hideshow', 'undeletable', 'settings', 'uninstall'));
|
||||
$table->define_headers(array($strname, $strcourses, $strversion, $strhide.'/'.$strshow, $strprotecthdr, $strsettings, $struninstall));
|
||||
$table->define_baseurl($CFG->wwwroot.'/'.$CFG->admin.'/blocks.php');
|
||||
$table->set_attribute('class', 'admintable blockstable generaltable');
|
||||
$table->set_attribute('id', 'compatibleblockstable');
|
||||
$table->setup();
|
||||
$tablerows = array();
|
||||
|
||||
// Sort blocks using current locale.
|
||||
$blocknames = array();
|
||||
foreach ($blocks as $blockid=>$block) {
|
||||
$blockname = $block->name;
|
||||
if (file_exists("$CFG->dirroot/blocks/$blockname/block_$blockname.php")) {
|
||||
$blocknames[$blockid] = get_string('pluginname', 'block_'.$blockname);
|
||||
} else {
|
||||
$blocknames[$blockid] = $blockname;
|
||||
}
|
||||
}
|
||||
core_collator::asort($blocknames);
|
||||
|
||||
foreach ($blocknames as $blockid=>$strblockname) {
|
||||
$block = $blocks[$blockid];
|
||||
$blockname = $block->name;
|
||||
$dbversion = get_config('block_'.$block->name, 'version');
|
||||
|
||||
if (!file_exists("$CFG->dirroot/blocks/$blockname/block_$blockname.php")) {
|
||||
$blockobject = false;
|
||||
$strblockname = '<span class="notifyproblem">'.$strblockname.' ('.get_string('missingfromdisk').')</span>';
|
||||
$plugin = new stdClass();
|
||||
$plugin->version = $dbversion;
|
||||
|
||||
} else {
|
||||
$plugin = new stdClass();
|
||||
$plugin->version = '???';
|
||||
if (file_exists("$CFG->dirroot/blocks/$blockname/version.php")) {
|
||||
include("$CFG->dirroot/blocks/$blockname/version.php");
|
||||
}
|
||||
|
||||
if (!$blockobject = block_instance($block->name)) {
|
||||
$incompatible[] = $block;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('block_'.$blockname, 'manage')) {
|
||||
$uninstall = html_writer::link($uninstallurl, $struninstall);
|
||||
} else {
|
||||
$uninstall = '';
|
||||
}
|
||||
|
||||
$settings = ''; // By default, no configuration
|
||||
if ($blockobject and $blockobject->has_config()) {
|
||||
$blocksettings = admin_get_root()->locate('blocksetting' . $block->name);
|
||||
|
||||
if ($blocksettings instanceof admin_externalpage) {
|
||||
$settings = '<a href="' . $blocksettings->url . '">' . get_string('settings') . '</a>';
|
||||
} else if ($blocksettings instanceof admin_settingpage) {
|
||||
$settings = '<a href="'.$CFG->wwwroot.'/'.$CFG->admin.'/settings.php?section=blocksetting'.$block->name.'">'.$strsettings.'</a>';
|
||||
} else if (!file_exists($CFG->dirroot.'/blocks/'.$block->name.'/settings.php')) {
|
||||
// If the block's settings node was not found, we check that the block really provides the settings.php file.
|
||||
// Note that blocks can inject their settings to other nodes in the admin tree without using the default locations.
|
||||
// This can be done by assigning null to $setting in settings.php and it is a valid case.
|
||||
debugging('Warning: block_'.$block->name.' returns true in has_config() but does not provide a settings.php file',
|
||||
DEBUG_DEVELOPER);
|
||||
}
|
||||
}
|
||||
|
||||
// MDL-11167, blocks can be placed on mymoodle, or the blogs page
|
||||
// and it should not show up on course search page
|
||||
|
||||
$totalcount = $DB->count_records('block_instances', array('blockname'=>$blockname));
|
||||
$count = $DB->count_records('block_instances', array('blockname'=>$blockname, 'pagetypepattern'=>'course-view-*'));
|
||||
|
||||
if ($count>0) {
|
||||
$blocklist = "<a href=\"{$CFG->wwwroot}/course/search.php?blocklist=$blockid&sesskey=".sesskey()."\" ";
|
||||
$blocklist .= "title=\"$strshowblockcourse\" >$totalcount</a>";
|
||||
}
|
||||
else {
|
||||
$blocklist = "$totalcount";
|
||||
}
|
||||
$class = ''; // Nothing fancy, by default
|
||||
|
||||
if (!$blockobject) {
|
||||
// ignore
|
||||
$visible = '';
|
||||
} else if ($blocks[$blockid]->visible) {
|
||||
$visible = '<a href="blocks.php?hide='.$blockid.'&sesskey='.sesskey().'" title="'.$strhide.'">'.
|
||||
$OUTPUT->pix_icon('t/hide', $strhide) . '</a>';
|
||||
} else {
|
||||
$visible = '<a href="blocks.php?show='.$blockid.'&sesskey='.sesskey().'" title="'.$strshow.'">'.
|
||||
$OUTPUT->pix_icon('t/show', $strshow) . '</a>';
|
||||
$class = 'dimmed_text';
|
||||
}
|
||||
|
||||
if ($dbversion == $plugin->version) {
|
||||
$version = $dbversion;
|
||||
} else {
|
||||
$version = "$dbversion ($plugin->version)";
|
||||
}
|
||||
|
||||
if (!$blockobject) {
|
||||
// ignore
|
||||
$undeletable = '';
|
||||
} else if (in_array($blockname, $undeletableblocktypes)) {
|
||||
$undeletable = '<a href="blocks.php?unprotect='.$blockid.'&sesskey='.sesskey().'" title="'.$strunprotect.'">'.
|
||||
$OUTPUT->pix_icon('t/unlock', $strunprotect) . '</a>';
|
||||
} else {
|
||||
$undeletable = '<a href="blocks.php?protect='.$blockid.'&sesskey='.sesskey().'" title="'.$strprotect.'">'.
|
||||
$OUTPUT->pix_icon('t/lock', $strprotect) . '</a>';
|
||||
}
|
||||
|
||||
$row = array(
|
||||
$strblockname,
|
||||
$blocklist,
|
||||
$version,
|
||||
$visible,
|
||||
$undeletable,
|
||||
$settings,
|
||||
$uninstall,
|
||||
);
|
||||
$table->add_data($row, $class);
|
||||
}
|
||||
|
||||
$table->print_html();
|
||||
|
||||
if (!empty($incompatible)) {
|
||||
echo $OUTPUT->heading(get_string('incompatibleblocks', 'blockstable', 'admin'));
|
||||
|
||||
$table = new flexible_table('admin-blocks-incompatible');
|
||||
|
||||
$table->define_columns(array('block', 'uninstall'));
|
||||
$table->define_headers(array($strname, $struninstall));
|
||||
$table->define_baseurl($CFG->wwwroot.'/'.$CFG->admin.'/blocks.php');
|
||||
|
||||
$table->set_attribute('class', 'incompatibleblockstable generaltable');
|
||||
|
||||
$table->setup();
|
||||
|
||||
foreach ($incompatible as $block) {
|
||||
if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('block_'.$block->name, 'manage')) {
|
||||
$uninstall = html_writer::link($uninstallurl, $struninstall);
|
||||
} else {
|
||||
$uninstall = '';
|
||||
}
|
||||
$table->add_data(array(
|
||||
$block->name,
|
||||
$uninstall,
|
||||
));
|
||||
}
|
||||
$table->print_html();
|
||||
}
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading($strmanageblocks);
|
||||
echo $OUTPUT->notification(get_string('noteunneededblocks', 'admin'), 'info', false);
|
||||
|
||||
// Print the table of all blocks.
|
||||
$table = new \core_admin\table\block_management_table();
|
||||
$table->out();
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
+10
-13
@@ -41,19 +41,13 @@ $adminroot = admin_get_root(); // need all settings
|
||||
$settingspage = $adminroot->locate($category, true);
|
||||
|
||||
if (empty($settingspage) or !($settingspage instanceof admin_category)) {
|
||||
throw new \moodle_exception('categoryerror', 'error', "$CFG->wwwroot/$CFG->admin/");
|
||||
print_error('categoryerror', 'admin', "$CFG->wwwroot/$CFG->admin/");
|
||||
}
|
||||
|
||||
if (!($settingspage->check_access())) {
|
||||
throw new \moodle_exception('accessdenied', 'admin');
|
||||
print_error('accessdenied', 'admin');
|
||||
}
|
||||
|
||||
$hassiteconfig = has_capability('moodle/site:config', $PAGE->context);
|
||||
if ($hassiteconfig) {
|
||||
$PAGE->add_header_action($OUTPUT->render_from_template('core_admin/header_search_input', [
|
||||
'action' => new moodle_url('/admin/search.php'),
|
||||
]));
|
||||
}
|
||||
|
||||
$statusmsg = '';
|
||||
$errormsg = '';
|
||||
@@ -81,7 +75,7 @@ if ($PAGE->user_allowed_editing() && $adminediting != -1) {
|
||||
$USER->editing = $adminediting;
|
||||
}
|
||||
$buttons = null;
|
||||
if ($PAGE->user_allowed_editing() && !$PAGE->theme->haseditswitch) {
|
||||
if ($PAGE->user_allowed_editing()) {
|
||||
$url = clone($PAGE->url);
|
||||
if ($PAGE->user_is_editing()) {
|
||||
$caption = get_string('blockseditoff');
|
||||
@@ -146,7 +140,13 @@ if ($errormsg !== '') {
|
||||
echo $OUTPUT->notification($statusmsg, 'notifysuccess');
|
||||
}
|
||||
|
||||
echo $OUTPUT->heading(get_string('admincategory', 'admin', $settingspage->visiblename), 2);
|
||||
$path = array_reverse($settingspage->visiblepath);
|
||||
if (is_array($path)) {
|
||||
$visiblename = join(' / ', $path);
|
||||
} else {
|
||||
$visiblename = $path;
|
||||
}
|
||||
echo $OUTPUT->heading(get_string('admincategory', 'admin', $visiblename), 2);
|
||||
|
||||
echo html_writer::start_tag('form', array('action' => '', 'method' => 'post', 'id' => 'adminsettings'));
|
||||
echo html_writer::start_tag('div');
|
||||
@@ -158,7 +158,4 @@ echo $outputhtml;
|
||||
echo html_writer::end_tag('fieldset');
|
||||
echo html_writer::end_tag('form');
|
||||
|
||||
// Add the form change checker.
|
||||
$PAGE->requires->js_call_amd('core_form/changechecker', 'watchFormById', ['adminsettings']);
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
@@ -1,127 +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\admin;
|
||||
|
||||
use admin_setting;
|
||||
use core_plugin_manager;
|
||||
use core_text;
|
||||
|
||||
/**
|
||||
* Admin setting plugin manager.
|
||||
*
|
||||
* @package core_admin
|
||||
* @subpackage admin
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class admin_setting_plugin_manager extends admin_setting {
|
||||
/** @var core_plugin_manager The plugin manager instance */
|
||||
protected core_plugin_manager $pluginmanager;
|
||||
|
||||
/** @var string The plugintype that this manager covers */
|
||||
protected string $plugintype;
|
||||
|
||||
/** @var string The class of the management table to use */
|
||||
protected string $tableclass;
|
||||
|
||||
public function __construct(
|
||||
string $plugintype,
|
||||
string $tableclass,
|
||||
string $name,
|
||||
string $visiblename,
|
||||
string $description = '',
|
||||
string $defaultsetting = '',
|
||||
) {
|
||||
$this->nosave = true;
|
||||
$this->pluginmanager = core_plugin_manager::instance();
|
||||
$this->plugintype = $plugintype;
|
||||
$this->tableclass = $tableclass;
|
||||
|
||||
parent::__construct($name, $visiblename, $description, $defaultsetting);
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns true, does nothing
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
public function get_setting(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns true, does nothing
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
public function get_defaultsetting(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns '', does not write anything
|
||||
*
|
||||
* @return string Always returns ''
|
||||
*/
|
||||
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
public function write_setting($data): string {
|
||||
// Do not write any setting.
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if $query is one of the available editors
|
||||
*
|
||||
* @param string $query The string to search for
|
||||
* @return bool Returns true if found, false if not
|
||||
*/
|
||||
public function is_related($query) {
|
||||
if (parent::is_related($query)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$plugins = $this->pluginmanager->get_installed_plugins($this->plugintype);
|
||||
foreach (array_keys($plugins) as $plugin) {
|
||||
$plugin = "{$this->plugintype}_{$plugin}";
|
||||
if (str_contains($plugin, $query)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$pluginname = get_string('pluginname', $plugin);
|
||||
if (strpos(core_text::strtolower($pluginname), $query) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the XHTML to display the control
|
||||
*
|
||||
* @param string $data Unused
|
||||
* @param string $query
|
||||
* @return string
|
||||
*/
|
||||
public function output_html($data, $query = ''): string {
|
||||
$table = new $this->tableclass();
|
||||
if (!($table instanceof \core_admin\table\plugin_management_table)) {
|
||||
throw new \coding_exception("{$this->tableclass} must be an instance of \\core_admin\\table\\plugin_management_table");
|
||||
}
|
||||
return highlight($query, $table->get_content());
|
||||
}
|
||||
}
|
||||
-97
@@ -1,97 +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\external;
|
||||
|
||||
use block_manager;
|
||||
use core_external\external_api;
|
||||
use core_external\external_function_parameters;
|
||||
use core_external\external_single_structure;
|
||||
use core_external\external_value;
|
||||
|
||||
/**
|
||||
* Web Service to control the state of a plugin.
|
||||
*
|
||||
* @package core_admin
|
||||
* @category external
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class set_block_protection extends external_api {
|
||||
/**
|
||||
* Returns description of method parameters
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): external_function_parameters {
|
||||
return new external_function_parameters([
|
||||
'plugin' => new external_value(PARAM_PLUGIN, 'The name of the plugin', VALUE_REQUIRED),
|
||||
'state' => new external_value(PARAM_INT, 'The target state', VALUE_REQUIRED),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the block protection state.
|
||||
*
|
||||
* @param string $plugin The name of the plugin
|
||||
* @param int $state The target state
|
||||
* @return null
|
||||
*/
|
||||
public static function execute(
|
||||
string $plugin,
|
||||
int $state,
|
||||
): array {
|
||||
[
|
||||
'plugin' => $plugin,
|
||||
'state' => $state,
|
||||
] = self::validate_parameters(self::execute_parameters(), [
|
||||
'plugin' => $plugin,
|
||||
'state' => $state,
|
||||
]);
|
||||
|
||||
$context = \context_system::instance();
|
||||
self::validate_context($context);
|
||||
require_capability('moodle/site:config', $context);
|
||||
|
||||
[, $pluginname] = explode('_', \core_component::normalize_componentname($plugin), 2);
|
||||
$displayname = get_string('pluginname', $plugin);
|
||||
|
||||
if ($state) {
|
||||
block_manager::protect_block($pluginname);
|
||||
\core\notification::add(
|
||||
get_string('blockprotected', 'core_admin', $displayname),
|
||||
\core\notification::SUCCESS
|
||||
);
|
||||
} else {
|
||||
block_manager::unprotect_block($pluginname);
|
||||
\core\notification::add(
|
||||
get_string('blockunprotected', 'core_admin', $displayname),
|
||||
\core\notification::SUCCESS
|
||||
);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the return structure of the external service.
|
||||
*
|
||||
* @return external_single_structure
|
||||
*/
|
||||
public static function execute_returns(): external_single_structure {
|
||||
return new external_single_structure([]);
|
||||
}
|
||||
}
|
||||
-84
@@ -1,84 +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\external;
|
||||
|
||||
use core_external\external_api;
|
||||
use core_external\external_function_parameters;
|
||||
use core_external\external_single_structure;
|
||||
use core_external\external_value;
|
||||
|
||||
/**
|
||||
* Web Service to control the order of a plugin.
|
||||
*
|
||||
* @package core_admin
|
||||
* @category external
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class set_plugin_order extends external_api {
|
||||
/**
|
||||
* Returns description of method parameters
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): external_function_parameters {
|
||||
return new external_function_parameters([
|
||||
'plugin' => new external_value(PARAM_PLUGIN, 'The name of the plugin', VALUE_REQUIRED),
|
||||
'direction' => new external_value(PARAM_INT, 'The direction to move', VALUE_REQUIRED),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the plugin state.
|
||||
*
|
||||
* @param string $plugin The name of the plugin
|
||||
* @param int $direction The direction to move the plugin
|
||||
* @return array
|
||||
*/
|
||||
public static function execute(
|
||||
string $plugin,
|
||||
int $direction,
|
||||
): array {
|
||||
[
|
||||
'plugin' => $plugin,
|
||||
'direction' => $direction,
|
||||
] = self::validate_parameters(self::execute_parameters(), [
|
||||
'plugin' => $plugin,
|
||||
'direction' => $direction,
|
||||
]);
|
||||
|
||||
$context = \context_system::instance();
|
||||
self::validate_context($context);
|
||||
require_capability('moodle/site:config', $context);
|
||||
|
||||
[$plugintype, $pluginname] = explode('_', \core_component::normalize_componentname($plugin), 2);
|
||||
|
||||
$manager = \core_plugin_manager::resolve_plugininfo_class($plugintype);
|
||||
$manager::change_plugin_order($pluginname, $direction);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the return structure of the external service.
|
||||
*
|
||||
* @return external_single_structure
|
||||
*/
|
||||
public static function execute_returns(): external_single_structure {
|
||||
return new external_single_structure([]);
|
||||
}
|
||||
}
|
||||
-98
@@ -1,98 +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\external;
|
||||
|
||||
use core_external\external_api;
|
||||
use core_external\external_function_parameters;
|
||||
use core_external\external_single_structure;
|
||||
use core_external\external_value;
|
||||
|
||||
/**
|
||||
* Web Service to control the state of a plugin.
|
||||
*
|
||||
* @package core_admin
|
||||
* @category external
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class set_plugin_state extends external_api {
|
||||
/**
|
||||
* Returns description of method parameters
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): external_function_parameters {
|
||||
return new external_function_parameters([
|
||||
'plugin' => new external_value(PARAM_PLUGIN, 'The name of the plugin', VALUE_REQUIRED),
|
||||
'state' => new external_value(PARAM_INT, 'The target state', VALUE_REQUIRED),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the plugin state.
|
||||
*
|
||||
* @param string $plugin The name of the plugin
|
||||
* @param int $state The target state
|
||||
* @return null
|
||||
*/
|
||||
public static function execute(
|
||||
string $plugin,
|
||||
int $state,
|
||||
): array {
|
||||
[
|
||||
'plugin' => $plugin,
|
||||
'state' => $state,
|
||||
] = self::validate_parameters(self::execute_parameters(), [
|
||||
'plugin' => $plugin,
|
||||
'state' => $state,
|
||||
]);
|
||||
|
||||
$context = \context_system::instance();
|
||||
self::validate_context($context);
|
||||
require_capability('moodle/site:config', $context);
|
||||
|
||||
[$plugintype, $pluginname] = explode('_', \core_component::normalize_componentname($plugin), 2);
|
||||
|
||||
$manager = \core_plugin_manager::resolve_plugininfo_class($plugintype);
|
||||
$displayname = get_string('pluginname', $plugin);
|
||||
|
||||
if ($manager::enable_plugin($pluginname, $state)) {
|
||||
if (!empty($state)) {
|
||||
\core\notification::add(
|
||||
get_string('plugin_enabled', 'core_admin', $displayname),
|
||||
\core\notification::SUCCESS
|
||||
);
|
||||
} else {
|
||||
\core\notification::add(
|
||||
get_string('plugin_disabled', 'core_admin', $displayname),
|
||||
\core\notification::SUCCESS
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the return structure of the external service.
|
||||
*
|
||||
* @return external_single_structure
|
||||
*/
|
||||
public static function execute_returns(): external_single_structure {
|
||||
return new external_single_structure([]);
|
||||
}
|
||||
}
|
||||
@@ -43,22 +43,11 @@ class testoutgoingmailconf_form extends \moodleform {
|
||||
$mform = $this->_form;
|
||||
|
||||
// Recipient.
|
||||
$options = ['maxlength' => '100', 'size' => '25', 'autocomplete' => 'email'];
|
||||
$options = ['maxlength' => '100', 'size' => '25'];
|
||||
$mform->addElement('text', 'recipient', get_string('testoutgoingmailconf_toemail', 'admin'), $options);
|
||||
$mform->setType('recipient', PARAM_EMAIL);
|
||||
$mform->addRule('recipient', get_string('required'), 'required');
|
||||
|
||||
// From user.
|
||||
$options = ['maxlength' => '100', 'size' => '25'];
|
||||
$mform->addElement('text', 'from', get_string('testoutgoingmailconf_fromemail', 'admin'), $options);
|
||||
$mform->setType('from', PARAM_TEXT);
|
||||
$mform->addHelpButton('from', 'testoutgoingmailconf_fromemail', 'admin');
|
||||
|
||||
// Additional subject text.
|
||||
$options = ['size' => '25'];
|
||||
$mform->addElement('text', 'additionalsubject', get_string('testoutgoingmailconf_subjectadditional', 'admin'), $options);
|
||||
$mform->setType('additionalsubject', PARAM_TEXT);
|
||||
|
||||
$buttonarray = array();
|
||||
$buttonarray[] = $mform->createElement('submit', 'send', get_string('testoutgoingmailconf_sendtest', 'admin'));
|
||||
$buttonarray[] = $mform->createElement('cancel');
|
||||
@@ -67,26 +56,4 @@ class testoutgoingmailconf_form extends \moodleform {
|
||||
$mform->closeHeaderBefore('buttonar');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Form field, should be a valid email format or a username that matches with a Moodle user.
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $files
|
||||
* @return array
|
||||
* @throws \dml_exception|\coding_exception
|
||||
*/
|
||||
public function validation($data, $files): array {
|
||||
$errors = parent::validation($data, $files);
|
||||
|
||||
if (isset($data['from']) && $data['from']) {
|
||||
$userfrom = \core_user::get_user_by_username($data['from']);
|
||||
|
||||
if (!$userfrom && !validate_email($data['from'])) {
|
||||
$errors['from'] = get_string('testoutgoingmailconf_fromemail_invalid', 'admin');
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,71 +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/>.
|
||||
|
||||
/**
|
||||
* External admin page class that allows a callback to be provided to determine whether page can be accessed
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2019 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_admin\local\externalpage;
|
||||
|
||||
use admin_externalpage;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once("{$CFG->libdir}/adminlib.php");
|
||||
|
||||
/**
|
||||
* Admin externalpage class
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2019 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class accesscallback extends admin_externalpage {
|
||||
|
||||
/** @var callable $accesscheckcallback */
|
||||
protected $accesscheckcallback;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $visiblename
|
||||
* @param string $url
|
||||
* @param callable $accesscheckcallback The callback method that will be executed to check whether user has access to
|
||||
* this page. The setting instance ($this) is passed as an argument to the callback. Should return boolean value
|
||||
* @param bool $hidden
|
||||
*/
|
||||
public function __construct(string $name, string $visiblename, string $url, callable $accesscheckcallback,
|
||||
bool $hidden = false) {
|
||||
|
||||
$this->accesscheckcallback = $accesscheckcallback;
|
||||
|
||||
parent::__construct($name, $visiblename, $url, [], $hidden);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current user has access to this external page based on access callback
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function check_access() {
|
||||
return ($this->accesscheckcallback)($this);
|
||||
}
|
||||
}
|
||||
@@ -1,206 +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/>.
|
||||
|
||||
/**
|
||||
* Auto complete admin setting.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2020 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_admin\local\settings;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/adminlib.php');
|
||||
/**
|
||||
* Auto complete setting class.
|
||||
*
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @copyright 2020 The Open University
|
||||
*/
|
||||
class autocomplete extends \admin_setting_configmultiselect {
|
||||
/** @var boolean Should we allow typing new entries to the field? */
|
||||
protected $tags = false;
|
||||
/** @var string Name of an AMD module to send/process ajax requests. */
|
||||
protected $ajax = '';
|
||||
/** @var string Placeholder text for an empty list. */
|
||||
protected $placeholder = '';
|
||||
/** @var bool Whether the search has to be case-sensitive. */
|
||||
protected $casesensitive = false;
|
||||
/** @var bool Show suggestions by default - but this can be turned off. */
|
||||
protected $showsuggestions = true;
|
||||
/** @var string String that is shown when there are no selections. */
|
||||
protected $noselectionstring = '';
|
||||
/** @var string Delimiter to store values in database. */
|
||||
protected $delimiter = ',';
|
||||
/** @var string Should be multiple choices? */
|
||||
protected $multiple = true;
|
||||
/** @var string The link to manage choices. */
|
||||
protected $manageurl = true;
|
||||
/** @var string The text to display in manage link. */
|
||||
protected $managetext = true;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
|
||||
* for ones in config_plugins.
|
||||
* @param string $visiblename localised
|
||||
* @param string $description long localised info
|
||||
* @param array $defaultsetting array of selected items
|
||||
* @param array $choices options for autocomplete field
|
||||
* @param array $attributes settings for autocomplete field
|
||||
*/
|
||||
public function __construct($name, $visiblename, $description, $defaultsetting, $choices, $attributes = null) {
|
||||
|
||||
if ($attributes === null) {
|
||||
$attributes = [];
|
||||
}
|
||||
|
||||
$this->placeholder = get_string('search');
|
||||
$this->noselectionstring = get_string('noselection', 'form');
|
||||
$defaultattributes = [
|
||||
'tags',
|
||||
'showsuggestions',
|
||||
'placeholder',
|
||||
'noselectionstring',
|
||||
'ajax',
|
||||
'casesensitive',
|
||||
'delimiter',
|
||||
'multiple',
|
||||
'manageurl',
|
||||
'managetext'
|
||||
];
|
||||
|
||||
foreach ($defaultattributes as $attributename) {
|
||||
if (isset($attributes[$attributename])) {
|
||||
$this->$attributename = $attributes[$attributename];
|
||||
}
|
||||
}
|
||||
|
||||
parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the select setting(s)
|
||||
*
|
||||
* @return mixed null or array. Null if no settings else array of setting(s)
|
||||
*/
|
||||
public function get_setting() {
|
||||
$result = $this->config_read($this->name);
|
||||
if (is_null($result)) {
|
||||
return null;
|
||||
}
|
||||
if ($result === '') {
|
||||
return [];
|
||||
}
|
||||
return explode($this->delimiter, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves setting(s) provided through $data
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function write_setting($data) {
|
||||
if (!is_array($data)) {
|
||||
return ''; // Ignore it.
|
||||
}
|
||||
if (!$this->load_choices() || empty($this->choices)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
unset($data['xxxxx']);
|
||||
|
||||
$save = [];
|
||||
foreach ($data as $value) {
|
||||
if (!array_key_exists($value, $this->choices)) {
|
||||
continue; // Ignore it.
|
||||
}
|
||||
$save[] = $value;
|
||||
}
|
||||
|
||||
return ($this->config_write($this->name, implode($this->delimiter, $save)) ? '' : get_string('errorsetting', 'admin'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns XHTML autocomplete field
|
||||
*
|
||||
* @param array $data Array of values to select by default
|
||||
* @param string $query
|
||||
* @return string XHTML autocomplete field
|
||||
*/
|
||||
public function output_html($data, $query = '') {
|
||||
global $OUTPUT;
|
||||
|
||||
if (!$this->load_choices() or empty($this->choices)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$default = $this->get_defaultsetting();
|
||||
if (empty($default)) {
|
||||
$default = [];
|
||||
}
|
||||
|
||||
if (is_null($data)) {
|
||||
$data = [];
|
||||
}
|
||||
|
||||
$context = [
|
||||
'id' => $this->get_id(),
|
||||
'name' => $this->get_full_name()
|
||||
];
|
||||
|
||||
$defaults = [];
|
||||
$options = [];
|
||||
$template = 'core_admin/local/settings/autocomplete';
|
||||
|
||||
foreach ($this->choices as $value => $name) {
|
||||
if (in_array($value, $default)) {
|
||||
$defaults[] = $name;
|
||||
}
|
||||
$options[] = [
|
||||
'value' => $value,
|
||||
'text' => $name,
|
||||
'selected' => in_array($value, $data),
|
||||
'disabled' => false
|
||||
];
|
||||
}
|
||||
|
||||
$context['options'] = $options;
|
||||
$context['tags'] = $this->tags;
|
||||
$context['placeholder'] = $this->placeholder;
|
||||
$context['casesensitive'] = $this->casesensitive;
|
||||
$context['multiple'] = $this->multiple;
|
||||
$context['showsuggestions'] = $this->showsuggestions;
|
||||
$context['manageurl'] = $this->manageurl;
|
||||
$context['managetext'] = $this->managetext;
|
||||
|
||||
if (is_null($default)) {
|
||||
$defaultinfo = null;
|
||||
} if (!empty($defaults)) {
|
||||
$defaultinfo = implode(', ', $defaults);
|
||||
} else {
|
||||
$defaultinfo = get_string('none');
|
||||
}
|
||||
|
||||
$element = $OUTPUT->render_from_template($template, $context);
|
||||
|
||||
return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ class filesize extends \admin_setting {
|
||||
if (empty($bytes)) {
|
||||
return get_string('none');
|
||||
}
|
||||
return display_size($bytes, 0);
|
||||
return display_size($bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +126,6 @@ class filesize extends \admin_setting {
|
||||
if (is_null($bytes)) {
|
||||
return null;
|
||||
}
|
||||
$bytes = intval($bytes);
|
||||
|
||||
return self::parse_bytes($bytes);
|
||||
}
|
||||
@@ -146,10 +145,9 @@ class filesize extends \admin_setting {
|
||||
return get_string('errorsetting', 'admin');
|
||||
}
|
||||
|
||||
// Calculate size in bytes, ensuring we don't overflow PHP_INT_MAX.
|
||||
$bytes = $data['v'] * $data['u'];
|
||||
$result = (is_int($bytes) && $this->config_write($this->name, $bytes));
|
||||
|
||||
$result = $this->config_write($this->name, $bytes);
|
||||
return ($result ? '' : get_string('errorsetting', 'admin'));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,37 +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/>.
|
||||
|
||||
/**
|
||||
* A settings page which can be viewed with a link directly.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2021 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_admin\local\settings;
|
||||
|
||||
use moodle_url;
|
||||
|
||||
interface linkable_settings_page {
|
||||
|
||||
/**
|
||||
* Get the URL to view this settings page.
|
||||
*
|
||||
* @return moodle_url
|
||||
*/
|
||||
public function get_settings_page_url(): moodle_url;
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Payment gateway admin setting.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_admin\local\settings;
|
||||
|
||||
/**
|
||||
* Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
|
||||
*/
|
||||
class manage_payment_gateway_plugins extends \admin_setting_manage_plugins {
|
||||
/**
|
||||
* Get the admin settings section title (use get_string).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_section_title() {
|
||||
return get_string('type_paygw_plural', 'plugin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of plugin to manage.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_plugin_type() {
|
||||
return 'paygw';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the second column.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_info_column_name() {
|
||||
return get_string('supportedcurrencies', 'core_payment');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of plugin to manage.
|
||||
*
|
||||
* @param plugininfo The plugin info class.
|
||||
* @return string
|
||||
*/
|
||||
public function get_info_column($plugininfo) {
|
||||
$codes = $plugininfo->get_supported_currencies();
|
||||
|
||||
$currencies = [];
|
||||
foreach ($codes as $c) {
|
||||
$currencies[$c] = new \lang_string($c, 'core_currencies');
|
||||
}
|
||||
|
||||
return implode(get_string('listsep', 'langconfig') . ' ', $currencies);
|
||||
}
|
||||
}
|
||||
@@ -1,121 +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/>.
|
||||
|
||||
/**
|
||||
* Admin setting to show current scheduled task's status.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2021 Universitat Rovira i Virgili
|
||||
* @author Jordi Pujol-Ahulló <jpahullo@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core_admin\local\settings;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/adminlib.php');
|
||||
require_once($CFG->libdir . '/moodlelib.php');
|
||||
|
||||
use admin_setting_description;
|
||||
use core\task\manager;
|
||||
use core\task\scheduled_task;
|
||||
use html_writer;
|
||||
use lang_string;
|
||||
use moodle_url;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* This admin setting tells whether a given scheduled task is enabled, providing a link to its configuration page.
|
||||
*
|
||||
* The goal of this setting is to help contextualizing the configuration settings with related scheduled task status,
|
||||
* providing the big picture of that part of the system.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2021 Universitat Rovira i Virgili
|
||||
* @author Jordi Pujol-Ahulló <jpahullo@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class setting_scheduled_task_status extends admin_setting_description {
|
||||
/**
|
||||
* @var string fully qualified class name of a scheduled task.
|
||||
*/
|
||||
private $classname;
|
||||
/**
|
||||
* @var string additional text to append to the description.
|
||||
*/
|
||||
private $extradescription;
|
||||
|
||||
/**
|
||||
* setting_scheduled_task_status constructor.
|
||||
* @param string $name unique setting name.
|
||||
* @param string $scheduledtaskclassname full classpath class name of the scheduled task.
|
||||
* @param string $extradescription extra detail to append to the scheduled task status to add context in the setting
|
||||
* page.
|
||||
*/
|
||||
public function __construct(string $name, string $scheduledtaskclassname, string $extradescription = '') {
|
||||
$visiblename = new lang_string('task_status', 'admin');
|
||||
$this->classname = $scheduledtaskclassname;
|
||||
$this->extradescription = $extradescription;
|
||||
|
||||
parent::__construct($name, $visiblename, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates lazily the content of the description.
|
||||
* @param mixed $data nothing expected in this case.
|
||||
* @param string $query nothing expected in this case.
|
||||
* @return string the HTML content to print for this setting.
|
||||
*/
|
||||
public function output_html($data, $query = ''): string {
|
||||
if (empty($this->description)) {
|
||||
$this->description = $this->get_task_description();
|
||||
}
|
||||
|
||||
return parent::output_html($data, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTML to print as the description.
|
||||
* @return string description to be printed.
|
||||
*/
|
||||
private function get_task_description(): string {
|
||||
$task = manager::get_scheduled_task($this->classname);
|
||||
if ($task->is_enabled()) {
|
||||
$taskenabled = get_string('enabled', 'admin');
|
||||
} else {
|
||||
$taskenabled = get_string('disabled', 'admin');
|
||||
}
|
||||
$taskenabled = strtolower($taskenabled);
|
||||
$gotourl = new moodle_url(
|
||||
'/admin/tool/task/scheduledtasks.php',
|
||||
[],
|
||||
scheduled_task::get_html_id($this->classname)
|
||||
);
|
||||
if (!empty($this->extradescription)) {
|
||||
$this->extradescription = '<br />' . $this->extradescription;
|
||||
}
|
||||
|
||||
$taskdetail = new stdClass();
|
||||
$taskdetail->class = $this->classname;
|
||||
$taskdetail->name = $task->get_name();
|
||||
$taskdetail->status = $taskenabled;
|
||||
$taskdetail->gotourl = $gotourl->out(false);
|
||||
$taskdetail->extradescription = $this->extradescription;
|
||||
|
||||
return html_writer::tag('p', get_string('task_status_desc', 'admin', $taskdetail));
|
||||
}
|
||||
}
|
||||
@@ -1,117 +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/>.
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace core_admin\reportbuilder\datasource;
|
||||
|
||||
use core_admin\reportbuilder\local\entities\task_log;
|
||||
use core_reportbuilder\datasource;
|
||||
use core_reportbuilder\local\entities\user;
|
||||
use core_reportbuilder\local\filters\select;
|
||||
|
||||
/**
|
||||
* Task logs datasource
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2022 Paul Holden <paulh@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class task_logs extends datasource {
|
||||
|
||||
/**
|
||||
* Return user friendly name of the report source
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name(): string {
|
||||
return get_string('tasklogs', 'core_admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise report
|
||||
*/
|
||||
protected function initialise(): void {
|
||||
$tasklogentity = new task_log();
|
||||
|
||||
$tasklogalias = $tasklogentity->get_table_alias('task_log');
|
||||
$this->set_main_table('task_log', $tasklogalias);
|
||||
|
||||
$this->add_entity($tasklogentity);
|
||||
|
||||
// Join the user entity to represent the associated user.
|
||||
$userentity = new user();
|
||||
$useralias = $userentity->get_table_alias('user');
|
||||
$this->add_entity($userentity->add_join("
|
||||
LEFT JOIN {user} {$useralias}
|
||||
ON {$useralias}.id = {$tasklogalias}.userid")
|
||||
);
|
||||
|
||||
// Add report elements from each of the entities we added to the report.
|
||||
$this->add_all_from_entities();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the columns that will be added to the report upon creation
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_default_columns(): array {
|
||||
return [
|
||||
'task_log:name',
|
||||
'task_log:starttime',
|
||||
'task_log:duration',
|
||||
'task_log:result',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the filters that will be added to the report upon creation
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_default_filters(): array {
|
||||
return [
|
||||
'task_log:timestart',
|
||||
'task_log:result',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the conditions that will be added to the report upon creation
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_default_conditions(): array {
|
||||
return [
|
||||
'task_log:type',
|
||||
'task_log:timestart',
|
||||
'task_log:result',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the condition values that will be set for the report upon creation
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_default_condition_values(): array {
|
||||
return [
|
||||
'task_log:type_operator' => select::EQUAL_TO,
|
||||
'task_log:type_value' => \core\task\database_logger::TYPE_SCHEDULED,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,427 +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\reportbuilder\local\entities;
|
||||
|
||||
use core_reportbuilder\local\filters\date;
|
||||
use core_reportbuilder\local\filters\duration;
|
||||
use core_reportbuilder\local\filters\number;
|
||||
use core_reportbuilder\local\filters\select;
|
||||
use core_reportbuilder\local\filters\text;
|
||||
use core_reportbuilder\local\filters\autocomplete;
|
||||
use core_reportbuilder\local\helpers\format;
|
||||
use lang_string;
|
||||
use core_reportbuilder\local\entities\base;
|
||||
use core_reportbuilder\local\report\column;
|
||||
use core_reportbuilder\local\report\filter;
|
||||
use stdClass;
|
||||
use core_collator;
|
||||
|
||||
/**
|
||||
* Task log entity class implementation
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2021 David Matamoros <davidmc@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class task_log extends base {
|
||||
|
||||
/** @var int Result success */
|
||||
protected const SUCCESS = 0;
|
||||
|
||||
/** @var int Result failed */
|
||||
protected const FAILED = 1;
|
||||
|
||||
/**
|
||||
* Database tables that this entity uses and their default aliases
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_default_table_aliases(): array {
|
||||
return ['task_log' => 'tl'];
|
||||
}
|
||||
|
||||
/**
|
||||
* The default title for this entity in the list of columns/conditions/filters in the report builder
|
||||
*
|
||||
* @return lang_string
|
||||
*/
|
||||
protected function get_default_entity_title(): lang_string {
|
||||
return new lang_string('entitytasklog', 'admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the entity
|
||||
*
|
||||
* @return base
|
||||
*/
|
||||
public function initialise(): base {
|
||||
$columns = $this->get_all_columns();
|
||||
foreach ($columns as $column) {
|
||||
$this->add_column($column);
|
||||
}
|
||||
|
||||
// All the filters defined by the entity can also be used as conditions.
|
||||
$filters = $this->get_all_filters();
|
||||
foreach ($filters as $filter) {
|
||||
$this
|
||||
->add_filter($filter)
|
||||
->add_condition($filter);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all available columns
|
||||
*
|
||||
* @return column[]
|
||||
*/
|
||||
protected function get_all_columns(): array {
|
||||
global $DB;
|
||||
|
||||
$tablealias = $this->get_table_alias('task_log');
|
||||
|
||||
// Name column.
|
||||
$columns[] = (new column(
|
||||
'name',
|
||||
new lang_string('name'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_field("$tablealias.classname")
|
||||
->set_is_sortable(true)
|
||||
->add_callback(static function(string $classname): string {
|
||||
$output = '';
|
||||
if (class_exists($classname)) {
|
||||
$task = new $classname;
|
||||
if ($task instanceof \core\task\task_base) {
|
||||
$output = $task->get_name();
|
||||
}
|
||||
}
|
||||
$output .= \html_writer::tag('div', "\\{$classname}", [
|
||||
'class' => 'small text-muted',
|
||||
]);
|
||||
return $output;
|
||||
});
|
||||
|
||||
// Component column.
|
||||
$columns[] = (new column(
|
||||
'component',
|
||||
new lang_string('plugin'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_field("{$tablealias}.component")
|
||||
->set_is_sortable(true);
|
||||
|
||||
// Type column.
|
||||
$columns[] = (new column(
|
||||
'type',
|
||||
new lang_string('tasktype', 'admin'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_field("{$tablealias}.type")
|
||||
->set_is_sortable(true)
|
||||
->add_callback(static function($value): string {
|
||||
if (\core\task\database_logger::TYPE_SCHEDULED === (int) $value) {
|
||||
return get_string('task_type:scheduled', 'admin');
|
||||
}
|
||||
return get_string('task_type:adhoc', 'admin');
|
||||
});
|
||||
|
||||
// Start time column.
|
||||
$columns[] = (new column(
|
||||
'starttime',
|
||||
new lang_string('task_starttime', 'admin'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TIMESTAMP)
|
||||
->add_field("{$tablealias}.timestart")
|
||||
->set_is_sortable(true)
|
||||
->add_callback([format::class, 'userdate'], get_string('strftimedatetimeshortaccurate', 'core_langconfig'));
|
||||
|
||||
// End time column.
|
||||
$columns[] = (new column(
|
||||
'endtime',
|
||||
new lang_string('task_endtime', 'admin'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TIMESTAMP)
|
||||
->add_field("{$tablealias}.timeend")
|
||||
->set_is_sortable(true)
|
||||
->add_callback([format::class, 'userdate'], get_string('strftimedatetimeshortaccurate', 'core_langconfig'));
|
||||
|
||||
// Duration column.
|
||||
$columns[] = (new column(
|
||||
'duration',
|
||||
new lang_string('task_duration', 'admin'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_FLOAT)
|
||||
->add_field("{$tablealias}.timeend - {$tablealias}.timestart", 'duration')
|
||||
->set_is_sortable(true)
|
||||
->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(
|
||||
'hostname',
|
||||
new lang_string('hostname', 'admin'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_TEXT)
|
||||
->add_field("$tablealias.hostname")
|
||||
->set_is_sortable(true);
|
||||
|
||||
// PID column.
|
||||
$columns[] = (new column(
|
||||
'pid',
|
||||
new lang_string('pid', 'admin'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_INTEGER)
|
||||
->add_field("{$tablealias}.pid")
|
||||
->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(
|
||||
'database',
|
||||
new lang_string('task_dbstats', 'admin'),
|
||||
$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(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(
|
||||
'dbreads',
|
||||
new lang_string('task_dbreads', 'admin'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_INTEGER)
|
||||
->add_fields("{$tablealias}.dbreads")
|
||||
->set_is_sortable(true);
|
||||
|
||||
// Database writes column.
|
||||
$columns[] = (new column(
|
||||
'dbwrites',
|
||||
new lang_string('task_dbwrites', 'admin'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_INTEGER)
|
||||
->add_fields("{$tablealias}.dbwrites")
|
||||
->set_is_sortable(true);
|
||||
|
||||
// Result column.
|
||||
$columns[] = (new column(
|
||||
'result',
|
||||
new lang_string('task_result', 'admin'),
|
||||
$this->get_entity_name()
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_type(column::TYPE_BOOLEAN)
|
||||
// For accurate aggregation, we need to return boolean success = true by xor'ing the field value.
|
||||
->add_field($DB->sql_bitxor("{$tablealias}.result", 1), 'success')
|
||||
->set_is_sortable(true)
|
||||
->add_callback(static function(bool $success): string {
|
||||
if (!$success) {
|
||||
return get_string('task_result:failed', 'admin');
|
||||
}
|
||||
return get_string('success');
|
||||
});
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of all available filters
|
||||
*
|
||||
* @return filter[]
|
||||
*/
|
||||
protected function get_all_filters(): array {
|
||||
global $DB;
|
||||
|
||||
$tablealias = $this->get_table_alias('task_log');
|
||||
|
||||
// Name filter (Filter by classname).
|
||||
$filters[] = (new filter(
|
||||
autocomplete::class,
|
||||
'name',
|
||||
new lang_string('classname', 'tool_task'),
|
||||
$this->get_entity_name(),
|
||||
"{$tablealias}.classname"
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_options_callback(static function(): array {
|
||||
global $DB;
|
||||
$classnames = $DB->get_fieldset_sql('SELECT DISTINCT classname FROM {task_log} ORDER BY classname ASC');
|
||||
|
||||
$options = [];
|
||||
foreach ($classnames as $classname) {
|
||||
if (class_exists($classname)) {
|
||||
$task = new $classname;
|
||||
$options[$classname] = $task->get_name();
|
||||
}
|
||||
}
|
||||
|
||||
core_collator::asort($options);
|
||||
return $options;
|
||||
});
|
||||
|
||||
// Component filter.
|
||||
$filters[] = (new filter(
|
||||
text::class,
|
||||
'component',
|
||||
new lang_string('plugin'),
|
||||
$this->get_entity_name(),
|
||||
"{$tablealias}.component"
|
||||
))
|
||||
->add_joins($this->get_joins());
|
||||
|
||||
// Type filter.
|
||||
$filters[] = (new filter(
|
||||
select::class,
|
||||
'type',
|
||||
new lang_string('tasktype', 'admin'),
|
||||
$this->get_entity_name(),
|
||||
"{$tablealias}.type"
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_options([
|
||||
\core\task\database_logger::TYPE_ADHOC => new lang_string('task_type:adhoc', 'admin'),
|
||||
\core\task\database_logger::TYPE_SCHEDULED => new lang_string('task_type:scheduled', 'admin'),
|
||||
]);
|
||||
|
||||
// Output filter (Filter by task output).
|
||||
$filters[] = (new filter(
|
||||
text::class,
|
||||
'output',
|
||||
new lang_string('task_logoutput', 'admin'),
|
||||
$this->get_entity_name(),
|
||||
$DB->sql_cast_to_char("{$tablealias}.output")
|
||||
))
|
||||
->add_joins($this->get_joins());
|
||||
|
||||
// Start time filter.
|
||||
$filters[] = (new filter(
|
||||
date::class,
|
||||
'timestart',
|
||||
new lang_string('task_starttime', 'admin'),
|
||||
$this->get_entity_name(),
|
||||
"{$tablealias}.timestart"
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_limited_operators([
|
||||
date::DATE_ANY,
|
||||
date::DATE_RANGE,
|
||||
date::DATE_PREVIOUS,
|
||||
date::DATE_CURRENT,
|
||||
]);
|
||||
|
||||
// End time.
|
||||
$filters[] = (new filter(
|
||||
date::class,
|
||||
'timeend',
|
||||
new lang_string('task_endtime', 'admin'),
|
||||
$this->get_entity_name(),
|
||||
"{$tablealias}.timeend"
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_limited_operators([
|
||||
date::DATE_ANY,
|
||||
date::DATE_RANGE,
|
||||
date::DATE_PREVIOUS,
|
||||
date::DATE_CURRENT,
|
||||
]);
|
||||
|
||||
// Duration filter.
|
||||
$filters[] = (new filter(
|
||||
duration::class,
|
||||
'duration',
|
||||
new lang_string('task_duration', 'admin'),
|
||||
$this->get_entity_name(),
|
||||
"{$tablealias}.timeend - {$tablealias}.timestart"
|
||||
))
|
||||
->add_joins($this->get_joins());
|
||||
|
||||
// Database reads.
|
||||
$filters[] = (new filter(
|
||||
number::class,
|
||||
'dbreads',
|
||||
new lang_string('task_dbreads', 'admin'),
|
||||
$this->get_entity_name(),
|
||||
"{$tablealias}.dbreads"
|
||||
))
|
||||
->add_joins($this->get_joins());
|
||||
|
||||
// Database writes.
|
||||
$filters[] = (new filter(
|
||||
number::class,
|
||||
'dbwrites',
|
||||
new lang_string('task_dbwrites', 'admin'),
|
||||
$this->get_entity_name(),
|
||||
"{$tablealias}.dbwrites"
|
||||
))
|
||||
->add_joins($this->get_joins());
|
||||
|
||||
// Result filter.
|
||||
$filters[] = (new filter(
|
||||
select::class,
|
||||
'result',
|
||||
new lang_string('task_result', 'admin'),
|
||||
$this->get_entity_name(),
|
||||
"{$tablealias}.result"
|
||||
))
|
||||
->add_joins($this->get_joins())
|
||||
->set_options([
|
||||
self::SUCCESS => get_string('success'),
|
||||
self::FAILED => get_string('task_result:failed', 'admin'),
|
||||
]);
|
||||
|
||||
return $filters;
|
||||
}
|
||||
}
|
||||
@@ -1,159 +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\reportbuilder\local\systemreports;
|
||||
|
||||
use context_system;
|
||||
use core_admin\reportbuilder\local\entities\task_log;
|
||||
use core_reportbuilder\local\entities\user;
|
||||
use core_reportbuilder\local\report\action;
|
||||
use lang_string;
|
||||
use moodle_url;
|
||||
use pix_icon;
|
||||
use core_reportbuilder\system_report;
|
||||
|
||||
/**
|
||||
* Task logs system report class implementation
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2021 David Matamoros <davidmc@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class task_logs extends system_report {
|
||||
|
||||
/**
|
||||
* Initialise report, we need to set the main table, load our entities and set columns/filters
|
||||
*/
|
||||
protected function initialise(): void {
|
||||
// Our main entity, it contains all of the column definitions that we need.
|
||||
$entitymain = new task_log();
|
||||
$entitymainalias = $entitymain->get_table_alias('task_log');
|
||||
|
||||
$this->set_main_table('task_log', $entitymainalias);
|
||||
$this->add_entity($entitymain);
|
||||
|
||||
// Any columns required by actions should be defined here to ensure they're always available.
|
||||
$this->add_base_fields("{$entitymainalias}.id");
|
||||
|
||||
// We can join the "user" entity to our "main" entity and use the fullname column from the user entity.
|
||||
$entityuser = new user();
|
||||
$entituseralias = $entityuser->get_table_alias('user');
|
||||
$this->add_entity($entityuser->add_join(
|
||||
"LEFT JOIN {user} {$entituseralias} ON {$entituseralias}.id = {$entitymainalias}.userid"
|
||||
));
|
||||
|
||||
// Now we can call our helper methods to add the content we want to include in the report.
|
||||
$this->add_columns();
|
||||
$this->add_filters();
|
||||
$this->add_actions();
|
||||
|
||||
// Set if report can be downloaded.
|
||||
$this->set_downloadable(true, get_string('tasklogs', 'admin'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates access to view this report
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function can_view(): bool {
|
||||
return has_capability('moodle/site:config', context_system::instance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the visible name of the report
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name(): string {
|
||||
return get_string('entitytasklog', 'admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the columns we want to display in the report
|
||||
*
|
||||
* They are all provided by the entities we previously added in the {@see initialise} method, referencing each by their
|
||||
* unique identifier
|
||||
*/
|
||||
public function add_columns(): void {
|
||||
$columns = [
|
||||
'task_log:name',
|
||||
'task_log:type',
|
||||
'user:fullname',
|
||||
'task_log:starttime',
|
||||
'task_log:duration',
|
||||
'task_log:hostname',
|
||||
'task_log:pid',
|
||||
'task_log:database',
|
||||
'task_log:result',
|
||||
];
|
||||
|
||||
$this->add_columns_from_entities($columns);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the filters we want to display in the report
|
||||
*
|
||||
* They are all provided by the entities we previously added in the {@see initialise} method, referencing each by their
|
||||
* unique identifier
|
||||
*/
|
||||
protected function add_filters(): void {
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the system report actions. An extra column will be appended to each row, containing all actions added here
|
||||
*
|
||||
* Note the use of ":id" placeholder which will be substituted according to actual values in the row
|
||||
*/
|
||||
protected function add_actions(): void {
|
||||
|
||||
// Action to view individual task log on a popup window.
|
||||
$this->add_action((new action(
|
||||
new moodle_url('/admin/tasklogs.php', ['logid' => ':id']),
|
||||
new pix_icon('e/search', ''),
|
||||
[],
|
||||
true,
|
||||
new lang_string('view'),
|
||||
)));
|
||||
|
||||
// Action to download individual task log.
|
||||
$this->add_action((new action(
|
||||
new moodle_url('/admin/tasklogs.php', ['logid' => ':id', 'download' => true]),
|
||||
new pix_icon('t/download', ''),
|
||||
[],
|
||||
false,
|
||||
new lang_string('download'),
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -1,118 +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 core_plugin_manager;
|
||||
use dml_exception;
|
||||
use html_writer;
|
||||
use moodle_url;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Activity Module admin settings.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class activity_management_table extends plugin_management_table {
|
||||
|
||||
public function setup() {
|
||||
$this->set_attribute('id', 'modules');
|
||||
$this->set_attribute('class', 'admintable generaltable');
|
||||
parent::setup();
|
||||
}
|
||||
|
||||
protected function get_table_id(): string {
|
||||
return 'module-administration-table';
|
||||
}
|
||||
|
||||
protected function get_plugintype(): string {
|
||||
return 'mod';
|
||||
}
|
||||
|
||||
public function guess_base_url(): void {
|
||||
$this->define_baseurl(
|
||||
new moodle_url('/admin/modules.php')
|
||||
);
|
||||
}
|
||||
|
||||
protected function get_action_url(array $params = []): moodle_url {
|
||||
return new moodle_url('/admin/modules.php', $params);
|
||||
}
|
||||
|
||||
protected function get_column_list(): array {
|
||||
$columns = parent::get_column_list();
|
||||
return array_merge(
|
||||
array_slice($columns, 0, 1, true),
|
||||
['activities' => get_string('activities')],
|
||||
array_slice($columns, 1, null, true),
|
||||
);
|
||||
}
|
||||
|
||||
protected function col_name(stdClass $row): string {
|
||||
global $OUTPUT;
|
||||
|
||||
$status = $row->plugininfo->get_status();
|
||||
if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
|
||||
return html_writer::span(
|
||||
get_string('pluginmissingfromdisk', 'core', $row->plugininfo),
|
||||
'notifyproblem'
|
||||
);
|
||||
}
|
||||
|
||||
return html_writer::span(
|
||||
html_writer::img(
|
||||
$OUTPUT->image_url('monologo', $row->plugininfo->name),
|
||||
'',
|
||||
[
|
||||
'class' => 'icon',
|
||||
],
|
||||
) . get_string('modulename', $row->plugininfo->name)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the number of activities present, with a link to courses containing activity if relevant.
|
||||
*
|
||||
* @param mixed $row
|
||||
* @return string
|
||||
*/
|
||||
protected function col_activities(stdClass $row): string {
|
||||
global $DB, $OUTPUT;
|
||||
try {
|
||||
$count = $DB->count_records_select($row->plugininfo->name, "course <> 0");
|
||||
} catch (dml_exception $e) {
|
||||
$count = -1;
|
||||
}
|
||||
|
||||
if ($count > 0) {
|
||||
return $OUTPUT->action_link(
|
||||
new moodle_url('/course/search.php', [
|
||||
'modulelist' => $row->plugininfo->name,
|
||||
]),
|
||||
$count,
|
||||
null,
|
||||
['title' => get_string('showmodulecourse')]
|
||||
);
|
||||
} else if ($count < 0) {
|
||||
return get_string('error');
|
||||
} else {
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core_admin\table;
|
||||
|
||||
use html_writer;
|
||||
use moodle_url;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Tiny admin settings.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class block_management_table extends \core_admin\table\plugin_management_table {
|
||||
|
||||
/** @var plugininfo[] A list of blocks which cannot be deleted */
|
||||
protected array $undeletableblocktypes;
|
||||
|
||||
/** @var stdClass[] A list of basic block data */
|
||||
protected array $blockdata;
|
||||
|
||||
public function __construct() {
|
||||
global $DB;
|
||||
parent::__construct();
|
||||
$this->undeletableblocktypes = \block_manager::get_undeletable_block_types();
|
||||
|
||||
$sql = <<<EOF
|
||||
SELECT
|
||||
b.name,
|
||||
b.id,
|
||||
b.name,
|
||||
COUNT(DISTINCT binst.id) as totalcount,
|
||||
COUNT(DISTINCT bcinst.id) as courseviewcount
|
||||
FROM {block} b
|
||||
LEFT JOIN {block_instances} binst ON binst.blockname = b.name
|
||||
LEFT JOIN {block_instances} bcinst ON
|
||||
bcinst.blockname = b.name AND
|
||||
bcinst.pagetypepattern = 'course-view-*'
|
||||
GROUP BY b.id, b.name, b.visible, binst.blockname, bcinst.blockname
|
||||
ORDER BY b.name ASC
|
||||
EOF;
|
||||
|
||||
$this->blockdata = $DB->get_records_sql($sql);
|
||||
}
|
||||
|
||||
protected function get_plugintype(): string {
|
||||
return 'block';
|
||||
}
|
||||
|
||||
public function guess_base_url(): void {
|
||||
$this->define_baseurl(
|
||||
new moodle_url('/admin/blocks.php')
|
||||
);
|
||||
}
|
||||
|
||||
protected function get_action_url(array $params = []): moodle_url {
|
||||
return new moodle_url('/admin/blocks.php', $params);
|
||||
}
|
||||
|
||||
|
||||
protected function get_table_js_module(): string {
|
||||
return 'core_admin/block_management_table';
|
||||
}
|
||||
|
||||
protected function get_column_list(): array {
|
||||
$columns = parent::get_column_list();
|
||||
return array_merge(
|
||||
array_slice($columns, 0, 1, true),
|
||||
['instances' => get_string('blockinstances', 'admin')],
|
||||
array_slice($columns, 1, 2, true),
|
||||
['protect' => get_string('blockprotect', 'admin')],
|
||||
array_slice($columns, 3, null, true),
|
||||
);
|
||||
}
|
||||
|
||||
protected function get_columns_with_help(): array {
|
||||
return [
|
||||
'protect' => new \help_icon('blockprotect', 'admin'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the instances column
|
||||
* @param stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
protected function col_instances(stdClass $row): string {
|
||||
$blockdata = $this->blockdata[$row->plugininfo->name];
|
||||
if ($blockdata->courseviewcount > 0) {
|
||||
return html_writer::link(
|
||||
new moodle_url('/course/search.php', [
|
||||
'blocklist' => $row->plugininfo->name,
|
||||
'sesskey' => sesskey(),
|
||||
]),
|
||||
$blockdata->totalcount,
|
||||
);
|
||||
}
|
||||
|
||||
return $blockdata->totalcount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the protect column.
|
||||
*
|
||||
* @param stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
protected function col_protect(stdClass $row): string {
|
||||
global $OUTPUT;
|
||||
|
||||
$params = [
|
||||
'sesskey' => sesskey(),
|
||||
];
|
||||
|
||||
$protected = in_array($row->plugininfo->name, $this->undeletableblocktypes);
|
||||
|
||||
$pluginname = $row->plugininfo->displayname;
|
||||
if ($protected) {
|
||||
$params['unprotect'] = $row->plugininfo->name;
|
||||
$icon = $OUTPUT->pix_icon('t/unlock', get_string('blockunprotectblock', 'admin', $pluginname));
|
||||
} else {
|
||||
$params['protect'] = $row->plugininfo->name;
|
||||
$icon = $OUTPUT->pix_icon('t/lock', get_string('blockprotectblock', 'admin', $pluginname));
|
||||
}
|
||||
|
||||
return html_writer::link(
|
||||
$this->get_action_url($params),
|
||||
$icon,
|
||||
[
|
||||
'data-action' => 'toggleprotectstate',
|
||||
'data-plugin' => $row->plugin,
|
||||
'data-target-state' => $protected ? 0 : 1,
|
||||
],
|
||||
);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core_admin\table;
|
||||
|
||||
use moodle_url;
|
||||
|
||||
/**
|
||||
* Tiny admin settings.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class editor_management_table extends \core_admin\table\plugin_management_table {
|
||||
protected function get_plugintype(): string {
|
||||
return 'editor';
|
||||
}
|
||||
|
||||
public function guess_base_url(): void {
|
||||
$this->define_baseurl(
|
||||
new moodle_url('/admin/settings.php', ['section' => 'manageeditors'])
|
||||
);
|
||||
}
|
||||
|
||||
protected function get_action_url(array $params = []): moodle_url {
|
||||
return new moodle_url('/admin/editors.php', $params);
|
||||
}
|
||||
|
||||
protected function order_plugins(array $plugins): array {
|
||||
global $CFG;
|
||||
|
||||
// The Editor list is stored in an ordered string.
|
||||
$activeeditors = explode(',', $CFG->texteditors);
|
||||
|
||||
$sortedplugins = [];
|
||||
foreach ($activeeditors as $editor) {
|
||||
if (isset($plugins[$editor])) {
|
||||
$sortedplugins[$editor] = $plugins[$editor];
|
||||
unset($plugins[$editor]);
|
||||
}
|
||||
}
|
||||
|
||||
$otherplugins = parent::order_plugins($plugins);
|
||||
return array_merge(
|
||||
$sortedplugins,
|
||||
$otherplugins
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,71 +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;
|
||||
|
||||
/**
|
||||
* Media plugin admin settings.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class media_management_table extends \core_admin\table\plugin_management_table {
|
||||
/** @var array The list of used extensions */
|
||||
protected array $usedextensions = [];
|
||||
|
||||
protected function get_plugintype(): string {
|
||||
return 'media';
|
||||
}
|
||||
|
||||
protected function get_action_url(array $params = []): moodle_url {
|
||||
return new moodle_url('/admin/media.php', $params);
|
||||
}
|
||||
|
||||
protected function get_column_list(): array {
|
||||
$columns = parent::get_column_list();
|
||||
return array_merge(
|
||||
array_slice($columns, 0, 1, true),
|
||||
['supports' => get_string('supports', 'core_media')],
|
||||
array_slice($columns, 1, null, true),
|
||||
);
|
||||
}
|
||||
|
||||
protected function col_name(stdClass $row): string {
|
||||
global $OUTPUT, $PAGE;
|
||||
|
||||
$name = $row->plugininfo->name;
|
||||
if ($PAGE->theme->resolve_image_location('icon', 'media_' . $name, false)) {
|
||||
$icon = $OUTPUT->pix_icon('icon', '', "media_{$name}", ['class' => 'icon pluginicon']);
|
||||
} else {
|
||||
$icon = $OUTPUT->pix_icon('spacer', '', 'moodle', ['class' => 'icon pluginicon noicon']);
|
||||
}
|
||||
|
||||
$help = '';
|
||||
if (get_string_manager()->string_exists('pluginname_help', 'media_' . $name)) {
|
||||
$help = ' ' . $OUTPUT->help_icon('pluginname', 'media_' . $name);
|
||||
}
|
||||
|
||||
return $icon . $row->plugininfo->displayname . $help;
|
||||
}
|
||||
|
||||
protected function col_supports(stdClass $row): string {
|
||||
return $row->plugininfo->supports($this->usedextensions);
|
||||
}
|
||||
}
|
||||
@@ -1,503 +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 context_system;
|
||||
use core_plugin_manager;
|
||||
use core_table\dynamic as dynamic_table;
|
||||
use flexible_table;
|
||||
use html_writer;
|
||||
use moodle_url;
|
||||
use stdClass;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
require_once("{$CFG->libdir}/tablelib.php");
|
||||
|
||||
/**
|
||||
* Plugin Management table.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class plugin_management_table extends flexible_table implements dynamic_table {
|
||||
|
||||
/** @var \core\plugininfo\base[] The plugin list */
|
||||
protected array $plugins = [];
|
||||
|
||||
/** @var int The number of enabled plugins of this type */
|
||||
protected int $enabledplugincount = 0;
|
||||
|
||||
/** @var core_plugin_manager */
|
||||
protected core_plugin_manager $pluginmanager;
|
||||
|
||||
/** @var string The plugininfo class for this plugintype */
|
||||
protected string $plugininfoclass;
|
||||
|
||||
public function __construct() {
|
||||
global $CFG;
|
||||
|
||||
parent::__construct($this->get_table_id());
|
||||
require_once($CFG->libdir . '/adminlib.php');
|
||||
|
||||
// Fetch the plugininfo class.
|
||||
$this->pluginmanager = core_plugin_manager::instance();
|
||||
$this->plugininfoclass = $this->pluginmanager::resolve_plugininfo_class($this->get_plugintype());
|
||||
|
||||
$this->guess_base_url();
|
||||
|
||||
$this->plugins = $this->get_sorted_plugins();
|
||||
$this->enabledplugincount = count(array_filter($this->plugins, function ($plugin) {
|
||||
return $plugin->is_enabled();
|
||||
}));
|
||||
|
||||
$this->setup_column_configuration();
|
||||
$this->set_filterset(new plugin_management_table_filterset());
|
||||
$this->setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of sorted plugins.
|
||||
*
|
||||
* @return \core\plugininfo\base[]
|
||||
*/
|
||||
protected function get_sorted_plugins(): array {
|
||||
if ($this->plugininfoclass::plugintype_supports_ordering()) {
|
||||
return $this->plugininfoclass::get_sorted_plugins();
|
||||
} else {
|
||||
$plugins = $this->pluginmanager->get_plugins_of_type($this->get_plugintype());
|
||||
return self::sort_plugins($plugins);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the plugins list.
|
||||
*
|
||||
* Note: This only applies to plugins which do not support ordering.
|
||||
*
|
||||
* @param \core\plugininfo\base[] $plugins
|
||||
* @return \core\plugininfo\base[]
|
||||
*/
|
||||
protected function sort_plugins(array $plugins): array {
|
||||
// The asort functions work by reference.
|
||||
\core_collator::asort_objects_by_property($plugins, 'displayname');
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the column configuration for this table.
|
||||
*/
|
||||
protected function setup_column_configuration(): void {
|
||||
$columnlist = $this->get_column_list();
|
||||
$this->define_columns(array_keys($columnlist));
|
||||
$this->define_headers(array_values($columnlist));
|
||||
|
||||
$columnswithhelp = $this->get_columns_with_help();
|
||||
$columnhelp = array_map(function (string $column) use ($columnswithhelp): ?\renderable {
|
||||
if (array_key_exists($column, $columnswithhelp)) {
|
||||
return $columnswithhelp[$column];
|
||||
}
|
||||
|
||||
return null;
|
||||
}, array_keys($columnlist));
|
||||
$this->define_help_for_headers($columnhelp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the standard order of the plugins.
|
||||
*
|
||||
* @param array $plugins
|
||||
* @return array
|
||||
*/
|
||||
protected function order_plugins(array $plugins): array {
|
||||
uasort($plugins, function ($a, $b) {
|
||||
if ($a->is_enabled() && !$b->is_enabled()) {
|
||||
return -1;
|
||||
} else if (!$a->is_enabled() && $b->is_enabled()) {
|
||||
return 1;
|
||||
}
|
||||
return strnatcasecmp($a->name, $b->name);
|
||||
});
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugintype for this table.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function get_plugintype(): string;
|
||||
|
||||
/**
|
||||
* Get the action URL for this table.
|
||||
*
|
||||
* The action URL is used to perform all actions when JS is not available.
|
||||
*
|
||||
* @param array $params
|
||||
* @return moodle_url
|
||||
*/
|
||||
abstract protected function get_action_url(array $params = []): moodle_url;
|
||||
|
||||
/**
|
||||
* Provide a default implementation for guessing the base URL from the action URL.
|
||||
*/
|
||||
public function guess_base_url(): void {
|
||||
$this->define_baseurl($this->get_action_url());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the web service method used to toggle state.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function get_toggle_service(): ?string {
|
||||
return 'core_admin_set_plugin_state';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the web service method used to order plugins.
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
protected function get_sortorder_service(): ?string {
|
||||
return 'core_admin_set_plugin_order';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ID of the table.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_table_id(): string {
|
||||
return 'plugin_management_table-' . $this->get_plugintype();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the column titles
|
||||
* @return string[]
|
||||
*/
|
||||
protected function get_column_list(): array {
|
||||
$columns = [
|
||||
'name' => get_string('name', 'core'),
|
||||
'version' => get_string('version', 'core'),
|
||||
];
|
||||
|
||||
if ($this->supports_disabling()) {
|
||||
$columns['enabled'] = get_string('pluginenabled', 'core_plugin');
|
||||
}
|
||||
|
||||
if ($this->supports_ordering()) {
|
||||
$columns['order'] = get_string('order', 'core');
|
||||
}
|
||||
|
||||
$columns['settings'] = get_string('settings', 'core');
|
||||
$columns['uninstall'] = get_string('uninstallplugin', 'core_admin');
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
protected function get_columns_with_help(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the context for this table.
|
||||
*
|
||||
* @return context_system
|
||||
*/
|
||||
public function get_context(): context_system {
|
||||
return context_system::instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the table content.
|
||||
*/
|
||||
public function get_content(): string {
|
||||
ob_start();
|
||||
$this->out();
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the table.
|
||||
*/
|
||||
public function out(): void {
|
||||
$plugintype = $this->get_plugintype();
|
||||
foreach ($this->plugins as $plugininfo) {
|
||||
$plugin = "{$plugintype}_{$plugininfo->name}";
|
||||
$rowdata = (object) [
|
||||
'plugin' => $plugin,
|
||||
'plugininfo' => $plugininfo,
|
||||
'name' => $plugininfo->displayname,
|
||||
'version' => $plugininfo->versiondb,
|
||||
];
|
||||
$this->add_data_keyed(
|
||||
$this->format_row($rowdata),
|
||||
$this->get_row_class($rowdata)
|
||||
);
|
||||
}
|
||||
|
||||
$this->finish_output(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* This table is not downloadable.
|
||||
* @param bool $downloadable
|
||||
* @return bool
|
||||
*/
|
||||
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
public function is_downloadable($downloadable = null): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the name column content.
|
||||
*
|
||||
* @param stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
protected function col_name(stdClass $row): string {
|
||||
$status = $row->plugininfo->get_status();
|
||||
if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
|
||||
return html_writer::span(
|
||||
get_string('pluginmissingfromdisk', 'core', $row->plugininfo),
|
||||
'notifyproblem'
|
||||
);
|
||||
}
|
||||
|
||||
if ($row->plugininfo->is_installed_and_upgraded()) {
|
||||
return $row->plugininfo->displayname;
|
||||
}
|
||||
|
||||
return html_writer::span(
|
||||
$row->plugininfo->displayname,
|
||||
'notifyproblem'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the enable/disable column content.
|
||||
*
|
||||
* @param stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
protected function col_enabled(stdClass $row): string {
|
||||
global $OUTPUT;
|
||||
|
||||
$enabled = $row->plugininfo->is_enabled();
|
||||
$params = [
|
||||
'sesskey' => sesskey(),
|
||||
'plugin' => $row->plugininfo->name,
|
||||
'action' => $enabled ? 'disable' : 'enable',
|
||||
];
|
||||
|
||||
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 {
|
||||
global $OUTPUT;
|
||||
|
||||
if (!$this->supports_ordering()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!$row->plugininfo->is_enabled()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($this->enabledplugincount <= 1) {
|
||||
// There is only one row.
|
||||
return '';
|
||||
}
|
||||
|
||||
$hasup = true;
|
||||
$hasdown = true;
|
||||
|
||||
if (empty($this->currentrow)) {
|
||||
// This is the top row.
|
||||
$hasup = false;
|
||||
}
|
||||
|
||||
if ($this->currentrow === ($this->enabledplugincount - 1)) {
|
||||
// This is the last row.
|
||||
$hasdown = false;
|
||||
}
|
||||
|
||||
if ($this->supports_ordering()) {
|
||||
$dataattributes = [
|
||||
'data-method' => $this->get_sortorder_service(),
|
||||
'data-action' => 'move',
|
||||
'data-plugin' => $row->plugin,
|
||||
];
|
||||
} else {
|
||||
$dataattributes = [];
|
||||
}
|
||||
|
||||
if ($hasup) {
|
||||
$upicon = html_writer::link(
|
||||
$this->get_action_url([
|
||||
'sesskey' => sesskey(),
|
||||
'action' => 'up',
|
||||
'plugin' => $row->plugininfo->name,
|
||||
]),
|
||||
$OUTPUT->pix_icon('t/up', get_string('moveup')),
|
||||
array_merge($dataattributes, ['data-direction' => 'up']),
|
||||
);
|
||||
} else {
|
||||
$upicon = $OUTPUT->spacer();
|
||||
}
|
||||
|
||||
if ($hasdown) {
|
||||
$downicon = html_writer::link(
|
||||
$this->get_action_url([
|
||||
'sesskey' => sesskey(),
|
||||
'action' => 'down',
|
||||
'plugin' => $row->plugininfo->name,
|
||||
]),
|
||||
$OUTPUT->pix_icon('t/down', get_string('movedown')),
|
||||
array_merge($dataattributes, ['data-direction' => 'down']),
|
||||
);
|
||||
} else {
|
||||
$downicon = $OUTPUT->spacer();
|
||||
}
|
||||
|
||||
// For now just add the up/down icons.
|
||||
return html_writer::span($upicon . $downicon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the settings column content.
|
||||
*
|
||||
* @param stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
protected function col_settings(stdClass $row): string {
|
||||
if ($settingsurl = $row->plugininfo->get_settings_url()) {
|
||||
return html_writer::link($settingsurl, get_string('settings'));
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the Uninstall column content.
|
||||
*
|
||||
* @param stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
protected function col_uninstall(stdClass $row): string {
|
||||
$status = $row->plugininfo->get_status();
|
||||
|
||||
if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
|
||||
return get_string('status_new', 'core_plugin');
|
||||
}
|
||||
|
||||
if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
|
||||
$uninstall = get_string('status_missing', 'core_plugin') . '<br/>';
|
||||
} else {
|
||||
$uninstall = '';
|
||||
}
|
||||
|
||||
if ($uninstallurl = $this->pluginmanager->get_uninstall_url($row->plugin)) {
|
||||
$uninstall .= html_writer::link($uninstallurl, get_string('uninstallplugin', 'core_admin'));
|
||||
}
|
||||
|
||||
return $uninstall;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JS module used to manage this table.
|
||||
*
|
||||
* This should be a class which extends 'core_admin/plugin_management_table'.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_table_js_module(): string {
|
||||
return 'core_admin/plugin_management_table';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add JS specific to this implementation.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_dynamic_table_html_end(): string {
|
||||
global $PAGE;
|
||||
|
||||
$PAGE->requires->js_call_amd($this->get_table_js_module(), 'init');
|
||||
return parent::get_dynamic_table_html_end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any class to add to the row.
|
||||
*
|
||||
* @param mixed $row
|
||||
* @return string
|
||||
*/
|
||||
protected function get_row_class($row): string {
|
||||
$plugininfo = $row->plugininfo;
|
||||
if ($plugininfo->get_status() === core_plugin_manager::PLUGIN_STATUS_MISSING) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!$plugininfo->is_enabled()) {
|
||||
return 'dimmed_text';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function get_filterset_class(): string {
|
||||
return self::class . '_filterset';
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this plugin type supports the disabling of plugins.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function supports_disabling(): bool {
|
||||
return $this->plugininfoclass::plugintype_supports_disabling();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this table should show ordering fields.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function supports_ordering(): bool {
|
||||
return $this->plugininfoclass::plugintype_supports_ordering();
|
||||
}
|
||||
}
|
||||
@@ -1,27 +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;
|
||||
|
||||
/**
|
||||
* This file contains the dynamic interface.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class plugin_management_table_filterset extends \core_table\local\filter\filterset {
|
||||
}
|
||||
@@ -1,50 +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;
|
||||
|
||||
/**
|
||||
* Tiny admin settings.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tool_plugin_management_table extends \core_admin\table\plugin_management_table {
|
||||
protected function get_plugintype(): string {
|
||||
return 'tool';
|
||||
}
|
||||
|
||||
public function guess_base_url(): void {
|
||||
$this->define_baseurl(
|
||||
new moodle_url('/admin/tools.php')
|
||||
);
|
||||
}
|
||||
|
||||
protected function get_column_list(): array {
|
||||
$columns = parent::get_column_list();
|
||||
|
||||
unset($columns['settings']);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
protected function get_action_url(array $params = []): moodle_url {
|
||||
return new moodle_url('/admin/tool.php', $params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Task log table.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_admin;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/tablelib.php');
|
||||
|
||||
/**
|
||||
* Table to display list of task logs.
|
||||
*
|
||||
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class task_log_table extends \table_sql {
|
||||
|
||||
/**
|
||||
* Constructor for the task_log table.
|
||||
*
|
||||
* @param string $filter
|
||||
* @param int $resultfilter
|
||||
*/
|
||||
public function __construct(string $filter = '', int $resultfilter = null) {
|
||||
global $DB;
|
||||
|
||||
if (-1 === $resultfilter) {
|
||||
$resultfilter = null;
|
||||
}
|
||||
|
||||
parent::__construct('tasklogs');
|
||||
|
||||
$columnheaders = [
|
||||
'classname' => get_string('name'),
|
||||
'type' => get_string('tasktype', 'admin'),
|
||||
'userid' => get_string('user', 'admin'),
|
||||
'timestart' => get_string('task_starttime', 'admin'),
|
||||
'duration' => get_string('task_duration', 'admin'),
|
||||
'db' => get_string('task_dbstats', 'admin'),
|
||||
'result' => get_string('task_result', 'admin'),
|
||||
'actions' => '',
|
||||
];
|
||||
$this->define_columns(array_keys($columnheaders));
|
||||
$this->define_headers(array_values($columnheaders));
|
||||
|
||||
// The name column is a header.
|
||||
$this->define_header_column('classname');
|
||||
|
||||
// This table is not collapsible.
|
||||
$this->collapsible(false);
|
||||
|
||||
// The actions class should not wrap. Use the BS text utility class.
|
||||
$this->column_class('actions', 'text-nowrap');
|
||||
|
||||
// Allow pagination.
|
||||
$this->pageable(true);
|
||||
|
||||
// Allow sorting. Default to sort by timestarted DESC.
|
||||
$this->sortable(true, 'timestart', SORT_DESC);
|
||||
|
||||
// Add filtering.
|
||||
$where = [];
|
||||
$params = [];
|
||||
if (!empty($filter)) {
|
||||
$orwhere = [];
|
||||
$filter = str_replace('\\', '\\\\', $filter);
|
||||
|
||||
// Check the class name.
|
||||
$orwhere[] = $DB->sql_like('classname', ':classfilter', false, false);
|
||||
$params['classfilter'] = '%' . $DB->sql_like_escape($filter) . '%';
|
||||
|
||||
$orwhere[] = $DB->sql_like('output', ':outputfilter', false, false);
|
||||
$params['outputfilter'] = '%' . $DB->sql_like_escape($filter) . '%';
|
||||
|
||||
$where[] = "(" . implode(' OR ', $orwhere) . ")";
|
||||
}
|
||||
|
||||
if (null !== $resultfilter) {
|
||||
$where[] = 'tl.result = :result';
|
||||
$params['result'] = $resultfilter;
|
||||
}
|
||||
|
||||
$where = implode(' AND ', $where);
|
||||
|
||||
$this->set_sql('', '', $where, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the db. Store results in the table object for use by build_table.
|
||||
*
|
||||
* @param int $pagesize size of page for paginated displayed table.
|
||||
* @param bool $useinitialsbar do you want to use the initials bar. Bar
|
||||
* will only be used if there is a fullname column defined for the table.
|
||||
*/
|
||||
public function query_db($pagesize, $useinitialsbar = true) {
|
||||
global $DB;
|
||||
|
||||
// Fetch the attempts.
|
||||
$sort = $this->get_sql_sort();
|
||||
if ($sort) {
|
||||
$sort = "ORDER BY $sort";
|
||||
}
|
||||
|
||||
$extrafields = get_extra_user_fields(\context_system::instance());
|
||||
$userfields = \user_picture::fields('u', $extrafields, 'userid2', 'user');
|
||||
|
||||
$where = '';
|
||||
if (!empty($this->sql->where)) {
|
||||
$where = "WHERE {$this->sql->where}";
|
||||
}
|
||||
|
||||
$sql = "SELECT
|
||||
tl.id, tl.type, tl.component, tl.classname, tl.userid, tl.timestart, tl.timeend,
|
||||
tl.dbreads, tl.dbwrites, tl.result,
|
||||
tl.dbreads + tl.dbwrites AS db,
|
||||
tl.timeend - tl.timestart AS duration,
|
||||
{$userfields}
|
||||
FROM {task_log} tl
|
||||
LEFT JOIN {user} u ON u.id = tl.userid
|
||||
{$where}
|
||||
{$sort}";
|
||||
|
||||
$this->pagesize($pagesize, $DB->count_records_sql("SELECT COUNT('x') FROM {task_log} tl {$where}", $this->sql->params));
|
||||
if (!$this->is_downloading()) {
|
||||
$this->rawdata = $DB->get_records_sql($sql, $this->sql->params, $this->get_page_start(), $this->get_page_size());
|
||||
} else {
|
||||
$this->rawdata = $DB->get_records_sql($sql, $this->sql->params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the name cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_classname($row) : string {
|
||||
$output = '';
|
||||
if (class_exists($row->classname)) {
|
||||
$task = new $row->classname;
|
||||
if ($task instanceof \core\task\scheduled_task) {
|
||||
$output = $task->get_name();
|
||||
}
|
||||
}
|
||||
|
||||
$output .= \html_writer::tag('div', "\\{$row->classname}", [
|
||||
'class' => 'task-class',
|
||||
]);
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the type cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_type($row) : string {
|
||||
if (\core\task\database_logger::TYPE_SCHEDULED == $row->type) {
|
||||
return get_string('task_type:scheduled', 'admin');
|
||||
} else {
|
||||
return get_string('task_type:adhoc', 'admin');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the timestart cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_result($row) : string {
|
||||
if ($row->result) {
|
||||
return get_string('task_result:failed', 'admin');
|
||||
} else {
|
||||
return get_string('success');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the timestart cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_timestart($row) : string {
|
||||
return userdate($row->timestart, get_string('strftimedatetimeshort', 'langconfig'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the duration cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_duration($row) : string {
|
||||
$duration = round($row->timeend - $row->timestart, 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the DB details cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_db($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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the actions cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_actions($row) : string {
|
||||
global $OUTPUT;
|
||||
|
||||
$actions = [];
|
||||
|
||||
$url = new \moodle_url('/admin/tasklogs.php', ['logid' => $row->id]);
|
||||
|
||||
// Quick view.
|
||||
$actions[] = $OUTPUT->action_icon(
|
||||
$url,
|
||||
new \pix_icon('e/search', get_string('view')),
|
||||
new \popup_action('click', $url)
|
||||
);
|
||||
|
||||
// Download.
|
||||
$actions[] = $OUTPUT->action_icon(
|
||||
new \moodle_url($url, ['download' => true]),
|
||||
new \pix_icon('t/download', get_string('download'))
|
||||
);
|
||||
|
||||
return implode(' ', $actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the user cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_userid($row) : string {
|
||||
if (empty($row->userid)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$user = (object) [];
|
||||
username_load_fields_from_object($user, $row, 'user');
|
||||
|
||||
return fullname($user);
|
||||
}
|
||||
}
|
||||
@@ -1,174 +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/>.
|
||||
|
||||
/**
|
||||
* Task executor for adhoc tasks.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage cli
|
||||
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__ . '/../../config.php');
|
||||
require_once("{$CFG->libdir}/clilib.php");
|
||||
|
||||
list($options, $unrecognized) = cli_get_params(
|
||||
[
|
||||
'help' => false,
|
||||
'showsql' => false,
|
||||
'showdebugging' => false,
|
||||
'execute' => false,
|
||||
'keep-alive' => 0,
|
||||
'ignorelimits' => false,
|
||||
'force' => false,
|
||||
'id' => null,
|
||||
'classname' => null,
|
||||
'taskslimit' => null,
|
||||
'failed' => false,
|
||||
], [
|
||||
'h' => 'help',
|
||||
'e' => 'execute',
|
||||
'k' => 'keep-alive',
|
||||
'i' => 'ignorelimits',
|
||||
'f' => 'force',
|
||||
'c' => 'classname',
|
||||
'l' => 'taskslimit',
|
||||
]
|
||||
);
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
}
|
||||
|
||||
$help = <<<EOT
|
||||
Ad hoc cron tasks.
|
||||
|
||||
Options:
|
||||
-h, --help Print out this help
|
||||
--showsql Show sql queries before they are executed
|
||||
--showdebugging Show developer level debugging information
|
||||
-e, --execute Run all queued adhoc tasks
|
||||
-k, --keep-alive=N Keep this script alive for N seconds and poll for new adhoc tasks
|
||||
-i --ignorelimits Ignore task_adhoc_concurrency_limit and task_adhoc_max_runtime limits
|
||||
-f, --force Run even if cron is disabled
|
||||
--id Run (failed) task with id
|
||||
-c, --classname Run tasks with a certain classname (FQN)
|
||||
-l, --taskslimit=N Run at most N tasks
|
||||
--failed Run only tasks that failed, ie those with a fail delay
|
||||
|
||||
Run all queued tasks:
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/adhoc_task.php --execute
|
||||
|
||||
Run all queued tasks of specific class:
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/adhoc_task.php --classname=\\\\core_course\\\\task\\\\course_delete_modules
|
||||
|
||||
Double backslash for the shell escape reasons.
|
||||
Run a specific task:
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/adhoc_task.php --id=123456
|
||||
|
||||
Run a specific task with debugging:
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/adhoc_task.php --id=123456 --showsql --showdebugging
|
||||
|
||||
To profile a long running task:
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/adhoc_task.php --taskslimit=1 --classname='\\some\\class\\name' --ignorelimits
|
||||
|
||||
EOT;
|
||||
|
||||
if ($options['help']) {
|
||||
echo $help;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (CLI_MAINTENANCE) {
|
||||
echo "CLI maintenance mode active, cron execution suspended.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (moodle_needs_upgrading()) {
|
||||
echo "Moodle upgrade pending, cron execution suspended.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!get_config('core', 'cron_enabled') && !$options['force']) {
|
||||
mtrace('Cron is disabled. Use --force to override.');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Common debugging options.
|
||||
if ($options['showdebugging']) {
|
||||
set_debugging(DEBUG_DEVELOPER, true);
|
||||
}
|
||||
|
||||
if ($options['showsql']) {
|
||||
$DB->set_debug(true);
|
||||
}
|
||||
|
||||
if (!empty($CFG->showcronsql)) {
|
||||
$DB->set_debug(true);
|
||||
}
|
||||
if (!empty($CFG->showcrondebugging)) {
|
||||
set_debugging(DEBUG_DEVELOPER, true);
|
||||
}
|
||||
|
||||
// Process params.
|
||||
core_php_time_limit::raise();
|
||||
|
||||
// Increase memory limit.
|
||||
raise_memory_limit(MEMORY_EXTRA);
|
||||
|
||||
// Emulate normal session - we use admin account by default.
|
||||
\core\cron::setup_user();
|
||||
|
||||
\core\local\cli\shutdown::script_supports_graceful_exit();
|
||||
$humantimenow = date('r', time());
|
||||
mtrace("Server Time: {$humantimenow}\n");
|
||||
|
||||
$classname = $options['classname'];
|
||||
|
||||
// Run a single adhoc task only, if requested.
|
||||
if (!empty($options['id'])) {
|
||||
$taskid = (int) $options['id'];
|
||||
\core\cron::run_adhoc_task($taskid);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Run all failed tasks.
|
||||
if (!empty($options['failed'])) {
|
||||
\core\cron::run_failed_adhoc_tasks($classname);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Examine params and determine if we should run.
|
||||
$execute = (bool) $options['execute'];
|
||||
$keepalive = empty($options['keep-alive']) ? 0 : (int) $options['keep-alive'];
|
||||
$taskslimit = empty($options['taskslimit']) ? null : (int) $options['taskslimit'];
|
||||
$checklimits = empty($options['ignorelimits']);
|
||||
|
||||
if ($classname || $keepalive || $taskslimit) {
|
||||
$execute = true;
|
||||
}
|
||||
|
||||
// Output the help text if no criteria for running the adhoc tasks are given.
|
||||
if (!$execute) {
|
||||
echo $help;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
\core\cron::run_adhoc_tasks(time(), $keepalive, $checklimits, null, $taskslimit, $classname);
|
||||
@@ -30,6 +30,7 @@ define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__.'/../../config.php');
|
||||
require_once($CFG->libdir.'/clilib.php'); // cli only functions
|
||||
require_once($CFG->libdir.'/cronlib.php');
|
||||
|
||||
// now get cli options
|
||||
list($options, $unrecognized) = cli_get_params(array('help'=>false),
|
||||
@@ -79,10 +80,10 @@ if (!empty($CFG->showcrondebugging)) {
|
||||
|
||||
$starttime = microtime();
|
||||
|
||||
// Emulate normal session.
|
||||
\core\cron::setup_user();
|
||||
/// emulate normal session
|
||||
cron_setup_user();
|
||||
|
||||
// Start output log.
|
||||
/// Start output log
|
||||
$timenow = time();
|
||||
|
||||
mtrace("Server Time: ".date('r',$timenow)."\n\n");
|
||||
@@ -95,4 +96,4 @@ backup_cron_automated_helper::run_automated_backup(backup_cron_automated_helper:
|
||||
mtrace("Automated cron backups completed correctly");
|
||||
|
||||
$difftime = microtime_diff($starttime, microtime());
|
||||
mtrace("Execution took ".$difftime." seconds");
|
||||
mtrace("Execution took ".$difftime." seconds");
|
||||
+2
-14
@@ -120,23 +120,11 @@ if ($options['unset'] || $options['set'] !== null) {
|
||||
}
|
||||
|
||||
// Check that the variable is not hard-set in the main config.php already.
|
||||
$component = $options['component'];
|
||||
if (!empty($component)) {
|
||||
$componentsettings = $CFG->forced_plugin_settings[$component] ?? [];
|
||||
$settinginconfig = array_key_exists($options['name'], $componentsettings);
|
||||
} else {
|
||||
$settinginconfig = array_key_exists($options['name'], $CFG->config_php_settings);
|
||||
}
|
||||
if ($settinginconfig) {
|
||||
if (array_key_exists($options['name'], $CFG->config_php_settings)) {
|
||||
cli_error('The configuration variable is hard-set in the config.php, unable to change.', 4);
|
||||
}
|
||||
|
||||
$new = $options['set'];
|
||||
$old = get_config($options['component'], $options['name']);
|
||||
if ($new !== $old) {
|
||||
set_config($options['name'], $options['set'], $options['component']);
|
||||
add_to_config_log($options['name'], $old, $new, $options['component']);
|
||||
}
|
||||
set_config($options['name'], $options['set'], $options['component']);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,171 +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/>.
|
||||
|
||||
/**
|
||||
* CLI tool for system checks
|
||||
*
|
||||
* @package core
|
||||
* @category check
|
||||
* @copyright 2020 Brendan Heywood (brendan@catalyst-au.net)
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__ . '/../../config.php');
|
||||
require_once($CFG->libdir.'/clilib.php');
|
||||
|
||||
use core\check\result;
|
||||
|
||||
list($options, $unrecognized) = cli_get_params([
|
||||
'help' => false,
|
||||
'filter' => '',
|
||||
'type' => 'status',
|
||||
'verbose' => false,
|
||||
], [
|
||||
'h' => 'help',
|
||||
'f' => 'filter',
|
||||
'v' => 'verbose',
|
||||
't' => 'type',
|
||||
]);
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
}
|
||||
|
||||
$checks = \core\check\manager::get_checks($options['type']);
|
||||
$types = join(', ', \core\check\manager::TYPES);
|
||||
|
||||
$help = "Run Moodle system checks
|
||||
|
||||
Options:
|
||||
-h, --help Print out this help
|
||||
-f, --filter Filter to a subset of checks
|
||||
-t, --type Which set of checks? Defaults to 'status'
|
||||
One of $types
|
||||
-v, --verbose Show details of all checks, not just failed checks
|
||||
|
||||
Example:
|
||||
|
||||
sudo -u www-data php admin/cli/checks.php
|
||||
sudo -u www-data php admin/cli/checks.php -v
|
||||
sudo -u www-data php admin/cli/checks.php -v --filter=environment
|
||||
|
||||
";
|
||||
|
||||
if ($options['help']) {
|
||||
echo $help;
|
||||
die();
|
||||
}
|
||||
|
||||
$filter = $options['filter'];
|
||||
if ($filter) {
|
||||
$checks = array_filter($checks, function($check, $key) use ($filter) {
|
||||
$ref = $check->get_ref();
|
||||
return (strpos($ref, $filter) !== false);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
// These shell exit codes and labels align with the NRPE standard.
|
||||
$exitcodes = [
|
||||
result::NA => 0,
|
||||
result::OK => 0,
|
||||
result::INFO => 0,
|
||||
result::UNKNOWN => 3,
|
||||
result::WARNING => 1,
|
||||
result::ERROR => 2,
|
||||
result::CRITICAL => 2,
|
||||
];
|
||||
$exitlabel = [
|
||||
result::NA => 'OK',
|
||||
result::OK => 'OK',
|
||||
result::INFO => 'OK',
|
||||
result::UNKNOWN => 'UNKNOWN',
|
||||
result::WARNING => 'WARNING',
|
||||
result::ERROR => 'CRITICAL',
|
||||
result::CRITICAL => 'CRITICAL',
|
||||
];
|
||||
|
||||
$format = "% 10s| % -60s\n";
|
||||
$spacer = "----------+--------------------------------------------------------------------\n";
|
||||
$prefix = ' |';
|
||||
|
||||
$output = '';
|
||||
$header = $exitlabel[result::OK] . ': ' . get_string('checksok', '', $options['type']) . "\n";
|
||||
$exitcode = $exitcodes[result::OK];
|
||||
|
||||
foreach ($checks as $check) {
|
||||
$ref = $check->get_ref();
|
||||
$result = $check->get_result();
|
||||
|
||||
$status = $result->get_status();
|
||||
$checkexitcode = $exitcodes[$status];
|
||||
|
||||
// Summary is treated as html.
|
||||
$summary = $result->get_summary();
|
||||
$summary = html_to_text($summary, 60, false);
|
||||
|
||||
if ($checkexitcode > $exitcode) {
|
||||
$exitcode = $checkexitcode;
|
||||
$header = $exitlabel[$status] . ': ' . $check->get_name() . " (" . $check->get_ref() . ")\n";
|
||||
}
|
||||
|
||||
if (empty($messages[$status])) {
|
||||
$messages[$status] = $result;
|
||||
}
|
||||
|
||||
$len = strlen(get_string('status' . $status));
|
||||
|
||||
if ($options['verbose'] ||
|
||||
$status == result::WARNING ||
|
||||
$status == result::CRITICAL ||
|
||||
$status == result::ERROR) {
|
||||
|
||||
$output .= sprintf(
|
||||
$format,
|
||||
$OUTPUT->check_result($result),
|
||||
sprintf('%s (%s)', $check->get_name(), $ref)
|
||||
);
|
||||
|
||||
$summary = str_replace("\n", "\n" . $prefix . ' ', $summary);
|
||||
$output .= sprintf( $format, '', ' ' . $summary);
|
||||
|
||||
if ($options['verbose']) {
|
||||
$actionlink = $check->get_action_link();
|
||||
if ($actionlink) {
|
||||
$output .= sprintf( $format, '', ' ' . $actionlink->url);
|
||||
}
|
||||
$output .= sprintf( $format, '', '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print NRPE header.
|
||||
print $header;
|
||||
|
||||
// Only show the table header if there is anything to show.
|
||||
if ($output) {
|
||||
print sprintf($format,
|
||||
get_string('status'). ' ',
|
||||
get_string('check')
|
||||
) . $spacer;
|
||||
print $output;
|
||||
}
|
||||
|
||||
// NRPE shell exit code.
|
||||
exit($exitcode);
|
||||
|
||||
+10
-135
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@@ -29,32 +30,13 @@
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__ . '/../../config.php');
|
||||
require_once($CFG->libdir . '/clilib.php');
|
||||
require(__DIR__.'/../../config.php');
|
||||
require_once($CFG->libdir.'/clilib.php'); // cli only functions
|
||||
require_once($CFG->libdir.'/cronlib.php');
|
||||
|
||||
// Now get cli option.
|
||||
[$options, $unrecognized] = cli_get_params(
|
||||
[
|
||||
'help' => false,
|
||||
'stop' => false,
|
||||
'list' => false,
|
||||
'force' => false,
|
||||
'enable' => false,
|
||||
'disable' => false,
|
||||
'disable-wait' => false,
|
||||
'keep-alive' => null,
|
||||
],
|
||||
[
|
||||
'h' => 'help',
|
||||
's' => 'stop',
|
||||
'l' => 'list',
|
||||
'f' => 'force',
|
||||
'e' => 'enable',
|
||||
'd' => 'disable',
|
||||
'w' => 'disable-wait',
|
||||
'k' => 'keep-alive',
|
||||
]
|
||||
);
|
||||
// now get cli options
|
||||
list($options, $unrecognized) = cli_get_params(array('help'=>false),
|
||||
array('h'=>'help'));
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
@@ -63,19 +45,10 @@ if ($unrecognized) {
|
||||
|
||||
if ($options['help']) {
|
||||
$help =
|
||||
"Execute periodic cron actions.
|
||||
"Execute periodic cron actions.
|
||||
|
||||
Options:
|
||||
-h, --help Print out this help
|
||||
-s, --stop Notify all other running cron processes to stop after the current task
|
||||
-l, --list Show the list of currently running tasks and how long they have been running
|
||||
-f, --force Execute task even if cron is disabled
|
||||
-e, --enable Enable cron
|
||||
-d, --disable Disable cron
|
||||
-w, --disable-wait=600 Disable cron and wait until all tasks finished or fail after N seconds (optional param)
|
||||
-k, --keep-alive=N Keep this script alive for N seconds and poll for new tasks
|
||||
The default value can be set by administrators in:
|
||||
Site administration > Server > Tasks > Task processing > Keep alive time
|
||||
-h, --help Print out this help
|
||||
|
||||
Example:
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/cron.php
|
||||
@@ -85,102 +58,4 @@ Example:
|
||||
die;
|
||||
}
|
||||
|
||||
if ($options['stop']) {
|
||||
// By clearing the caches this signals to other running processes
|
||||
// to exit after finishing the current task.
|
||||
\core\task\manager::clear_static_caches();
|
||||
die;
|
||||
}
|
||||
|
||||
if ($options['enable']) {
|
||||
set_config('cron_enabled', 1);
|
||||
mtrace('Cron has been enabled for the site.');
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($options['disable']) {
|
||||
set_config('cron_enabled', 0);
|
||||
\core\task\manager::clear_static_caches();
|
||||
mtrace('Cron has been disabled for the site.');
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($options['list']) {
|
||||
$tasks = \core\task\manager::get_running_tasks();
|
||||
mtrace('The list of currently running tasks:');
|
||||
$format = "%7s %-12s %-9s %-20s %-52s\n";
|
||||
printf(
|
||||
$format,
|
||||
'PID',
|
||||
'HOST',
|
||||
'TYPE',
|
||||
'TIME',
|
||||
'CLASSNAME'
|
||||
);
|
||||
foreach ($tasks as $task) {
|
||||
printf(
|
||||
$format,
|
||||
$task->pid,
|
||||
substr($task->hostname, 0, 12),
|
||||
$task->type,
|
||||
format_time(time() - $task->timestarted),
|
||||
substr($task->classname, 0, 52)
|
||||
);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($wait = $options['disable-wait']) {
|
||||
$started = time();
|
||||
if (true === $wait) {
|
||||
// Default waiting time.
|
||||
$waitsec = 600;
|
||||
} else {
|
||||
$waitsec = $wait;
|
||||
$wait = true;
|
||||
}
|
||||
|
||||
set_config('cron_enabled', 0);
|
||||
\core\task\manager::clear_static_caches();
|
||||
mtrace('Cron has been disabled for the site.');
|
||||
mtrace('Allocating ' . format_time($waitsec) . ' for the tasks to finish.');
|
||||
|
||||
$lastcount = 0;
|
||||
while ($wait) {
|
||||
$tasks = \core\task\manager::get_running_tasks();
|
||||
|
||||
if (count($tasks) == 0) {
|
||||
mtrace('');
|
||||
mtrace('All scheduled and adhoc tasks finished.');
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (time() - $started >= $waitsec) {
|
||||
mtrace('');
|
||||
mtrace('Wait time (' . format_time($waitsec) . ') elapsed, but ' . count($tasks) . ' task(s) still running.');
|
||||
mtrace('Exiting with code 1.');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (count($tasks) !== $lastcount) {
|
||||
mtrace('');
|
||||
mtrace(count($tasks) . " tasks currently running.", '');
|
||||
$lastcount = count($tasks);
|
||||
} else {
|
||||
mtrace('.', '');
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!get_config('core', 'cron_enabled') && !$options['force']) {
|
||||
mtrace('Cron is disabled. Use --force to override.');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
\core\local\cli\shutdown::script_supports_graceful_exit();
|
||||
|
||||
|
||||
$keepalive = $options['keep-alive'];
|
||||
\core\cron::run_main_process($keepalive);
|
||||
cron_run();
|
||||
|
||||
@@ -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/>.
|
||||
|
||||
/**
|
||||
* CLI script to reset dashboards.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage cli
|
||||
* @copyright 2021 Brendan Heywood (brendan@catalyst-au.net)
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__.'/../../config.php');
|
||||
require_once($CFG->libdir.'/clilib.php');
|
||||
require_once($CFG->dirroot.'/my/lib.php');
|
||||
|
||||
list($options, $unrecognized) = cli_get_params([
|
||||
'help' => false,
|
||||
'execute' => false,
|
||||
], [
|
||||
'h' => 'help',
|
||||
'e' => 'execute',
|
||||
]);
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized), 2);
|
||||
}
|
||||
|
||||
if (!$options['execute']) {
|
||||
$help = <<<EOF
|
||||
Resets Moodle dashboards for all users
|
||||
|
||||
Options:
|
||||
-h, --help Print out this help
|
||||
-e, --execute Actually run the reset
|
||||
|
||||
Example:
|
||||
\$ sudo -u www-data /usr/bin/php admin/cli/dashboard_reset.php -e
|
||||
|
||||
EOF;
|
||||
|
||||
echo $help;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$progressbar = new progress_bar();
|
||||
$progressbar->create();
|
||||
my_reset_page_for_all_users(MY_PAGE_PRIVATE, 'my-index', $progressbar);
|
||||
|
||||
@@ -1,123 +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/>.
|
||||
|
||||
/**
|
||||
* CLI script to delete a course.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage cli
|
||||
* @author Mikhail Golenkov <mikhailgolenkov@catalyst-au.net>
|
||||
* @copyright 2022 Catalyst IT
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__ . '/../../config.php');
|
||||
require_once($CFG->libdir . '/clilib.php');
|
||||
|
||||
list($options, $unrecognized) = cli_get_params(
|
||||
[
|
||||
'courseid' => false,
|
||||
'help' => false,
|
||||
'showsql' => false,
|
||||
'showdebugging' => false,
|
||||
'disablerecyclebin' => false,
|
||||
'non-interactive' => false,
|
||||
], [
|
||||
'c' => 'courseid',
|
||||
'h' => 'help',
|
||||
]
|
||||
);
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
}
|
||||
|
||||
if ($options['help'] || empty($options['courseid'])) {
|
||||
$help = <<<EOT
|
||||
CLI script to delete a course.
|
||||
|
||||
Options:
|
||||
-h, --help Print out this help
|
||||
--showsql Show sql queries before they are executed
|
||||
--showdebugging Show developer level debugging information
|
||||
--disablerecyclebin Skip backing up the course
|
||||
--non-interactive No interactive questions or confirmations
|
||||
-c, --courseid Course id to be deleted
|
||||
|
||||
Example:
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/delete_course.php --courseid=123456
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/delete_course.php --courseid=123456 --showdebugging
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/delete_course.php --courseid=123456 --disablerecyclebin
|
||||
|
||||
EOT;
|
||||
|
||||
echo $help;
|
||||
die;
|
||||
}
|
||||
|
||||
$interactive = empty($options['non-interactive']);
|
||||
|
||||
if ($options['showdebugging']) {
|
||||
mtrace('Enabling debugging...');
|
||||
set_debugging(DEBUG_DEVELOPER, true);
|
||||
}
|
||||
|
||||
if ($options['showsql']) {
|
||||
mtrace('Enabling SQL debugging...');
|
||||
$DB->set_debug(true);
|
||||
}
|
||||
|
||||
if (CLI_MAINTENANCE) {
|
||||
cli_error('CLI maintenance mode active, CLI execution suspended');
|
||||
}
|
||||
|
||||
if (moodle_needs_upgrading()) {
|
||||
cli_error('Moodle upgrade pending, CLI execution suspended');
|
||||
}
|
||||
|
||||
$course = $DB->get_record('course', array('id' => $options['courseid']));
|
||||
if (empty($course)) {
|
||||
cli_error('Course not found');
|
||||
}
|
||||
|
||||
mtrace('Deleting course id ' . $course->id);
|
||||
mtrace('Course name: ' . $course->fullname);
|
||||
mtrace('Short name: ' . $course->shortname);
|
||||
|
||||
if ($interactive) {
|
||||
mtrace('');
|
||||
$input = cli_input('Are you sure you wish to delete this course? (y/N)', 'N', ['y', 'Y', 'n', 'N']);
|
||||
if (strtolower($input) != 'y') {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['disablerecyclebin']) {
|
||||
mtrace('Disabling recycle bin...');
|
||||
$overrideconfig = ['tool_recyclebin' => ['coursebinenable' => false, 'categorybinenable' => false]];
|
||||
$CFG->forced_plugin_settings = array_merge($CFG->forced_plugin_settings, $overrideconfig);
|
||||
}
|
||||
|
||||
core_php_time_limit::raise();
|
||||
delete_course($course);
|
||||
|
||||
mtrace('Updating course count in categories...');
|
||||
fix_course_sortorder();
|
||||
|
||||
mtrace('Done!');
|
||||
@@ -1,209 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This script allows you to view and change the emailstop flag of any user.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage cli
|
||||
* @copyright 2023 Stephan Robotta (stephan.robotta@bfh.ch)
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__.'/../../config.php');
|
||||
require_once($CFG->libdir.'/clilib.php');
|
||||
|
||||
// Define the input options.
|
||||
$longparams = [
|
||||
'email' => '',
|
||||
'help' => false,
|
||||
'id' => '',
|
||||
'quiet' => false,
|
||||
'stop' => '',
|
||||
'username' => '',
|
||||
];
|
||||
|
||||
$shortparams = [
|
||||
'e' => 'email',
|
||||
'h' => 'help',
|
||||
'i' => 'id',
|
||||
'q' => 'quiet',
|
||||
's' => 'stop',
|
||||
'u' => 'username',
|
||||
];
|
||||
|
||||
// Define exit codes.
|
||||
$exitsuccess = 0;
|
||||
$exitunknownoption = 1;
|
||||
$exitmissinguserarg = 2;
|
||||
$exittoomanyuserarg = 3;
|
||||
$exitnosearchargs = 4;
|
||||
$exitnousersfound = 5;
|
||||
$exitinvalidstopflag = 6;
|
||||
$exiterrordb = 7;
|
||||
|
||||
// Now get cli options that are set by the caller.
|
||||
list($options, $unrecognized) = cli_get_params($longparams, $shortparams);
|
||||
|
||||
$verbose = empty($options['quiet']);
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
if ($verbose) {
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized), $exitunknownoption);
|
||||
}
|
||||
exit($exitunknownoption);
|
||||
}
|
||||
|
||||
if ($options['help']) {
|
||||
$help =
|
||||
"Set/unset or show status of emailstop flag for a user, identified by username or email.
|
||||
|
||||
There are no security checks here because anybody who is able to
|
||||
execute this file may execute any PHP too.
|
||||
|
||||
Options:
|
||||
-h, --help Print out this help
|
||||
-e, --email=email Specify user by email, separate many users by comma
|
||||
-i, --id=id Specify user by id, separate many users by comma
|
||||
-q, --quiet No output to stdout
|
||||
-s, --stop=0|1|off|on Set new value for emailstop flag
|
||||
-u, --username=username Specify user by username, separate many users by comma
|
||||
|
||||
Example:
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/emailstop.php --email=student1@example.com --stop=1
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/emailstop.php --email=student1@example.com,student2@example.com
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/emailstop.php --u=student1,student2 -s=on
|
||||
";
|
||||
|
||||
echo $help;
|
||||
exit($exitsuccess);
|
||||
}
|
||||
|
||||
$cntempty = 0;
|
||||
$cntfilled = 0;
|
||||
$searchargs = [];
|
||||
|
||||
// Try to find out which option is used to fetch the users from. Also do sanitize etc.
|
||||
foreach (['email', 'username', 'id'] as $option) {
|
||||
if (empty($options[$option])) {
|
||||
$cntempty++;
|
||||
} else {
|
||||
$cntfilled++;
|
||||
$argname = $option;
|
||||
// The search args must be: split by the comma, trimmed, and empty elements filtered out.
|
||||
$searchargs = array_flip(array_filter(
|
||||
array_map(
|
||||
function ($item) {
|
||||
return trim($item);
|
||||
},
|
||||
explode(',', $options[$option])
|
||||
),
|
||||
function ($item) {
|
||||
return $item !== '';
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
if ($cntempty === 3) {
|
||||
if ($verbose) {
|
||||
cli_error('One of username, email, or id must be set.', $exitmissinguserarg);
|
||||
}
|
||||
exit($exitmissinguserarg);
|
||||
}
|
||||
if ($cntfilled > 1) {
|
||||
if ($verbose) {
|
||||
cli_error('Only one of email, username, or id can be set to identify a user.', $exittoomanyuserarg);
|
||||
}
|
||||
exit($exittoomanyuserarg);
|
||||
}
|
||||
if (empty($searchargs)) {
|
||||
if ($verbose) {
|
||||
cli_error('No values are provided for users.', $exitnosearchargs);
|
||||
}
|
||||
exit($exitnosearchargs);
|
||||
}
|
||||
try {
|
||||
$users = $DB->get_records_list('user', $argname, array_keys($searchargs));
|
||||
} catch (Exception $e) {
|
||||
if ($verbose) {
|
||||
cli_error("Could not fetch data from db by {$argname}: '{$options[$argname]}'.", $exiterrordb);
|
||||
}
|
||||
exit($exiterrordb);
|
||||
}
|
||||
if (empty($users)) {
|
||||
if ($verbose) {
|
||||
cli_error("Can not find any user by {$argname}: '{$options[$argname]}'.", $exitnousersfound);
|
||||
}
|
||||
exit($exitnousersfound);
|
||||
}
|
||||
|
||||
// No stop flag set, then just print the user and the current emailstop flag state.
|
||||
if ($options['stop'] === '') {
|
||||
foreach ($users as $user) {
|
||||
if ($verbose) {
|
||||
echo 'user=' . $user->{$argname} . ' - emailstop=' . (int)$user->emailstop . PHP_EOL;
|
||||
unset($searchargs[$user->{$argname}]);
|
||||
}
|
||||
}
|
||||
if ($verbose) {
|
||||
foreach (array_keys($searchargs) as $arg) {
|
||||
echo 'user=' . $arg . ' - not found' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
exit($exitsuccess);
|
||||
}
|
||||
|
||||
// Allowed values for the stop flag enabled are 1 and on, for disabled are 0 and off.
|
||||
$validvalues = ['0', '1', 'off', 'on'];
|
||||
$stopflag = strtolower($options['stop']);
|
||||
if (!in_array($stopflag, $validvalues)) {
|
||||
if ($verbose) {
|
||||
cli_error('Value for the emailstop flag must be one of: ' . implode(', ', $validvalues) . '.', $exitinvalidstopflag);
|
||||
}
|
||||
exit($exitinvalidstopflag);
|
||||
}
|
||||
|
||||
foreach ($validvalues as $value) {
|
||||
if ($value === $stopflag) {
|
||||
$stopflag = ($value === '1' || $value === 'on') ? 1 : 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Update each user with the stop flag to be set if it is necessary.
|
||||
foreach ($users as $user) {
|
||||
$line = 'Update user ' . $user->{$argname} . ($argname !== 'id' ? ' (' . $user->id . ')' : '') . ' - ';
|
||||
if ((int)$user->emailstop !== $stopflag) {
|
||||
$DB->set_field('user', 'emailstop', $stopflag, ['id' => $user->id]);
|
||||
$line .= 'ok';
|
||||
} else {
|
||||
$line .= 'already done';
|
||||
}
|
||||
if ($verbose) {
|
||||
echo $line . PHP_EOL;
|
||||
unset($searchargs[$user->{$argname}]);
|
||||
}
|
||||
|
||||
}
|
||||
if ($verbose) {
|
||||
foreach (array_keys($searchargs) as $arg) {
|
||||
echo 'user=' . $arg . ' - not found' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
exit($exitsuccess);
|
||||
|
||||
@@ -1,138 +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/>.
|
||||
|
||||
/**
|
||||
* Fix orphaned calendar events that were broken by MDL-67494.
|
||||
*
|
||||
* This script will look for all the calendar events which userids
|
||||
* where broken by a wrong upgrade step, affecting to Moodle 3.9.5
|
||||
* and up.
|
||||
*
|
||||
* It performs checks to both:
|
||||
* a) Detect if the site was affected (ran the wrong upgrade step).
|
||||
* b) Look for orphaned calendar events, categorising them as:
|
||||
* - standard: site / category / course / group / user events
|
||||
* - subscription: events created via subscriptions.
|
||||
* - action: normal action events, created to show common important dates.
|
||||
* - override: user and group override events, particular, that some activities support.
|
||||
* - custom: other events, not being any of the above, common or particular.
|
||||
* By specifying it (--fix) try to recover as many broken events (missing userid) as
|
||||
* possible. Standard, subscription, action, override events in core are fully supported but
|
||||
* override or custom events should be fixed by each plugin as far as there isn't any standard
|
||||
* API (plugin-wise) to launch a rebuild of the calendar events.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2021 onwards Simey Lameze <simey@moodle.com>
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require_once(__DIR__ . '/../../config.php');
|
||||
require_once($CFG->libdir . "/clilib.php");
|
||||
require_once($CFG->libdir . '/db/upgradelib.php');
|
||||
|
||||
// Supported options.
|
||||
$long = ['fix' => false, 'help' => false];
|
||||
$short = ['f' => 'fix', 'h' => 'help'];
|
||||
|
||||
// CLI options.
|
||||
[$options, $unrecognized] = cli_get_params($long, $short);
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
}
|
||||
|
||||
if ($options['help']) {
|
||||
$help = <<<EOT
|
||||
Fix orphaned calendar events.
|
||||
|
||||
This script detects calendar events that have had their
|
||||
userid lost. By default it will perform various checks
|
||||
and report them, showing the site status in an easy way.
|
||||
|
||||
Also, optionally (--fix), it wil try to recover as many
|
||||
lost userids as possible from different sources. Note that
|
||||
this script aims to process well-know events in core,
|
||||
leaving custom events in 3rd part plugins mostly unmodified
|
||||
because there isn't any consistent way to regenerate them.
|
||||
|
||||
For more details: https://tracker.moodle.org/browse/MDL-71156
|
||||
|
||||
Options:
|
||||
-h, --help Print out this help.
|
||||
-f, --fix Fix the orphaned calendar events in the DB.
|
||||
If not specified only check and report problems to STDERR.
|
||||
|
||||
Usage:
|
||||
- Only report: \$ sudo -u www-data /usr/bin/php admin/cli/fix_orphaned_calendar_events.php
|
||||
- Report and fix: \$ sudo -u www-data /usr/bin/php admin/cli/fix_orphaned_calendar_events.php -f
|
||||
EOT;
|
||||
|
||||
cli_writeln($help);
|
||||
die;
|
||||
}
|
||||
|
||||
// Check various usual pre-requisites.
|
||||
if (empty($CFG->version)) {
|
||||
cli_error('Database is not yet installed.');
|
||||
}
|
||||
|
||||
$admin = get_admin();
|
||||
if (!$admin) {
|
||||
cli_error('Error: No admin account was found.');
|
||||
}
|
||||
|
||||
if (moodle_needs_upgrading()) {
|
||||
cli_error('Moodle upgrade pending, script execution suspended.');
|
||||
}
|
||||
|
||||
// Do everything as admin by default.
|
||||
\core\session\manager::set_user($admin);
|
||||
|
||||
// Report current site status.
|
||||
cli_heading('Checking the site status');
|
||||
$needsfix = upgrade_calendar_site_status();
|
||||
|
||||
// Report current calendar events status.
|
||||
cli_heading('Checking the calendar events status');
|
||||
$info = upgrade_calendar_events_status();
|
||||
$hasbadevents = $info['total']->bad > 0 || $info['total']->bad != $info['other']->bad;
|
||||
$needsfix = $needsfix || $hasbadevents;
|
||||
|
||||
// If, selected, fix as many calendar events as possible.
|
||||
if ($options['fix']) {
|
||||
|
||||
// If the report has told us that the fix was not needed... ask for confirmation!
|
||||
if (!$needsfix) {
|
||||
cli_writeln("This site DOES NOT NEED to run the calendar events fix.");
|
||||
$input = cli_input('Are you completely sure that you want to run the fix? (y/N)', 'N', ['y', 'Y', 'n', 'N']);
|
||||
if (strtolower($input) != 'y') {
|
||||
exit(0);
|
||||
}
|
||||
cli_writeln("");
|
||||
}
|
||||
cli_heading('Fixing as many as possible calendar events');
|
||||
upgrade_calendar_events_fix_remaining($info);
|
||||
// Report current (after fix) calendar events status.
|
||||
cli_heading('Checking the calendar events status (after fix)');
|
||||
upgrade_calendar_events_status();
|
||||
} else if ($needsfix) {
|
||||
// Fix option was not provided but problem events have been found. Notify the user and provide info how to fix these events.
|
||||
cli_writeln("This site NEEDS to run the calendar events fix!");
|
||||
cli_writeln("To fix the calendar events, re-run this script with the --fix option.");
|
||||
}
|
||||
@@ -1,79 +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/>.
|
||||
|
||||
/**
|
||||
* Generates a secure key for the current server (presuming it does not already exist).
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2020 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
use \core\encryption;
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__ . '/../../config.php');
|
||||
require_once($CFG->libdir . '/clilib.php');
|
||||
|
||||
// Get cli options.
|
||||
[$options, $unrecognized] = cli_get_params(
|
||||
['help' => false, 'method' => null],
|
||||
['h' => 'help']);
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
}
|
||||
|
||||
// TODO: MDL-71421 - Remove the openssl alternative once sodium becomes a requirement in Moodle 4.2.
|
||||
|
||||
if ($options['help']) {
|
||||
echo "Generate secure key
|
||||
|
||||
This script manually creates a secure key within the secret data root folder (configured in
|
||||
config.php as \$CFG->secretdataroot). You must run it using an account with access to write
|
||||
to that folder.
|
||||
|
||||
In normal use Moodle automatically creates the key; this script is intended when setting up
|
||||
a new Moodle system, for cases where the secure folder is not on shared storage and the key
|
||||
may be manually installed on multiple servers.
|
||||
|
||||
Options:
|
||||
-h, --help Print out this help
|
||||
--method <method> Generate key for specified encryption method instead of default.
|
||||
* sodium
|
||||
* openssl-aes-256-ctr
|
||||
|
||||
Example:
|
||||
php admin/cli/generate_key.php
|
||||
";
|
||||
exit;
|
||||
}
|
||||
|
||||
$method = $options['method'];
|
||||
|
||||
if (encryption::key_exists($method)) {
|
||||
echo 'Key already exists: ' . encryption::get_key_file($method) . "\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
// Creates key with default permissions (no chmod).
|
||||
echo "Generating key...\n";
|
||||
encryption::create_key($method, false);
|
||||
|
||||
echo "\nKey created: " . encryption::get_key_file($method) . "\n\n";
|
||||
echo "If the key folder is not shared storage, then key files should be copied to all servers.\n";
|
||||
@@ -74,8 +74,6 @@ Options:
|
||||
--adminpass=PASSWORD Password for the moodle admin account,
|
||||
required in non-interactive mode.
|
||||
--adminemail=STRING Email address for the moodle admin account.
|
||||
--sitepreset=STRING Admin site preset to be applied during the installation process.
|
||||
--supportemail=STRING Email address for support and help.
|
||||
--upgradekey=STRING The upgrade key to be set in the config.php, leave empty to not set it.
|
||||
--non-interactive No interactive questions, installation fails if any
|
||||
problem encountered.
|
||||
@@ -217,7 +215,6 @@ define('SITEID', 1);
|
||||
|
||||
//Database types
|
||||
$databases = array('mysqli' => moodle_database::get_driver_instance('mysqli', 'native'),
|
||||
'auroramysql' => moodle_database::get_driver_instance('auroramysql', 'native'),
|
||||
'mariadb'=> moodle_database::get_driver_instance('mariadb', 'native'),
|
||||
'pgsql' => moodle_database::get_driver_instance('pgsql', 'native'),
|
||||
'oci' => moodle_database::get_driver_instance('oci', 'native'),
|
||||
@@ -256,8 +253,6 @@ list($options, $unrecognized) = cli_get_params(
|
||||
'adminuser' => 'admin',
|
||||
'adminpass' => '',
|
||||
'adminemail' => '',
|
||||
'sitepreset' => '',
|
||||
'supportemail' => '',
|
||||
'upgradekey' => '',
|
||||
'non-interactive' => false,
|
||||
'agree-license' => false,
|
||||
@@ -280,12 +275,6 @@ if (array_key_exists($lang, $languages)) {
|
||||
$CFG->lang = $lang;
|
||||
}
|
||||
|
||||
// Set up site admin preset.
|
||||
$sitepreset = clean_param($options['sitepreset'], PARAM_RAW);
|
||||
if (!empty($sitepreset)) {
|
||||
$CFG->setsitepresetduringinstall = $sitepreset;
|
||||
}
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
@@ -741,20 +730,6 @@ if (!$skipdatabase) {
|
||||
$a = (object)['option' => 'adminemail', 'value' => $options['adminemail']];
|
||||
cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
|
||||
}
|
||||
|
||||
// Ask for the support email address.
|
||||
if ($interactive) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('clisupportemail', 'install'));
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['supportemail']);
|
||||
$options['supportemail'] = cli_input($prompt, $options['supportemail']);
|
||||
}
|
||||
|
||||
// Validate that the support email address provided is valid.
|
||||
if (!empty($options['supportemail']) && !validate_email($options['supportemail'])) {
|
||||
$a = (object)['option' => 'supportemail', 'value' => $options['supportemail']];
|
||||
cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
|
||||
}
|
||||
}
|
||||
|
||||
// Ask for the upgrade key.
|
||||
|
||||
@@ -56,7 +56,6 @@ Options:
|
||||
--agree-license Indicates agreement with software license.
|
||||
--fullname=STRING Name of the site
|
||||
--shortname=STRING Name of the site
|
||||
--summary=STRING The summary to be displayed on the front page
|
||||
-h, --help Print out this help
|
||||
|
||||
Example:
|
||||
@@ -97,7 +96,6 @@ list($options, $unrecognized) = cli_get_params(
|
||||
'adminemail' => '',
|
||||
'fullname' => '',
|
||||
'shortname' => '',
|
||||
'summary' => '',
|
||||
'agree-license' => false,
|
||||
'help' => false
|
||||
),
|
||||
@@ -106,11 +104,6 @@ list($options, $unrecognized) = cli_get_params(
|
||||
)
|
||||
);
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
}
|
||||
|
||||
// We show help text even if tables are installed.
|
||||
if ($options['help']) {
|
||||
echo $help;
|
||||
|
||||
@@ -119,7 +119,7 @@ if (!empty($options['collation'])) {
|
||||
if ($dbcollation->value !== $collation || $dbcharset->value !== $charset) {
|
||||
// Try to convert the DB.
|
||||
echo "Converting database to '$collation' for $CFG->wwwroot:\n";
|
||||
$sql = "ALTER DATABASE `$CFG->dbname` DEFAULT CHARACTER SET $charset DEFAULT COLLATE = $collation";
|
||||
$sql = "ALTER DATABASE $CFG->dbname DEFAULT CHARACTER SET $charset DEFAULT COLLATE = $collation";
|
||||
try {
|
||||
$DB->change_database_structure($sql);
|
||||
} catch (exception $e) {
|
||||
@@ -146,7 +146,7 @@ if (!empty($options['collation'])) {
|
||||
|
||||
} else {
|
||||
try {
|
||||
$DB->change_database_structure("ALTER TABLE `$table->name` CONVERT TO CHARACTER SET $charset COLLATE $collation");
|
||||
$DB->change_database_structure("ALTER TABLE $table->name CONVERT TO CHARACTER SET $charset COLLATE $collation");
|
||||
echo "CONVERTED\n";
|
||||
$converted++;
|
||||
} catch (ddl_exception $e) {
|
||||
@@ -163,7 +163,7 @@ if (!empty($options['collation'])) {
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "SHOW FULL COLUMNS FROM `$table->name` WHERE collation IS NOT NULL";
|
||||
$sql = "SHOW FULL COLUMNS FROM $table->name WHERE collation IS NOT NULL";
|
||||
$rs2 = $DB->get_recordset_sql($sql);
|
||||
foreach ($rs2 as $column) {
|
||||
$column = (object)array_change_key_case((array)$column, CASE_LOWER);
|
||||
@@ -181,7 +181,7 @@ if (!empty($options['collation'])) {
|
||||
$notnull = ($column->null === 'NO') ? 'NOT NULL' : 'NULL';
|
||||
$default = (!is_null($column->default) and $column->default !== '') ? "DEFAULT '$column->default'" : '';
|
||||
// primary, unique and inc are not supported for texts
|
||||
$sql = "ALTER TABLE `$table->name`
|
||||
$sql = "ALTER TABLE $table->name
|
||||
MODIFY COLUMN $column->field $column->type
|
||||
CHARACTER SET $charset
|
||||
COLLATE $collation $notnull $default";
|
||||
@@ -192,11 +192,11 @@ if (!empty($options['collation'])) {
|
||||
$default = !is_null($column->default) ? "DEFAULT '$column->default'" : '';
|
||||
|
||||
if ($rowformat != '') {
|
||||
$sql = "ALTER TABLE `$table->name` $rowformat";
|
||||
$sql = "ALTER TABLE $table->name $rowformat";
|
||||
$DB->change_database_structure($sql);
|
||||
}
|
||||
|
||||
$sql = "ALTER TABLE `$table->name`
|
||||
$sql = "ALTER TABLE $table->name
|
||||
MODIFY COLUMN $column->field $column->type
|
||||
CHARACTER SET $charset
|
||||
COLLATE $collation $notnull $default";
|
||||
@@ -315,8 +315,8 @@ function mysql_set_row_format($tablename, $charset, $collation, $engine) {
|
||||
if ($rs->row_format == 'Compact' || $rs->row_format == 'Redundant') {
|
||||
$rowformat = $DB->get_row_format_sql($engine, $collation);
|
||||
// Try to convert to compressed format and then try updating the collation again.
|
||||
$DB->change_database_structure("ALTER TABLE `$tablename` $rowformat");
|
||||
$DB->change_database_structure("ALTER TABLE `$tablename` CONVERT TO CHARACTER SET $charset COLLATE $collation");
|
||||
$DB->change_database_structure("ALTER TABLE $tablename $rowformat");
|
||||
$DB->change_database_structure("ALTER TABLE $tablename CONVERT TO CHARACTER SET $charset COLLATE $collation");
|
||||
} else {
|
||||
// Row format may not be the problem. Can not diagnose problem. Send fail reply.
|
||||
return false;
|
||||
|
||||
@@ -158,7 +158,7 @@ if (!empty($options['info'])) {
|
||||
}
|
||||
|
||||
foreach ($fixtables as $table) {
|
||||
$DB->change_database_structure("ALTER TABLE `{$prefix}$table` ROW_FORMAT=Compressed");
|
||||
$DB->change_database_structure("ALTER TABLE {$prefix}$table ROW_FORMAT=Compressed");
|
||||
echo str_pad($prefix . $table, 32, ' ', STR_PAD_RIGHT) . " ... Compressed\n";
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ if (!empty($options['info'])) {
|
||||
echo "SET GLOBAL innodb_file_per_table=1;\n";
|
||||
echo "SET GLOBAL innodb_file_format=Barracuda;\n";
|
||||
foreach ($fixtables as $table) {
|
||||
echo "ALTER TABLE `{$prefix}$table` ROW_FORMAT=Compressed;\n";
|
||||
echo "ALTER TABLE {$prefix}$table ROW_FORMAT=Compressed;\n";
|
||||
}
|
||||
echo "\n";
|
||||
exit(0);
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* MySQL engine conversion tool.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage cli
|
||||
* @copyright 2009 Petr Skoda (http://skodak.org)
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__.'/../../config.php');
|
||||
require_once($CFG->libdir.'/clilib.php'); // cli only functions
|
||||
|
||||
if ($DB->get_dbfamily() !== 'mysql') {
|
||||
cli_error('This function is designed for MySQL databases only!');
|
||||
}
|
||||
|
||||
// now get cli options
|
||||
list($options, $unrecognized) = cli_get_params(array('help'=>false, 'list'=>false, 'engine'=>false, 'available'=>false),
|
||||
array('h'=>'help', 'l'=>'list', 'a'=>'available'));
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
}
|
||||
|
||||
$help =
|
||||
"MySQL engine conversions script.
|
||||
|
||||
It is recommended to stop the web server before the conversion.
|
||||
Do not use MyISAM if possible, because it is not ACID compliant
|
||||
and does not support transactions.
|
||||
|
||||
Options:
|
||||
--engine=ENGINE Convert MySQL tables to different engine
|
||||
-l, --list Show table information
|
||||
-a, --available Show list of available engines
|
||||
-h, --help Print out this help
|
||||
|
||||
Example:
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/mysql_engine.php --engine=InnoDB
|
||||
";
|
||||
|
||||
if (!empty($options['engine'])) {
|
||||
$engines = mysql_get_engines();
|
||||
$engine = clean_param($options['engine'], PARAM_ALPHA);
|
||||
if (!isset($engines[strtoupper($engine)])) {
|
||||
cli_error("Error: engine '$engine' is not available on this server!");
|
||||
}
|
||||
|
||||
echo "Converting tables to '$engine' for $CFG->wwwroot:\n";
|
||||
$prefix = $DB->get_prefix();
|
||||
$prefix = str_replace('_', '\\_', $prefix);
|
||||
$sql = "SHOW TABLE STATUS WHERE Name LIKE BINARY '$prefix%'";
|
||||
$rs = $DB->get_recordset_sql($sql);
|
||||
$converted = 0;
|
||||
$skipped = 0;
|
||||
$errors = 0;
|
||||
foreach ($rs as $table) {
|
||||
if (strtoupper($table->engine) === strtoupper($engine)) {
|
||||
$newengine = mysql_get_table_engine($table->name);
|
||||
echo str_pad($table->name, 40). " - NO CONVERSION NEEDED ($newengine)\n";
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
echo str_pad($table->name, 40). " - ";
|
||||
|
||||
try {
|
||||
$DB->change_database_structure("ALTER TABLE {$table->name} ENGINE = $engine");
|
||||
$newengine = mysql_get_table_engine($table->name);
|
||||
if (strtoupper($newengine) !== strtoupper($engine)) {
|
||||
echo "ERROR ($newengine)\n";
|
||||
$errors++;
|
||||
continue;
|
||||
}
|
||||
echo "DONE ($newengine)\n";
|
||||
$converted++;
|
||||
} catch (moodle_exception $e) {
|
||||
echo $e->getMessage()."\n";
|
||||
$errors++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$rs->close();
|
||||
echo "Converted: $converted, skipped: $skipped, errors: $errors\n";
|
||||
exit(0); // success
|
||||
|
||||
} else if (!empty($options['list'])) {
|
||||
echo "List of tables for $CFG->wwwroot:\n";
|
||||
$prefix = $DB->get_prefix();
|
||||
$prefix = str_replace('_', '\\_', $prefix);
|
||||
$sql = "SHOW TABLE STATUS WHERE Name LIKE BINARY '$prefix%'";
|
||||
$rs = $DB->get_recordset_sql($sql);
|
||||
$counts = array();
|
||||
foreach ($rs as $table) {
|
||||
if (isset($counts[$table->engine])) {
|
||||
$counts[$table->engine]++;
|
||||
} else {
|
||||
$counts[$table->engine] = 1;
|
||||
}
|
||||
echo str_pad($table->engine, 10);
|
||||
echo $table->name . "\n";
|
||||
}
|
||||
$rs->close();
|
||||
|
||||
echo "\n";
|
||||
echo "Table engines summary for $CFG->wwwroot:\n";
|
||||
foreach ($counts as $engine => $count) {
|
||||
echo "$engine: $count\n";
|
||||
}
|
||||
exit(0); // success
|
||||
|
||||
} else if (!empty($options['available'])) {
|
||||
echo "List of available MySQL engines for $CFG->wwwroot:\n";
|
||||
$engines = mysql_get_engines();
|
||||
foreach ($engines as $engine) {
|
||||
echo " $engine\n";
|
||||
}
|
||||
die;
|
||||
|
||||
} else {
|
||||
echo $help;
|
||||
die;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ========== Some functions ==============
|
||||
|
||||
function mysql_get_engines() {
|
||||
global $DB;
|
||||
|
||||
$sql = "SHOW Engines";
|
||||
$rs = $DB->get_recordset_sql($sql);
|
||||
$engines = array();
|
||||
foreach ($rs as $engine) {
|
||||
if (strtoupper($engine->support) !== 'YES' and strtoupper($engine->support) !== 'DEFAULT') {
|
||||
continue;
|
||||
}
|
||||
$engines[strtoupper($engine->engine)] = $engine->engine;
|
||||
if (strtoupper($engine->support) === 'DEFAULT') {
|
||||
$engines[strtoupper($engine->engine)] .= ' (default)';
|
||||
}
|
||||
}
|
||||
$rs->close();
|
||||
|
||||
return $engines;
|
||||
}
|
||||
|
||||
function mysql_get_table_engine($tablename) {
|
||||
global $DB;
|
||||
|
||||
$engine = null;
|
||||
$sql = "SHOW TABLE STATUS WHERE Name = '$tablename'"; // no special chars expected here
|
||||
$rs = $DB->get_recordset_sql($sql);
|
||||
if ($rs->valid()) {
|
||||
$record = $rs->current();
|
||||
$engine = $record->engine;
|
||||
}
|
||||
$rs->close();
|
||||
return $engine;
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This script allows to restore a course from CLI.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage cli
|
||||
* @copyright 2020 Catalyst IT
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', 1);
|
||||
|
||||
require(__DIR__ . '/../../config.php');
|
||||
require_once($CFG->libdir . '/clilib.php');
|
||||
require_once($CFG->dirroot . "/backup/util/includes/restore_includes.php");
|
||||
|
||||
list($options, $unrecognized) = cli_get_params([
|
||||
'file' => '',
|
||||
'categoryid' => '',
|
||||
'showdebugging' => false,
|
||||
'help' => false,
|
||||
], [
|
||||
'f' => 'file',
|
||||
'c' => 'categoryid',
|
||||
's' => 'showdebugging',
|
||||
'h' => 'help',
|
||||
]);
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
}
|
||||
|
||||
if ($options['help'] || !($options['file']) || !($options['categoryid'])) {
|
||||
$help = <<<EOL
|
||||
Restore backup into provided category.
|
||||
|
||||
Options:
|
||||
-f, --file=STRING Path to the backup file.
|
||||
-c, --categoryid=INT ID of the category to restore too.
|
||||
-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/file.mbz --categoryid=1\n
|
||||
EOL;
|
||||
|
||||
echo $help;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($options['showdebugging']) {
|
||||
set_debugging(DEBUG_DEVELOPER, true);
|
||||
}
|
||||
|
||||
if (!$admin = get_admin()) {
|
||||
throw new \moodle_exception('noadmins');
|
||||
}
|
||||
|
||||
if (!file_exists($options['file'])) {
|
||||
throw new \moodle_exception('filenotfound');
|
||||
}
|
||||
|
||||
if (!$category = $DB->get_record('course_categories', ['id' => $options['categoryid']], 'id')) {
|
||||
throw new \moodle_exception('invalidcategoryid');
|
||||
}
|
||||
|
||||
$backupdir = "restore_" . uniqid();
|
||||
$path = $CFG->tempdir . DIRECTORY_SEPARATOR . "backup" . DIRECTORY_SEPARATOR . $backupdir;
|
||||
|
||||
cli_heading(get_string('extractingbackupfileto', 'backup', $path));
|
||||
$fp = get_file_packer('application/vnd.moodle.backup');
|
||||
$fp->extract_to_pathname($options['file'], $path);
|
||||
|
||||
cli_heading(get_string('preprocessingbackupfile'));
|
||||
try {
|
||||
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);
|
||||
$rc->execute_precheck();
|
||||
$rc->execute_plan();
|
||||
$rc->destroy();
|
||||
} catch (Exception $e) {
|
||||
cli_heading(get_string('cleaningtempdata'));
|
||||
fulldelete($path);
|
||||
throw new \moodle_exception('generalexceptionmessage', 'error', '', $e->getMessage());
|
||||
}
|
||||
|
||||
cli_heading(get_string('restoredcourseid', 'backup', $courseid));
|
||||
exit(0);
|
||||
@@ -1,167 +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/>.
|
||||
|
||||
/**
|
||||
* CLI task execution.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage cli
|
||||
* @copyright 2014 Petr Skoda
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__ . '/../../config.php');
|
||||
require_once("$CFG->libdir/clilib.php");
|
||||
|
||||
list($options, $unrecognized) = cli_get_params(
|
||||
[
|
||||
'help' => false,
|
||||
'list' => false,
|
||||
'execute' => false,
|
||||
'showsql' => false,
|
||||
'showdebugging' => false,
|
||||
'force' => false,
|
||||
], [
|
||||
'h' => 'help',
|
||||
'f' => 'force',
|
||||
]
|
||||
);
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
}
|
||||
|
||||
if ($options['help'] or (!$options['list'] and !$options['execute'])) {
|
||||
$help =
|
||||
"Scheduled cron tasks.
|
||||
|
||||
Options:
|
||||
--execute=\\some\\task Execute scheduled task manually
|
||||
--list List all scheduled tasks
|
||||
--showsql Show sql queries before they are executed
|
||||
--showdebugging Show developer level debugging information
|
||||
-h, --help Print out this help
|
||||
-f, --force Execute task even if cron is disabled
|
||||
|
||||
Example:
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/scheduled_task.php --execute=\\core\\task\\session_cleanup_task
|
||||
|
||||
";
|
||||
|
||||
echo $help;
|
||||
die;
|
||||
}
|
||||
|
||||
if ($options['showdebugging'] || !empty($CFG->showcrondebugging)) {
|
||||
set_debugging(DEBUG_DEVELOPER, true);
|
||||
}
|
||||
|
||||
if ($options['showsql'] || !empty($CFG->showcronsql)) {
|
||||
$DB->set_debug(true);
|
||||
}
|
||||
if ($options['list']) {
|
||||
cli_heading("List of scheduled tasks ($CFG->wwwroot)");
|
||||
|
||||
$shorttime = get_string('strftimedatetimeshort');
|
||||
|
||||
$tasks = \core\task\manager::get_all_scheduled_tasks();
|
||||
echo str_pad(get_string('scheduledtasks', 'tool_task'), 50, ' ') . ' ' . str_pad(get_string('runpattern', 'tool_task'), 17, ' ')
|
||||
. ' ' . str_pad(get_string('lastruntime', 'tool_task'), 40, ' ') . get_string('nextruntime', 'tool_task') . "\n";
|
||||
foreach ($tasks as $task) {
|
||||
$class = '\\' . get_class($task);
|
||||
$schedule = $task->get_minute() . ' '
|
||||
. $task->get_hour() . ' '
|
||||
. $task->get_day() . ' '
|
||||
. $task->get_day_of_week() . ' '
|
||||
. $task->get_month() . ' '
|
||||
. $task->get_day_of_week();
|
||||
$nextrun = $task->get_next_run_time();
|
||||
$lastrun = $task->get_last_run_time();
|
||||
|
||||
$plugininfo = core_plugin_manager::instance()->get_plugin_info($task->get_component());
|
||||
$plugindisabled = $plugininfo && $plugininfo->is_enabled() === false && !$task->get_run_if_component_disabled();
|
||||
|
||||
if ($plugindisabled) {
|
||||
$nextrun = get_string('plugindisabled', 'tool_task');
|
||||
} else if ($task->get_disabled()) {
|
||||
$nextrun = get_string('taskdisabled', 'tool_task');
|
||||
} else if ($nextrun > time()) {
|
||||
$nextrun = userdate($nextrun);
|
||||
} else {
|
||||
$nextrun = get_string('asap', 'tool_task');
|
||||
}
|
||||
|
||||
if ($lastrun) {
|
||||
$lastrun = userdate($lastrun);
|
||||
} else {
|
||||
$lastrun = get_string('never');
|
||||
}
|
||||
|
||||
echo str_pad($class, 50, ' ') . ' ' . str_pad($schedule, 17, ' ') .
|
||||
' ' . str_pad($lastrun, 40, ' ') . ' ' . $nextrun . "\n";
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($execute = $options['execute']) {
|
||||
if (!$task = \core\task\manager::get_scheduled_task($execute)) {
|
||||
mtrace("Task '$execute' not found");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (moodle_needs_upgrading()) {
|
||||
mtrace("Moodle upgrade pending, cannot execute tasks.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!get_config('core', 'cron_enabled') && !$options['force']) {
|
||||
mtrace('Cron is disabled. Use --force to override.');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
\core\task\manager::scheduled_task_starting($task);
|
||||
|
||||
// Increase memory limit.
|
||||
raise_memory_limit(MEMORY_EXTRA);
|
||||
|
||||
// Emulate normal session - we use admin account by default.
|
||||
\core\cron::setup_user();
|
||||
|
||||
// Execute the task.
|
||||
\core\local\cli\shutdown::script_supports_graceful_exit();
|
||||
$cronlockfactory = \core\lock\lock_config::get_lock_factory('cron');
|
||||
if (!$cronlock = $cronlockfactory->get_lock('core_cron', 10)) {
|
||||
mtrace('Cannot obtain cron lock');
|
||||
exit(129);
|
||||
}
|
||||
if (!$lock = $cronlockfactory->get_lock('\\' . get_class($task), 10)) {
|
||||
$cronlock->release();
|
||||
mtrace('Cannot obtain task lock');
|
||||
exit(130);
|
||||
}
|
||||
|
||||
$task->set_lock($lock);
|
||||
if (!$task->is_blocking()) {
|
||||
$cronlock->release();
|
||||
} else {
|
||||
$task->set_cron_lock($cronlock);
|
||||
}
|
||||
|
||||
\core\cron::run_inner_scheduled_task($task);
|
||||
}
|
||||
@@ -38,17 +38,17 @@ if ($unrecognized) {
|
||||
}
|
||||
|
||||
// If necessary add files that should be ignored - such as in 3rd party plugins.
|
||||
$ignorelist = array();
|
||||
$blacklist = array();
|
||||
$path = $options['path'];
|
||||
if (!file_exists($path)) {
|
||||
cli_error("Invalid path $path");
|
||||
}
|
||||
|
||||
if ($options['ie9fix']) {
|
||||
core_admin_recurse_svgs($path, '', 'core_admin_svgtool_ie9fix', $ignorelist);
|
||||
core_admin_recurse_svgs($path, '', 'core_admin_svgtool_ie9fix', $blacklist);
|
||||
|
||||
} else if ($options['noaspectratio']) {
|
||||
core_admin_recurse_svgs($path, '', 'core_admin_svgtool_noaspectratio', $ignorelist);
|
||||
core_admin_recurse_svgs($path, '', 'core_admin_svgtool_noaspectratio', $blacklist);
|
||||
|
||||
} else {
|
||||
$help =
|
||||
@@ -153,9 +153,9 @@ function core_admin_svgtool_noaspectratio($file) {
|
||||
* @param string $base
|
||||
* @param string $sub
|
||||
* @param string $filecallback
|
||||
* @param array $ignorelist List of files to be ignored and skipped.
|
||||
* @param array $blacklist
|
||||
*/
|
||||
function core_admin_recurse_svgs($base, $sub, $filecallback, $ignorelist) {
|
||||
function core_admin_recurse_svgs($base, $sub, $filecallback, $blacklist) {
|
||||
if (is_dir("$base/$sub")) {
|
||||
$items = new DirectoryIterator("$base/$sub");
|
||||
foreach ($items as $item) {
|
||||
@@ -163,7 +163,7 @@ function core_admin_recurse_svgs($base, $sub, $filecallback, $ignorelist) {
|
||||
continue;
|
||||
}
|
||||
$file = $item->getFilename();
|
||||
core_admin_recurse_svgs("$base/$sub", $file, $filecallback, $ignorelist);
|
||||
core_admin_recurse_svgs("$base/$sub", $file, $filecallback, $blacklist);
|
||||
}
|
||||
unset($item);
|
||||
unset($items);
|
||||
@@ -174,7 +174,7 @@ function core_admin_recurse_svgs($base, $sub, $filecallback, $ignorelist) {
|
||||
return;
|
||||
}
|
||||
$file = realpath("$base/$sub");
|
||||
if (in_array($file, $ignorelist)) {
|
||||
if (in_array($file, $blacklist)) {
|
||||
return;
|
||||
}
|
||||
$filecallback($file);
|
||||
|
||||
@@ -39,8 +39,6 @@ Options:
|
||||
--purge-missing Uninstall all missing from disk plugins.
|
||||
--plugins=<plugin name> A comma separated list of plugins to be uninstalled. E.g. mod_assign,mod_forum
|
||||
--run Execute uninstall. If this option is not set, then the script will be run in a dry mode.
|
||||
--showsql Show sql queries before they are executed.
|
||||
--showdebugging Show developer level debugging information.
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -74,8 +72,6 @@ list($options, $unrecognised) = cli_get_params([
|
||||
'purge-missing' => false,
|
||||
'plugins' => false,
|
||||
'run' => false,
|
||||
'showsql' => false,
|
||||
'showdebugging' => false,
|
||||
], [
|
||||
'h' => 'help'
|
||||
]);
|
||||
@@ -90,14 +86,6 @@ if ($options['help']) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if ($options['showdebugging']) {
|
||||
set_debugging(DEBUG_DEVELOPER, true);
|
||||
}
|
||||
|
||||
if ($options['showsql']) {
|
||||
$DB->set_debug(true);
|
||||
}
|
||||
|
||||
$pluginman = core_plugin_manager::instance();
|
||||
$plugininfo = $pluginman->get_plugins();
|
||||
|
||||
|
||||
+29
-124
@@ -48,16 +48,12 @@ require_once($CFG->libdir.'/environmentlib.php');
|
||||
$lang = isset($SESSION->lang) ? $SESSION->lang : $CFG->lang;
|
||||
list($options, $unrecognized) = cli_get_params(
|
||||
array(
|
||||
'allow-unstable' => false,
|
||||
'help' => false,
|
||||
'is-maintenance-required' => false,
|
||||
'is-pending' => false,
|
||||
'lang' => $lang,
|
||||
'maintenance' => true,
|
||||
'non-interactive' => false,
|
||||
'set-ui-upgrade-lock' => false,
|
||||
'unset-ui-upgrade-lock' => false,
|
||||
'verbose-settings' => false,
|
||||
'non-interactive' => false,
|
||||
'allow-unstable' => false,
|
||||
'help' => false,
|
||||
'lang' => $lang,
|
||||
'verbose-settings' => false,
|
||||
'is-pending' => false,
|
||||
),
|
||||
array(
|
||||
'h' => 'help'
|
||||
@@ -83,35 +79,19 @@ Please note you must execute this script with the same uid as apache!
|
||||
Site defaults may be changed via local/defaults.php.
|
||||
|
||||
Options:
|
||||
--allow-unstable Upgrade even if the version is not marked as stable yet,
|
||||
required in non-interactive mode.
|
||||
-h, --help Print out this help.
|
||||
--is-maintenance-required Returns exit code 2 if the upgrade requires maintenance mode.
|
||||
Returns exit code 3 if no maintenance is required for the upgrade.
|
||||
--is-pending Exit with error code 2 if an upgrade is required.
|
||||
--lang=CODE Set preferred language for CLI output. Defaults to the
|
||||
site language if not set. Defaults to 'en' if the lang
|
||||
parameter is invalid or if the language pack is not
|
||||
installed.
|
||||
--maintenance Sets whether this upgrade will use maintenance mode.
|
||||
If not possible, the upgrade will not happen and the script will exit.
|
||||
WARNING: Caches (except theme) will be STALE and MUST be purged after upgrading.
|
||||
DO NOT USE if the upgrade contains known breaking changes to the way data
|
||||
and the database interact.
|
||||
RECOMMENDED for lightweight deployments, to allow for a graceful purge and
|
||||
rebuild of the cache.
|
||||
--non-interactive No interactive questions or confirmations.
|
||||
--set-ui-upgrade-lock Sets the upgrade to CLI only and unable to be triggered from the frontend.
|
||||
If called with --maintenance=false, the lock WILL NOT be released when the
|
||||
upgrade finishes, and MUST be manually removed.
|
||||
If called with --is-maintenance-required before an upgrade,
|
||||
The lock WILL be released when the upgrade finishes.
|
||||
--unset-ui-upgrade-lock Removes the frontend upgrade lock, if the lock exists.
|
||||
Useful when an error during the upgrade leaves the upgrade locked,
|
||||
or there is need to control the time where the unlock happens.
|
||||
--verbose-settings Show new settings values. By default only the name of
|
||||
new core or plugin settings are displayed. This option
|
||||
outputs the new values as well as the setting name.
|
||||
--non-interactive No interactive questions or confirmations
|
||||
--allow-unstable Upgrade even if the version is not marked as stable yet,
|
||||
required in non-interactive mode.
|
||||
--lang=CODE Set preferred language for CLI output. Defaults to the
|
||||
site language if not set. Defaults to 'en' if the lang
|
||||
parameter is invalid or if the language pack is not
|
||||
installed.
|
||||
--verbose-settings Show new settings values. By default only the name of
|
||||
new core or plugin settings are displayed. This option
|
||||
outputs the new values as well as the setting name.
|
||||
--is-pending If an upgrade is needed it exits with an error code of
|
||||
2 so it distinct from other types of errors.
|
||||
-h, --help Print out this help
|
||||
|
||||
Example:
|
||||
\$sudo -u www-data /usr/bin/php admin/cli/upgrade.php
|
||||
@@ -135,58 +115,12 @@ if ($version < $CFG->version) {
|
||||
$oldversion = "$CFG->release ($CFG->version)";
|
||||
$newversion = "$release ($version)";
|
||||
|
||||
if ($options['unset-ui-upgrade-lock']) {
|
||||
// Unconditionally unset this config if requested.
|
||||
set_config('outagelessupgrade', false);
|
||||
cli_writeln(get_string('cliupgradeunsetlock', 'admin'));
|
||||
}
|
||||
|
||||
$allhash = core_component::get_all_component_hash();
|
||||
|
||||
// Initialise allcomponent hash if not set. It will be correctly set after upgrade.
|
||||
$CFG->allcomponenthash = $CFG->allcomponenthash ?? '';
|
||||
if (!$options['maintenance']) {
|
||||
if ($allhash !== $CFG->allcomponenthash) {
|
||||
// Throw an error here, we can't proceed, this needs to set maintenance.
|
||||
cli_error(get_string('cliupgrademaintenancerequired', 'core_admin'), 2);
|
||||
}
|
||||
|
||||
// Set a constant to stop any upgrade var from being set during processing.
|
||||
// This protects against the upgrade setting timeouts and maintenance during the upgrade.
|
||||
define('CLI_UPGRADE_RUNNING', true);
|
||||
|
||||
// This database control is the control to block the GUI from doing upgrade related actions.
|
||||
set_config('outagelessupgrade', true);
|
||||
}
|
||||
|
||||
// We should ignore all upgrade locks here.
|
||||
if (!moodle_needs_upgrading(false)) {
|
||||
if (!moodle_needs_upgrading()) {
|
||||
cli_error(get_string('cliupgradenoneed', 'core_admin', $newversion), 0);
|
||||
}
|
||||
|
||||
// Handle exit based options for outputting upgrade state.
|
||||
if ($options['is-pending'] || $options['is-maintenance-required']) {
|
||||
// If we aren't doing a maintenance check, plain pending check.
|
||||
if (!$options['is-maintenance-required']) {
|
||||
cli_error(get_string('cliupgradepending', 'core_admin'), 2);
|
||||
}
|
||||
|
||||
// Can we do this safely with no maintenance/outage? Detect if there is a schema or other application state change.
|
||||
if ($allhash !== $CFG->allcomponenthash) {
|
||||
// State change here, we need to do this in maintenance.
|
||||
cli_writeln(get_string('cliupgradepending', 'core_admin'));
|
||||
cli_error(get_string('cliupgrademaintenancerequired', 'core_admin'), 2);
|
||||
}
|
||||
|
||||
// If requested, we should always set the upgrade lock here, so this cannot be run from frontend.
|
||||
if ($options['set-ui-upgrade-lock']) {
|
||||
set_config('outagelessupgrade', true);
|
||||
cli_writeln(get_string('cliupgradesetlock', 'admin'));
|
||||
}
|
||||
|
||||
// We can do an upgrade without maintenance!
|
||||
cli_writeln(get_string('cliupgradepending', 'core_admin'));
|
||||
cli_error(get_string('cliupgrademaintenancenotrequired', 'core_admin'), 3);
|
||||
if ($options['is-pending']) {
|
||||
cli_error(get_string('cliupgradepending', 'core_admin'), 2);
|
||||
}
|
||||
|
||||
// Test environment first.
|
||||
@@ -201,18 +135,9 @@ if (!$envstatus) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Make sure there are no files left over from previous versions.
|
||||
if (upgrade_stale_php_files_present()) {
|
||||
cli_problem(get_string('upgradestalefiles', 'admin'));
|
||||
|
||||
// Stale file info contains HTML elements which aren't suitable for CLI.
|
||||
$upgradestalefilesinfo = get_string('upgradestalefilesinfo', 'admin', get_docs_url('Upgrading'));
|
||||
cli_error(strip_tags($upgradestalefilesinfo));
|
||||
}
|
||||
|
||||
// Test plugin dependencies.
|
||||
$failed = array();
|
||||
if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed, $CFG->branch)) {
|
||||
if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
|
||||
cli_problem(get_string('pluginscheckfailed', 'admin', array('pluginslist' => implode(', ', array_unique($failed)))));
|
||||
cli_error(get_string('pluginschecktodo', 'admin'));
|
||||
}
|
||||
@@ -253,18 +178,12 @@ if ($interactive) {
|
||||
}
|
||||
|
||||
if ($version > $CFG->version) {
|
||||
|
||||
// Only purge caches if this is a plain upgrade.
|
||||
// In the case of a no-outage upgrade, we will gracefully roll caches after upgrade.
|
||||
if ($options['maintenance']) {
|
||||
// We purge all of MUC's caches here.
|
||||
// Caches are disabled for upgrade by CACHE_DISABLE_ALL so we must set the first arg to true.
|
||||
// This ensures a real config object is loaded and the stores will be purged.
|
||||
// This is the only way we can purge custom caches such as memcache or APC.
|
||||
// Note: all other calls to caches will still used the disabled API.
|
||||
cache_helper::purge_all(true);
|
||||
}
|
||||
|
||||
// We purge all of MUC's caches here.
|
||||
// Caches are disabled for upgrade by CACHE_DISABLE_ALL so we must set the first arg to true.
|
||||
// This ensures a real config object is loaded and the stores will be purged.
|
||||
// This is the only way we can purge custom caches such as memcache or APC.
|
||||
// Note: all other calls to caches will still used the disabled API.
|
||||
cache_helper::purge_all(true);
|
||||
upgrade_core($version, true);
|
||||
}
|
||||
set_config('release', $release);
|
||||
@@ -302,18 +221,4 @@ foreach ($settingsoutput as $setting => $value) {
|
||||
upgrade_themes();
|
||||
|
||||
echo get_string('cliupgradefinished', 'admin', $a)."\n";
|
||||
|
||||
if (!$options['maintenance']) {
|
||||
cli_writeln(get_string('cliupgradecompletenomaintenanceupgrade', 'admin'));
|
||||
|
||||
// Here we check if upgrade lock has not been specifically set during this upgrade run.
|
||||
// This supports wider server orchestration actions happening, which should call with no-maintenance AND set-ui-upgrade-lock,
|
||||
// such as a new docker container deployment, of which the moodle upgrade is only a component.
|
||||
if (!$options['set-ui-upgrade-lock']) {
|
||||
// In this case we should release the lock now, as the upgrade is finished.
|
||||
// We weren't told to keep the lock with set-ui-upgrade-lock, so release.
|
||||
set_config('outagelessupgrade', false);
|
||||
}
|
||||
}
|
||||
|
||||
exit(0); // 0 means success
|
||||
|
||||
@@ -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/>.
|
||||
|
||||
/**
|
||||
* Content bank and its plugins settings.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage contentbank
|
||||
* @copyright 2020 Amaia Anabitarte <amaia@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);
|
||||
$name = required_param('name', PARAM_PLUGIN);
|
||||
|
||||
$syscontext = context_system::instance();
|
||||
$PAGE->set_url('/admin/contentbank.php');
|
||||
$PAGE->set_context($syscontext);
|
||||
|
||||
require_admin();
|
||||
require_sesskey();
|
||||
|
||||
$return = new moodle_url('/admin/settings.php', array('section' => 'managecontentbanktypes'));
|
||||
|
||||
$plugins = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
|
||||
$sortorder = array_flip(array_keys($plugins));
|
||||
|
||||
if (!isset($plugins[$name])) {
|
||||
throw new \moodle_exception('contenttypenotfound', 'error', $return, $name);
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'disable':
|
||||
if ($plugins[$name]->is_enabled()) {
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('contenttype');
|
||||
$class::enable_plugin($name, false);
|
||||
}
|
||||
break;
|
||||
case 'enable':
|
||||
if (!$plugins[$name]->is_enabled()) {
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('contenttype');
|
||||
$class::enable_plugin($name, true);
|
||||
}
|
||||
break;
|
||||
case 'up':
|
||||
if ($sortorder[$name]) {
|
||||
$currentindex = $sortorder[$name];
|
||||
$seq = array_keys($plugins);
|
||||
$seq[$currentindex] = $seq[$currentindex - 1];
|
||||
$seq[$currentindex - 1] = $name;
|
||||
set_config('contentbank_plugins_sortorder', implode(',', $seq));
|
||||
core_plugin_manager::reset_caches();
|
||||
}
|
||||
break;
|
||||
case 'down':
|
||||
if ($sortorder[$name] < count($sortorder) - 1) {
|
||||
$currentindex = $sortorder[$name];
|
||||
$seq = array_keys($plugins);
|
||||
$seq[$currentindex] = $seq[$currentindex + 1];
|
||||
$seq[$currentindex + 1] = $name;
|
||||
set_config('contentbank_plugins_sortorder', implode(',', $seq));
|
||||
core_plugin_manager::reset_caches();
|
||||
}
|
||||
break;
|
||||
}
|
||||
$cache = cache::make('core', 'contentbank_enabled_extensions');
|
||||
$cache->purge();
|
||||
$cache = cache::make('core', 'contentbank_context_extensions');
|
||||
$cache->purge();
|
||||
|
||||
redirect($return);
|
||||
@@ -1,79 +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/>.
|
||||
|
||||
/**
|
||||
* Reset course indentation
|
||||
*
|
||||
* @copyright 2023 Amaia Anabitarte <amaia@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once('../../config.php');
|
||||
|
||||
require_admin();
|
||||
|
||||
$format = required_param('format', PARAM_PLUGIN);
|
||||
$confirm = optional_param('confirm', false, PARAM_BOOL);
|
||||
$backurl = new moodle_url('/admin/settings.php', ['section' => 'formatsetting'.$format]);
|
||||
|
||||
$PAGE->set_url('/admin/course/resetindentation.php', ['format' => $format]);
|
||||
$PAGE->set_context(context_system::instance());
|
||||
|
||||
if ($confirm) {
|
||||
require_sesskey();
|
||||
$courses = $DB->get_records('course', ['format' => $format], 'id', 'id');
|
||||
if (!empty($courses)) {
|
||||
$courseids = array_keys($courses);
|
||||
list($courseinsql, $courseparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED, 'course');
|
||||
$DB->set_field_select('course_modules', 'indent', '0', "course $courseinsql AND indent <> 0", $courseparams);
|
||||
rebuild_course_cache(0, true);
|
||||
}
|
||||
redirect(
|
||||
$backurl,
|
||||
get_string('resetindentationsuccess', 'admin'),
|
||||
null,
|
||||
\core\output\notification::NOTIFY_SUCCESS
|
||||
);
|
||||
}
|
||||
|
||||
$strtitle = get_string('resetindentation', 'admin');
|
||||
|
||||
$PAGE->set_title($strtitle);
|
||||
$PAGE->set_heading($strtitle);
|
||||
|
||||
navigation_node::override_active_url(new moodle_url(
|
||||
'/admin/course/resetindentation.php',
|
||||
['action' => 'confirm', 'format' => $format]
|
||||
));
|
||||
|
||||
echo $OUTPUT->header();
|
||||
|
||||
$displayoptions = ['confirmtitle' => get_string('resetindentation_title', 'admin')];
|
||||
$confirmbutton = new single_button(
|
||||
new moodle_url('/admin/course/resetindentation.php', ['confirm' => 1, 'format' => $format, 'sesskey' => sesskey()]),
|
||||
get_string('resetindentation', 'admin'),
|
||||
'post',
|
||||
single_button::BUTTON_DANGER
|
||||
);
|
||||
$cancelbutton = new single_button($backurl, get_string('cancel'));
|
||||
echo $OUTPUT->confirm(
|
||||
get_string('resetindentation_help', 'admin', ['format' => get_string('pluginname', 'format_'.$format)]),
|
||||
$confirmbutton,
|
||||
$cancelbutton,
|
||||
$displayoptions
|
||||
);
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
@@ -41,20 +41,23 @@ $formatplugins = core_plugin_manager::instance()->get_plugins_of_type('format');
|
||||
$sortorder = array_flip(array_keys($formatplugins));
|
||||
|
||||
if (!isset($formatplugins[$formatname])) {
|
||||
throw new \moodle_exception('courseformatnotfound', 'error', $return, $formatname);
|
||||
print_error('courseformatnotfound', 'error', $return, $formatname);
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'disable':
|
||||
if ($formatplugins[$formatname]->is_enabled()) {
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('format');
|
||||
$class::enable_plugin($formatname, false);
|
||||
if (get_config('moodlecourse', 'format') === $formatname) {
|
||||
print_error('cannotdisableformat', 'error', $return);
|
||||
}
|
||||
set_config('disabled', 1, 'format_'. $formatname);
|
||||
core_plugin_manager::reset_caches();
|
||||
}
|
||||
break;
|
||||
case 'enable':
|
||||
if (!$formatplugins[$formatname]->is_enabled()) {
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('format');
|
||||
$class::enable_plugin($formatname, true);
|
||||
unset_config('disabled', 'format_'. $formatname);
|
||||
core_plugin_manager::reset_caches();
|
||||
}
|
||||
break;
|
||||
case 'up':
|
||||
|
||||
+9
-8
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@@ -35,7 +36,6 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
// phpcs:ignoreFile moodle.Files.MoodleInternal.MoodleInternalGlobalState
|
||||
|
||||
if (defined('STDIN')) {
|
||||
fwrite(STDERR, "ERROR: This script no longer supports CLI, please use admin/cli/cron.php instead\n");
|
||||
@@ -49,7 +49,8 @@ define('WEB_CRON_EMULATED_CLI', 'defined'); // ugly ugly hack, do not use elsewh
|
||||
define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
require('../config.php');
|
||||
require_once($CFG->libdir . '/clilib.php');
|
||||
require_once($CFG->libdir.'/clilib.php');
|
||||
require_once($CFG->libdir.'/cronlib.php');
|
||||
|
||||
// extra safety
|
||||
\core\session\manager::write_close();
|
||||
@@ -57,7 +58,7 @@ require_once($CFG->libdir . '/clilib.php');
|
||||
// check if execution allowed
|
||||
if (!empty($CFG->cronclionly)) {
|
||||
// This script can only be run via the cli.
|
||||
throw new \moodle_exception('cronerrorclionly', 'admin');
|
||||
print_error('cronerrorclionly', 'admin');
|
||||
exit;
|
||||
}
|
||||
// This script is being called via the web, so check the password if there is one.
|
||||
@@ -65,16 +66,16 @@ if (!empty($CFG->cronremotepassword)) {
|
||||
$pass = optional_param('password', '', PARAM_RAW);
|
||||
if ($pass != $CFG->cronremotepassword) {
|
||||
// wrong password.
|
||||
throw new \moodle_exception('cronerrorpassword', 'admin');
|
||||
print_error('cronerrorpassword', 'admin');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Send mime type and encoding.
|
||||
// send mime type and encoding
|
||||
@header('Content-Type: text/plain; charset=utf-8');
|
||||
|
||||
// We do not want html markup in emulated CLI.
|
||||
// we do not want html markup in emulated CLI
|
||||
@ini_set('html_errors', 'off');
|
||||
|
||||
// Execute the cron, disabling keepalive.
|
||||
\core\cron::run_main_process(0);
|
||||
// execute the cron
|
||||
cron_run();
|
||||
|
||||
@@ -41,20 +41,20 @@ $customfieldplugins = core_plugin_manager::instance()->get_plugins_of_type('cust
|
||||
$sortorder = array_flip(array_keys($customfieldplugins));
|
||||
|
||||
if (!isset($customfieldplugins[$customfieldname])) {
|
||||
throw new \moodle_exception('customfieldnotfound', 'error', $return, $customfieldname);
|
||||
print_error('customfieldnotfound', 'error', $return, $customfieldname);
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'disable':
|
||||
if ($customfieldplugins[$customfieldname]->is_enabled()) {
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('customfield');
|
||||
$class::enable_plugin($customfieldname, false);
|
||||
set_config('disabled', 1, 'customfield_'. $customfieldname);
|
||||
core_plugin_manager::reset_caches();
|
||||
}
|
||||
break;
|
||||
case 'enable':
|
||||
if (!$customfieldplugins[$customfieldname]->is_enabled()) {
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('customfield');
|
||||
$class::enable_plugin($customfieldname, true);
|
||||
unset_config('disabled', 'customfield_'. $customfieldname);
|
||||
core_plugin_manager::reset_caches();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -42,20 +42,20 @@ $plugins = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
|
||||
$sortorder = array_flip(array_keys($plugins));
|
||||
|
||||
if (!isset($plugins[$name])) {
|
||||
throw new \moodle_exception('courseformatnotfound', 'error', $return, $name);
|
||||
print_error('courseformatnotfound', 'error', $return, $name);
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'disable':
|
||||
if ($plugins[$name]->is_enabled()) {
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('dataformat');
|
||||
$class::enable_plugin($name, false);
|
||||
set_config('disabled', 1, 'dataformat_'. $name);
|
||||
core_plugin_manager::reset_caches();
|
||||
}
|
||||
break;
|
||||
case 'enable':
|
||||
if (!$plugins[$name]->is_enabled()) {
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('dataformat');
|
||||
$class::enable_plugin($name, true);
|
||||
unset_config('disabled', 'dataformat_'. $name);
|
||||
core_plugin_manager::reset_caches();
|
||||
}
|
||||
break;
|
||||
case 'up':
|
||||
@@ -78,3 +78,4 @@ switch ($action) {
|
||||
break;
|
||||
}
|
||||
redirect($return);
|
||||
|
||||
|
||||
+60
-59
@@ -1,96 +1,87 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* A page to manage editor plugins.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* Allows admin to configure editors.
|
||||
*/
|
||||
|
||||
require_once('../config.php');
|
||||
require_once($CFG->libdir . '/adminlib.php');
|
||||
require_once($CFG->libdir . '/tablelib.php');
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
require_once($CFG->libdir.'/tablelib.php');
|
||||
|
||||
$action = required_param('action', PARAM_ALPHANUMEXT);
|
||||
$plugin = required_param('plugin', PARAM_PLUGIN);
|
||||
$action = required_param('action', PARAM_ALPHANUMEXT);
|
||||
$editor = required_param('editor', PARAM_PLUGIN);
|
||||
$confirm = optional_param('confirm', 0, PARAM_BOOL);
|
||||
|
||||
$PAGE->set_url('/admin/editors.php', ['action' => $action, 'editor' => $plugin]);
|
||||
$PAGE->set_url('/admin/editors.php', array('action'=>$action, 'editor'=>$editor));
|
||||
$PAGE->set_context(context_system::instance());
|
||||
|
||||
require_admin();
|
||||
require_sesskey();
|
||||
|
||||
$returnurl = new moodle_url('/admin/settings.php', ['section' => 'manageeditors']);
|
||||
$returnurl = "$CFG->wwwroot/$CFG->admin/settings.php?section=manageeditors";
|
||||
|
||||
// Get currently installed and enabled auth plugins.
|
||||
$availableeditors = editors_get_available();
|
||||
if (!empty($plugin) && empty($availableeditors[$plugin])) {
|
||||
redirect($returnurl);
|
||||
// get currently installed and enabled auth plugins
|
||||
$available_editors = editors_get_available();
|
||||
if (!empty($editor) and empty($available_editors[$editor])) {
|
||||
redirect ($returnurl);
|
||||
}
|
||||
|
||||
$activeeditors = explode(',', $CFG->texteditors);
|
||||
foreach ($activeeditors as $key => $active) {
|
||||
if (empty($availableeditors[$active])) {
|
||||
unset($activeeditors[$key]);
|
||||
$active_editors = explode(',', $CFG->texteditors);
|
||||
foreach ($active_editors as $key=>$active) {
|
||||
if (empty($available_editors[$active])) {
|
||||
unset($active_editors[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// process actions
|
||||
|
||||
if (!confirm_sesskey()) {
|
||||
redirect($returnurl);
|
||||
}
|
||||
|
||||
|
||||
$return = true;
|
||||
switch ($action) {
|
||||
case 'disable':
|
||||
// Remove from enabled list.
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('editor');
|
||||
$class::enable_plugin($plugin, false);
|
||||
// remove from enabled list
|
||||
$key = array_search($editor, $active_editors);
|
||||
unset($active_editors[$key]);
|
||||
add_to_config_log('editor_visibility', '1', '0', $editor);
|
||||
break;
|
||||
|
||||
case 'enable':
|
||||
// Add to enabled list.
|
||||
if (!in_array($plugin, $activeeditors)) {
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('editor');
|
||||
$class::enable_plugin($plugin, true);
|
||||
// add to enabled list
|
||||
if (!in_array($editor, $active_editors)) {
|
||||
$active_editors[] = $editor;
|
||||
$active_editors = array_unique($active_editors);
|
||||
add_to_config_log('editor_visibility', '0', '1', $editor);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'down':
|
||||
$key = array_search($plugin, $activeeditors);
|
||||
$key = array_search($editor, $active_editors);
|
||||
// check auth plugin is valid
|
||||
if ($key !== false) {
|
||||
// Move down the list.
|
||||
if ($key < (count($activeeditors) - 1)) {
|
||||
$fsave = $activeeditors[$key];
|
||||
$activeeditors[$key] = $activeeditors[$key + 1];
|
||||
$activeeditors[$key + 1] = $fsave;
|
||||
add_to_config_log('editor_position', $key, $key + 1, $plugin);
|
||||
set_config('texteditors', implode(',', $activeeditors));
|
||||
core_plugin_manager::reset_caches();
|
||||
// move down the list
|
||||
if ($key < (count($active_editors) - 1)) {
|
||||
$fsave = $active_editors[$key];
|
||||
$active_editors[$key] = $active_editors[$key + 1];
|
||||
$active_editors[$key + 1] = $fsave;
|
||||
add_to_config_log('editor_position', $key, $key + 1, $editor);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'up':
|
||||
$key = array_search($plugin, $activeeditors);
|
||||
$key = array_search($editor, $active_editors);
|
||||
// check auth is valid
|
||||
if ($key !== false) {
|
||||
// Move up the list.
|
||||
// move up the list
|
||||
if ($key >= 1) {
|
||||
$fsave = $activeeditors[$key];
|
||||
$activeeditors[$key] = $activeeditors[$key - 1];
|
||||
$activeeditors[$key - 1] = $fsave;
|
||||
add_to_config_log('editor_position', $key, $key - 1, $plugin);
|
||||
set_config('texteditors', implode(',', $activeeditors));
|
||||
core_plugin_manager::reset_caches();
|
||||
$fsave = $active_editors[$key];
|
||||
$active_editors[$key] = $active_editors[$key - 1];
|
||||
$active_editors[$key - 1] = $fsave;
|
||||
add_to_config_log('editor_position', $key, $key - 1, $editor);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -99,4 +90,14 @@ switch ($action) {
|
||||
break;
|
||||
}
|
||||
|
||||
redirect($returnurl);
|
||||
// at least one editor must be active
|
||||
if (empty($active_editors)) {
|
||||
$active_editors = array('textarea');
|
||||
}
|
||||
|
||||
set_config('texteditors', implode(',', $active_editors));
|
||||
core_plugin_manager::reset_caches();
|
||||
|
||||
if ($return) {
|
||||
redirect ($returnurl);
|
||||
}
|
||||
|
||||
+12
-5
@@ -43,18 +43,25 @@ $all = enrol_get_plugins(false);
|
||||
|
||||
$return = new moodle_url('/admin/settings.php', array('section'=>'manageenrols'));
|
||||
|
||||
$syscontext = context_system::instance();
|
||||
|
||||
switch ($action) {
|
||||
case 'disable':
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('enrol');
|
||||
$class::enable_plugin($enrol, false);
|
||||
unset($enabled[$enrol]);
|
||||
set_config('enrol_plugins_enabled', implode(',', array_keys($enabled)));
|
||||
core_plugin_manager::reset_caches();
|
||||
$syscontext->mark_dirty(); // resets all enrol caches
|
||||
break;
|
||||
|
||||
case 'enable':
|
||||
if (!isset($all[$enrol])) {
|
||||
break;
|
||||
}
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('enrol');
|
||||
$class::enable_plugin($enrol, true);
|
||||
$enabled = array_keys($enabled);
|
||||
$enabled[] = $enrol;
|
||||
set_config('enrol_plugins_enabled', implode(',', $enabled));
|
||||
core_plugin_manager::reset_caches();
|
||||
$syscontext->mark_dirty(); // resets all enrol caches
|
||||
break;
|
||||
|
||||
case 'up':
|
||||
@@ -122,4 +129,4 @@ switch ($action) {
|
||||
}
|
||||
|
||||
|
||||
redirect($return);
|
||||
redirect($return);
|
||||
@@ -55,11 +55,11 @@ if ($action == 'updatecomponent' && confirm_sesskey()) {
|
||||
$a = new stdClass();
|
||||
$a->url = 'https://download.moodle.org/environment/environment.zip';
|
||||
$a->dest = $CFG->dataroot . '/';
|
||||
throw new \moodle_exception($cd->get_error(), 'error', $PAGE->url, $a);
|
||||
print_error($cd->get_error(), 'error', $PAGE->url, $a);
|
||||
die();
|
||||
|
||||
} else {
|
||||
throw new \moodle_exception($cd->get_error(), 'error', $PAGE->url);
|
||||
print_error($cd->get_error(), 'error', $PAGE->url);
|
||||
die();
|
||||
}
|
||||
|
||||
|
||||
+2
-586
@@ -3163,11 +3163,6 @@
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_igbinary322_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="igbinary322buggyversion" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.10" requires="3.5">
|
||||
@@ -3350,11 +3345,6 @@
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_igbinary322_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="igbinary322buggyversion" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.11" requires="3.6">
|
||||
@@ -3371,7 +3361,6 @@
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.3.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_81" message="unsupportedphpversion81" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
@@ -3458,569 +3447,7 @@
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
<PHP_EXTENSION name="sodium" 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="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</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_xmlrpc_usage" level="optional">
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="4.0" requires="3.6">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="10.2.29" />
|
||||
<VENDOR name="mysql" version="5.7" />
|
||||
<VENDOR name="postgres" version="10" />
|
||||
<VENDOR name="mssql" version="14.0" />
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.3.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_81" message="unsupportedphpversion81" />
|
||||
</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="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
<PHP_EXTENSION name="sodium" level="optional"/>
|
||||
<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="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</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_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="4.1" requires="3.9">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="10.4" />
|
||||
<VENDOR name="mysql" version="5.7" />
|
||||
<VENDOR name="postgres" version="12" />
|
||||
<VENDOR name="mssql" version="14.0" />
|
||||
<VENDOR name="oracle" version="19" />
|
||||
</DATABASE>
|
||||
<PHP version="7.4.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="optional"/>
|
||||
<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="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</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_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="4.2" requires="3.11.8">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="10.6.7" />
|
||||
<VENDOR name="mysql" version="8.0" />
|
||||
<VENDOR name="postgres" version="13" />
|
||||
<VENDOR name="mssql" version="14.0" />
|
||||
<VENDOR name="oracle" version="19" />
|
||||
</DATABASE>
|
||||
<PHP version="8.0.0" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="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">
|
||||
@@ -4095,22 +3522,11 @@
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="required">
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="sixtyfourbitsrequired" />
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</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_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
+33
-43
@@ -31,15 +31,14 @@ $filterpath = optional_param('filterpath', '', PARAM_PLUGIN);
|
||||
admin_externalpage_setup('managefilters');
|
||||
|
||||
// Clean up bogus filter states first.
|
||||
/** @var core\plugininfo\filter[] $plugininfos */
|
||||
$plugininfos = core_plugin_manager::instance()->get_plugins_of_type('filter');
|
||||
$filters = [];
|
||||
$filters = array();
|
||||
$states = filter_get_global_states();
|
||||
foreach ($states as $state) {
|
||||
if (!isset($plugininfos[$state->filter]) and !get_config('filter_'.$state->filter, 'version')) {
|
||||
// Purge messy leftovers after incorrectly uninstalled plugins and unfinished installations.
|
||||
$DB->delete_records('filter_active', ['filter' => $state->filter]);
|
||||
$DB->delete_records('filter_config', ['filter' => $state->filter]);
|
||||
// Purge messy leftovers after incorrectly uninstalled plugins and unfinished installs.
|
||||
$DB->delete_records('filter_active', array('filter' => $state->filter));
|
||||
$DB->delete_records('filter_config', array('filter' => $state->filter));
|
||||
error_log('Deleted bogus "filter_'.$state->filter.'" states and config data.');
|
||||
} else {
|
||||
$filters[$state->filter] = $state;
|
||||
@@ -51,6 +50,7 @@ foreach ($plugininfos as $filter => $info) {
|
||||
if (isset($filters[$filter])) {
|
||||
continue;
|
||||
}
|
||||
/** @var \core\plugininfo\base $info */
|
||||
if ($info->is_installed_and_upgraded()) {
|
||||
filter_set_global_state($filter, TEXTFILTER_DISABLED);
|
||||
$states = filter_get_global_states();
|
||||
@@ -72,9 +72,10 @@ switch ($action) {
|
||||
|
||||
case 'setstate':
|
||||
if (isset($filters[$filterpath]) and $newstate = optional_param('newstate', '', PARAM_INT)) {
|
||||
/** @var \core\plugininfo\filter $class */
|
||||
$class = core_plugin_manager::resolve_plugininfo_class('filter');
|
||||
$class::enable_plugin($filterpath, $newstate);
|
||||
filter_set_global_state($filterpath, $newstate);
|
||||
if ($newstate == TEXTFILTER_DISABLED) {
|
||||
filter_set_applies_to_strings($filterpath, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -82,16 +83,12 @@ switch ($action) {
|
||||
if (isset($filters[$filterpath])) {
|
||||
$applytostrings = optional_param('stringstoo', false, PARAM_BOOL);
|
||||
filter_set_applies_to_strings($filterpath, $applytostrings);
|
||||
reset_text_filters_cache();
|
||||
core_plugin_manager::reset_caches();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'down':
|
||||
if (isset($filters[$filterpath])) {
|
||||
filter_set_global_state($filterpath, $filters[$filterpath]->active, 1);
|
||||
reset_text_filters_cache();
|
||||
core_plugin_manager::reset_caches();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -99,14 +96,14 @@ switch ($action) {
|
||||
if (isset($filters[$filterpath])) {
|
||||
$oldpos = $filters[$filterpath]->sortorder;
|
||||
filter_set_global_state($filterpath, $filters[$filterpath]->active, -1);
|
||||
reset_text_filters_cache();
|
||||
core_plugin_manager::reset_caches();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Return.
|
||||
// Reset caches and return.
|
||||
if ($action) {
|
||||
reset_text_filters_cache();
|
||||
core_plugin_manager::reset_caches();
|
||||
redirect(new moodle_url('/admin/filters.php'));
|
||||
}
|
||||
|
||||
@@ -118,12 +115,12 @@ $states = filter_get_global_states();
|
||||
$stringfilters = filter_get_string_filters();
|
||||
|
||||
$table = new html_table();
|
||||
$table->head = [get_string('filter'), get_string('isactive', 'filters'),
|
||||
get_string('order'), get_string('applyto', 'filters'), get_string('settings'), get_string('uninstallplugin', 'core_admin')];
|
||||
$table->head = array(get_string('filter'), get_string('isactive', 'filters'),
|
||||
get_string('order'), get_string('applyto', 'filters'), get_string('settings'), get_string('uninstallplugin', 'core_admin'));
|
||||
$table->colclasses = array ('leftalign', 'leftalign', 'centeralign', 'leftalign', 'leftalign', 'leftalign');
|
||||
$table->attributes['class'] = 'admintable generaltable';
|
||||
$table->id = 'filterssetting';
|
||||
$table->data = [];
|
||||
$table->data = array();
|
||||
|
||||
$lastactive = null;
|
||||
foreach ($states as $state) {
|
||||
@@ -160,16 +157,15 @@ die;
|
||||
/**
|
||||
* Return action URL.
|
||||
*
|
||||
* @param string $filterpath which filter to get the URL for.
|
||||
* @param string $action which action to get the URL for.
|
||||
* @return moodle_url|null the requested URL.
|
||||
* @param string $filterpath
|
||||
* @param string $action
|
||||
* @return moodle_url
|
||||
*/
|
||||
function filters_action_url(string $filterpath, string $action): ?moodle_url {
|
||||
function filters_action_url($filterpath, $action) {
|
||||
if ($action === 'delete') {
|
||||
return core_plugin_manager::instance()->get_uninstall_url('filter_'.$filterpath, 'manage');
|
||||
}
|
||||
return new moodle_url('/admin/filters.php',
|
||||
['sesskey' => sesskey(), 'filterpath' => $filterpath, 'action' => $action]);
|
||||
return new moodle_url('/admin/filters.php', array('sesskey'=>sesskey(), 'filterpath'=>$filterpath, 'action'=>$action));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,25 +178,24 @@ function filters_action_url(string $filterpath, string $action): ?moodle_url {
|
||||
* @param bool $applytostrings
|
||||
* @return array data
|
||||
*/
|
||||
function get_table_row(\core\plugininfo\filter $plugininfo, stdClass $state,
|
||||
bool $isfirstrow, bool $islastactive, bool $applytostrings): array {
|
||||
function get_table_row(\core\plugininfo\filter $plugininfo, $state, $isfirstrow, $islastactive, $applytostrings) {
|
||||
global $OUTPUT;
|
||||
$row = [];
|
||||
$row = array();
|
||||
$filter = $state->filter;
|
||||
$active = $plugininfo->is_installed_and_upgraded();
|
||||
|
||||
static $activechoices;
|
||||
static $applytochoices;
|
||||
if (!isset($activechoices)) {
|
||||
$activechoices = [
|
||||
$activechoices = array(
|
||||
TEXTFILTER_DISABLED => get_string('disabled', 'core_filters'),
|
||||
TEXTFILTER_OFF => get_string('offbutavailable', 'core_filters'),
|
||||
TEXTFILTER_ON => get_string('on', 'core_filters'),
|
||||
];
|
||||
$applytochoices = [
|
||||
);
|
||||
$applytochoices = array(
|
||||
0 => get_string('content', 'core_filters'),
|
||||
1 => get_string('contentandheadings', 'core_filters'),
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
// Filter name.
|
||||
@@ -214,7 +209,7 @@ function get_table_row(\core\plugininfo\filter $plugininfo, stdClass $state,
|
||||
|
||||
// Disable/off/on.
|
||||
$select = new single_select(filters_action_url($filter, 'setstate'), 'newstate', $activechoices, $state->active, null, 'active' . $filter);
|
||||
$select->set_label(get_string('isactive', 'filters'), ['class' => 'accesshide']);
|
||||
$select->set_label(get_string('isactive', 'filters'), array('class' => 'accesshide'));
|
||||
$row[] = $OUTPUT->render($select);
|
||||
|
||||
// Re-order.
|
||||
@@ -222,14 +217,12 @@ function get_table_row(\core\plugininfo\filter $plugininfo, stdClass $state,
|
||||
$spacer = $OUTPUT->spacer();
|
||||
if ($state->active != TEXTFILTER_DISABLED) {
|
||||
if (!$isfirstrow) {
|
||||
$updown .= $OUTPUT->action_icon(filters_action_url($filter, 'up'),
|
||||
new pix_icon('t/up', get_string('up'), '', ['class' => 'iconsmall']));
|
||||
$updown .= $OUTPUT->action_icon(filters_action_url($filter, 'up'), new pix_icon('t/up', get_string('up'), '', array('class' => 'iconsmall')));
|
||||
} else {
|
||||
$updown .= $spacer;
|
||||
}
|
||||
if (!$islastactive) {
|
||||
$updown .= $OUTPUT->action_icon(filters_action_url($filter, 'down'),
|
||||
new pix_icon('t/down', get_string('down'), '', ['class' => 'iconsmall']));
|
||||
$updown .= $OUTPUT->action_icon(filters_action_url($filter, 'down'), new pix_icon('t/down', get_string('down'), '', array('class' => 'iconsmall')));
|
||||
} else {
|
||||
$updown .= $spacer;
|
||||
}
|
||||
@@ -237,23 +230,20 @@ function get_table_row(\core\plugininfo\filter $plugininfo, stdClass $state,
|
||||
$row[] = $updown;
|
||||
|
||||
// Apply to strings.
|
||||
$select = new single_select(filters_action_url($filter, 'setapplyto'),
|
||||
'stringstoo', $applytochoices, $applytostrings, null, 'applyto' . $filter);
|
||||
$select->set_label(get_string('applyto', 'filters'), ['class' => 'accesshide']);
|
||||
$select = new single_select(filters_action_url($filter, 'setapplyto'), 'stringstoo', $applytochoices, $applytostrings, null, 'applyto' . $filter);
|
||||
$select->set_label(get_string('applyto', 'filters'), array('class' => 'accesshide'));
|
||||
$select->disabled = ($state->active == TEXTFILTER_DISABLED);
|
||||
$row[] = $OUTPUT->render($select);
|
||||
|
||||
// Settings link, if required.
|
||||
if ($active and filter_has_global_settings($filter)) {
|
||||
$row[] = html_writer::link(new moodle_url('/admin/settings.php',
|
||||
['section' => 'filtersetting'.$filter]), get_string('settings'));
|
||||
$row[] = html_writer::link(new moodle_url('/admin/settings.php', array('section'=>'filtersetting'.$filter)), get_string('settings'));
|
||||
} else {
|
||||
$row[] = '';
|
||||
}
|
||||
|
||||
// Uninstall.
|
||||
$row[] = html_writer::link(filters_action_url($filter, 'delete'),
|
||||
get_string('uninstallplugin', 'core_admin'));
|
||||
$row[] = html_writer::link(filters_action_url($filter, 'delete'), get_string('uninstallplugin', 'core_admin'));
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
+326
-366
@@ -53,12 +53,6 @@ if (!extension_loaded('xml')) {
|
||||
die();
|
||||
}
|
||||
|
||||
// Make sure mbstring extension is available.
|
||||
if (!extension_loaded('mbstring')) {
|
||||
echo 'Moodle requires the mbstring PHP extension. Please install or enable the mbstring extension.';
|
||||
die();
|
||||
}
|
||||
|
||||
define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
if (isset($_POST['upgradekey'])) {
|
||||
@@ -174,17 +168,15 @@ $documentationlink = '<a href="http://docs.moodle.org/en/Installation">Installat
|
||||
// Check some PHP server settings
|
||||
|
||||
if (ini_get_bool('session.auto_start')) {
|
||||
throw new \moodle_exception('phpvaroff', 'debug', '',
|
||||
(object)array('name' => 'session.auto_start', 'link' => $documentationlink));
|
||||
print_error('phpvaroff', 'debug', '', (object)array('name'=>'session.auto_start', 'link'=>$documentationlink));
|
||||
}
|
||||
|
||||
if (!ini_get_bool('file_uploads')) {
|
||||
throw new \moodle_exception('phpvaron', 'debug', '',
|
||||
(object)array('name' => 'file_uploads', 'link' => $documentationlink));
|
||||
print_error('phpvaron', 'debug', '', (object)array('name'=>'file_uploads', 'link'=>$documentationlink));
|
||||
}
|
||||
|
||||
if (is_float_problem()) {
|
||||
throw new \moodle_exception('phpfloatproblem', 'admin', '', $documentationlink);
|
||||
print_error('phpfloatproblem', 'admin', '', $documentationlink);
|
||||
}
|
||||
|
||||
// Set some necessary variables during set-up to avoid PHP warnings later on this page
|
||||
@@ -205,7 +197,7 @@ require("$CFG->dirroot/version.php"); // defines $version, $release, $bran
|
||||
$CFG->target_release = $release; // used during installation and upgrades
|
||||
|
||||
if (!$version or !$release) {
|
||||
throw new \moodle_exception('withoutversion', 'debug'); // Without version, stop.
|
||||
print_error('withoutversion', 'debug'); // without version, stop
|
||||
}
|
||||
|
||||
if (!core_tables_exist()) {
|
||||
@@ -249,7 +241,7 @@ if (!core_tables_exist()) {
|
||||
|
||||
// check plugin dependencies
|
||||
$failed = array();
|
||||
if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed, $CFG->branch)) {
|
||||
if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
|
||||
$PAGE->navbar->add(get_string('pluginscheck', 'admin'));
|
||||
$PAGE->set_title($strinstallation);
|
||||
$PAGE->set_heading($strinstallation . ' - Moodle ' . $CFG->target_release);
|
||||
@@ -277,7 +269,7 @@ if (!core_tables_exist()) {
|
||||
if (!$DB->setup_is_unicodedb()) {
|
||||
if (!$DB->change_db_encoding()) {
|
||||
// If could not convert successfully, throw error, and prevent installation
|
||||
throw new \moodle_exception('unicoderequired', 'admin');
|
||||
print_error('unicoderequired', 'admin');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,80 +291,265 @@ $stradministration = get_string('administration');
|
||||
$PAGE->set_context(context_system::instance());
|
||||
|
||||
if (empty($CFG->version)) {
|
||||
throw new \moodle_exception('missingconfigversion', 'debug');
|
||||
print_error('missingconfigversion', 'debug');
|
||||
}
|
||||
|
||||
// If an upgrade is running, an admin page starting a frontend upgrade could corrupt the
|
||||
// DB if the upgrade collided with an already running upgrade process at the wrong time.
|
||||
// Pull the value direct from the DB, this needs to *always* be correct.
|
||||
$outagelessupgrade = !empty($DB->get_field('config', 'value', ['name' => 'outagelessupgrade']));
|
||||
if (!$outagelessupgrade) {
|
||||
// Detect config cache inconsistency, this happens when you switch branches on dev servers.
|
||||
if ($CFG->version != $DB->get_field('config', 'value', array('name' => 'version'))) {
|
||||
purge_all_caches();
|
||||
redirect(new moodle_url($PAGE->url), 'Config cache inconsistency detected, resetting caches...');
|
||||
// Detect config cache inconsistency, this happens when you switch branches on dev servers.
|
||||
if ($CFG->version != $DB->get_field('config', 'value', array('name'=>'version'))) {
|
||||
purge_all_caches();
|
||||
redirect(new moodle_url($PAGE->url), 'Config cache inconsistency detected, resetting caches...');
|
||||
}
|
||||
|
||||
if (!$cache and $version > $CFG->version) { // upgrade
|
||||
|
||||
$PAGE->set_url(new moodle_url($PAGE->url, array(
|
||||
'confirmupgrade' => $confirmupgrade,
|
||||
'confirmrelease' => $confirmrelease,
|
||||
'confirmplugincheck' => $confirmplugins,
|
||||
)));
|
||||
|
||||
check_upgrade_key($upgradekeyhash);
|
||||
|
||||
// Warning about upgrading a test site.
|
||||
$testsite = false;
|
||||
if (defined('BEHAT_SITE_RUNNING')) {
|
||||
$testsite = 'behat';
|
||||
}
|
||||
|
||||
if (!$cache && $version > $CFG->version && !$outagelessupgrade) { // Upgrade.
|
||||
if (isset($CFG->themerev)) {
|
||||
// Store the themerev to restore after purging caches.
|
||||
$themerev = $CFG->themerev;
|
||||
}
|
||||
|
||||
$PAGE->set_url(new moodle_url($PAGE->url, array(
|
||||
'confirmupgrade' => $confirmupgrade,
|
||||
'confirmrelease' => $confirmrelease,
|
||||
'confirmplugincheck' => $confirmplugins,
|
||||
)));
|
||||
// We purge all of MUC's caches here.
|
||||
// Caches are disabled for upgrade by CACHE_DISABLE_ALL so we must set the first arg to true.
|
||||
// This ensures a real config object is loaded and the stores will be purged.
|
||||
// This is the only way we can purge custom caches such as memcache or APC.
|
||||
// Note: all other calls to caches will still used the disabled API.
|
||||
cache_helper::purge_all(true);
|
||||
// We then purge the regular caches.
|
||||
purge_all_caches();
|
||||
|
||||
check_upgrade_key($upgradekeyhash);
|
||||
if (isset($themerev)) {
|
||||
// Restore the themerev
|
||||
set_config('themerev', $themerev);
|
||||
}
|
||||
|
||||
// Warning about upgrading a test site.
|
||||
$testsite = false;
|
||||
if (defined('BEHAT_SITE_RUNNING')) {
|
||||
$testsite = 'behat';
|
||||
$output = $PAGE->get_renderer('core', 'admin');
|
||||
|
||||
if (upgrade_stale_php_files_present()) {
|
||||
$PAGE->set_title($stradministration);
|
||||
$PAGE->set_cacheable(false);
|
||||
|
||||
echo $output->upgrade_stale_php_files_page();
|
||||
die();
|
||||
}
|
||||
|
||||
if (empty($confirmupgrade)) {
|
||||
$a = new stdClass();
|
||||
$a->oldversion = "$CFG->release (".sprintf('%.2f', $CFG->version).")";
|
||||
$a->newversion = "$release (".sprintf('%.2f', $version).")";
|
||||
$strdatabasechecking = get_string('databasechecking', '', $a);
|
||||
|
||||
$PAGE->set_title($stradministration);
|
||||
$PAGE->set_heading($strdatabasechecking);
|
||||
$PAGE->set_cacheable(false);
|
||||
|
||||
echo $output->upgrade_confirm_page($a->newversion, $maturity, $testsite);
|
||||
die();
|
||||
|
||||
} else if (empty($confirmrelease)) {
|
||||
require_once($CFG->libdir.'/environmentlib.php');
|
||||
list($envstatus, $environmentresults) = check_moodle_environment($release, ENV_SELECT_RELEASE);
|
||||
$strcurrentrelease = get_string('currentrelease');
|
||||
|
||||
$PAGE->navbar->add($strcurrentrelease);
|
||||
$PAGE->set_title($strcurrentrelease);
|
||||
$PAGE->set_heading($strcurrentrelease);
|
||||
$PAGE->set_cacheable(false);
|
||||
|
||||
echo $output->upgrade_environment_page($release, $envstatus, $environmentresults);
|
||||
die();
|
||||
|
||||
} else if (empty($confirmplugins)) {
|
||||
$strplugincheck = get_string('plugincheck');
|
||||
|
||||
$PAGE->navbar->add($strplugincheck);
|
||||
$PAGE->set_title($strplugincheck);
|
||||
$PAGE->set_heading($strplugincheck);
|
||||
$PAGE->set_cacheable(false);
|
||||
|
||||
$pluginman = core_plugin_manager::instance();
|
||||
|
||||
// Check for available updates.
|
||||
if ($fetchupdates) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
$updateschecker = \core\update\checker::instance();
|
||||
if ($updateschecker->enabled()) {
|
||||
$updateschecker->fetch();
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
}
|
||||
|
||||
if (isset($CFG->themerev)) {
|
||||
// Store the themerev to restore after purging caches.
|
||||
$themerev = $CFG->themerev;
|
||||
// Cancel all plugin installations.
|
||||
if ($abortinstallx) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
$abortables = $pluginman->list_cancellable_installations();
|
||||
if ($abortables) {
|
||||
if ($confirmabortinstall) {
|
||||
foreach ($abortables as $plugin) {
|
||||
$pluginman->cancel_plugin_installation($plugin->component);
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
} else {
|
||||
$continue = new moodle_url($PAGE->url, array('abortinstallx' => $abortinstallx, 'confirmabortinstall' => 1));
|
||||
echo $output->upgrade_confirm_abort_install_page($abortables, $continue);
|
||||
die();
|
||||
}
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
}
|
||||
|
||||
// We purge all of MUC's caches here.
|
||||
// Caches are disabled for upgrade by CACHE_DISABLE_ALL so we must set the first arg to true.
|
||||
// This ensures a real config object is loaded and the stores will be purged.
|
||||
// This is the only way we can purge custom caches such as memcache or APC.
|
||||
// Note: all other calls to caches will still used the disabled API.
|
||||
cache_helper::purge_all(true);
|
||||
// We then purge the regular caches.
|
||||
purge_all_caches();
|
||||
|
||||
if (isset($themerev)) {
|
||||
// Restore the themerev.
|
||||
set_config('themerev', $themerev);
|
||||
// Cancel single plugin installation.
|
||||
if ($abortinstall) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
if ($confirmabortinstall) {
|
||||
$pluginman->cancel_plugin_installation($abortinstall);
|
||||
redirect($PAGE->url);
|
||||
} else {
|
||||
$continue = new moodle_url($PAGE->url, array('abortinstall' => $abortinstall, 'confirmabortinstall' => 1));
|
||||
$abortable = $pluginman->get_plugin_info($abortinstall);
|
||||
if ($pluginman->can_cancel_plugin_installation($abortable)) {
|
||||
echo $output->upgrade_confirm_abort_install_page(array($abortable), $continue);
|
||||
die();
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel all plugins upgrades (that is, restore archived versions).
|
||||
if ($abortupgradex) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
$restorable = $pluginman->list_restorable_archives();
|
||||
if ($restorable) {
|
||||
upgrade_install_plugins($restorable, $confirmabortupgrade,
|
||||
get_string('cancelupgradehead', 'core_plugin'),
|
||||
new moodle_url($PAGE->url, array('abortupgradex' => 1, 'confirmabortupgrade' => 1))
|
||||
);
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
}
|
||||
|
||||
// Cancel single plugin upgrade (that is, install the archived version).
|
||||
if ($abortupgrade) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
$restorable = $pluginman->list_restorable_archives();
|
||||
if (isset($restorable[$abortupgrade])) {
|
||||
$restorable = array($restorable[$abortupgrade]);
|
||||
upgrade_install_plugins($restorable, $confirmabortupgrade,
|
||||
get_string('cancelupgradehead', 'core_plugin'),
|
||||
new moodle_url($PAGE->url, array('abortupgrade' => $abortupgrade, 'confirmabortupgrade' => 1))
|
||||
);
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
}
|
||||
|
||||
// Install all available missing dependencies.
|
||||
if ($installdepx) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
$installable = $pluginman->filter_installable($pluginman->missing_dependencies(true));
|
||||
upgrade_install_plugins($installable, $confirminstalldep,
|
||||
get_string('dependencyinstallhead', 'core_plugin'),
|
||||
new moodle_url($PAGE->url, array('installdepx' => 1, 'confirminstalldep' => 1))
|
||||
);
|
||||
}
|
||||
|
||||
// Install single available missing dependency.
|
||||
if ($installdep) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
$installable = $pluginman->filter_installable($pluginman->missing_dependencies(true));
|
||||
if (!empty($installable[$installdep])) {
|
||||
$installable = array($installable[$installdep]);
|
||||
upgrade_install_plugins($installable, $confirminstalldep,
|
||||
get_string('dependencyinstallhead', 'core_plugin'),
|
||||
new moodle_url($PAGE->url, array('installdep' => $installdep, 'confirminstalldep' => 1))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Install all available updates.
|
||||
if ($installupdatex) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
$installable = $pluginman->filter_installable($pluginman->available_updates());
|
||||
upgrade_install_plugins($installable, $confirminstallupdate,
|
||||
get_string('updateavailableinstallallhead', 'core_admin'),
|
||||
new moodle_url($PAGE->url, array('installupdatex' => 1, 'confirminstallupdate' => 1))
|
||||
);
|
||||
}
|
||||
|
||||
// Install single available update.
|
||||
if ($installupdate and $installupdateversion) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
if ($pluginman->is_remote_plugin_installable($installupdate, $installupdateversion)) {
|
||||
$installable = array($pluginman->get_remote_plugin_info($installupdate, $installupdateversion, true));
|
||||
upgrade_install_plugins($installable, $confirminstallupdate,
|
||||
get_string('updateavailableinstallallhead', 'core_admin'),
|
||||
new moodle_url($PAGE->url, array('installupdate' => $installupdate,
|
||||
'installupdateversion' => $installupdateversion, 'confirminstallupdate' => 1)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
echo $output->upgrade_plugin_check_page(core_plugin_manager::instance(), \core\update\checker::instance(),
|
||||
$version, $showallplugins, $PAGE->url, new moodle_url($PAGE->url, array('confirmplugincheck' => 1)));
|
||||
die();
|
||||
|
||||
} else {
|
||||
// Always verify plugin dependencies!
|
||||
$failed = array();
|
||||
if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
|
||||
echo $output->unsatisfied_dependencies_page($version, $failed, new moodle_url($PAGE->url,
|
||||
array('confirmplugincheck' => 0)));
|
||||
die();
|
||||
}
|
||||
unset($failed);
|
||||
|
||||
// Launch main upgrade.
|
||||
upgrade_core($version, true);
|
||||
}
|
||||
} else if ($version < $CFG->version) {
|
||||
// better stop here, we can not continue with plugin upgrades or anything else
|
||||
throw new moodle_exception('downgradedcore', 'error', new moodle_url('/admin/'));
|
||||
}
|
||||
|
||||
// Updated human-readable release version if necessary
|
||||
if (!$cache and $release <> $CFG->release) { // Update the release version
|
||||
set_config('release', $release);
|
||||
}
|
||||
|
||||
if (!$cache and $branch <> $CFG->branch) { // Update the branch
|
||||
set_config('branch', $branch);
|
||||
}
|
||||
|
||||
if (!$cache and moodle_needs_upgrading()) {
|
||||
|
||||
$PAGE->set_url(new moodle_url($PAGE->url, array(
|
||||
'confirmrelease' => $confirmrelease,
|
||||
'confirmplugincheck' => $confirmplugins,
|
||||
)));
|
||||
|
||||
check_upgrade_key($upgradekeyhash);
|
||||
|
||||
if (!$PAGE->headerprinted) {
|
||||
// means core upgrade or installation was not already done
|
||||
|
||||
$pluginman = core_plugin_manager::instance();
|
||||
$output = $PAGE->get_renderer('core', 'admin');
|
||||
|
||||
if (upgrade_stale_php_files_present()) {
|
||||
$PAGE->set_title($stradministration);
|
||||
$PAGE->set_cacheable(false);
|
||||
if (empty($confirmrelease)) {
|
||||
require_once($CFG->libdir . '/environmentlib.php');
|
||||
|
||||
echo $output->upgrade_stale_php_files_page();
|
||||
die();
|
||||
}
|
||||
|
||||
if (empty($confirmupgrade)) {
|
||||
$a = new stdClass();
|
||||
$a->oldversion = "$CFG->release (".sprintf('%.2f', $CFG->version).")";
|
||||
$a->newversion = "$release (".sprintf('%.2f', $version).")";
|
||||
$strdatabasechecking = get_string('databasechecking', '', $a);
|
||||
|
||||
$PAGE->set_title($stradministration);
|
||||
$PAGE->set_heading($strdatabasechecking);
|
||||
$PAGE->set_cacheable(false);
|
||||
|
||||
echo $output->upgrade_confirm_page($a->newversion, $maturity, $testsite);
|
||||
die();
|
||||
|
||||
} else if (empty($confirmrelease)) {
|
||||
require_once($CFG->libdir.'/environmentlib.php');
|
||||
list($envstatus, $environmentresults) = check_moodle_environment($release, ENV_SELECT_RELEASE);
|
||||
$strcurrentrelease = get_string('currentrelease');
|
||||
|
||||
@@ -384,7 +561,7 @@ if (!$outagelessupgrade) {
|
||||
echo $output->upgrade_environment_page($release, $envstatus, $environmentresults);
|
||||
die();
|
||||
|
||||
} else if (empty($confirmplugins)) {
|
||||
} else if (!$confirmplugins) {
|
||||
$strplugincheck = get_string('plugincheck');
|
||||
|
||||
$PAGE->navbar->add($strplugincheck);
|
||||
@@ -392,11 +569,9 @@ if (!$outagelessupgrade) {
|
||||
$PAGE->set_heading($strplugincheck);
|
||||
$PAGE->set_cacheable(false);
|
||||
|
||||
$pluginman = core_plugin_manager::instance();
|
||||
|
||||
// Check for available updates.
|
||||
if ($fetchupdates) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
require_sesskey();
|
||||
$updateschecker = \core\update\checker::instance();
|
||||
if ($updateschecker->enabled()) {
|
||||
$updateschecker->fetch();
|
||||
@@ -406,7 +581,7 @@ if (!$outagelessupgrade) {
|
||||
|
||||
// Cancel all plugin installations.
|
||||
if ($abortinstallx) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
require_sesskey();
|
||||
$abortables = $pluginman->list_cancellable_installations();
|
||||
if ($abortables) {
|
||||
if ($confirmabortinstall) {
|
||||
@@ -415,7 +590,8 @@ if (!$outagelessupgrade) {
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
} else {
|
||||
$continue = new moodle_url($PAGE->url, ['abortinstallx' => $abortinstallx, 'confirmabortinstall' => 1]);
|
||||
$continue = new moodle_url($PAGE->url, array('abortinstallx' => $abortinstallx,
|
||||
'confirmabortinstall' => 1));
|
||||
echo $output->upgrade_confirm_abort_install_page($abortables, $continue);
|
||||
die();
|
||||
}
|
||||
@@ -425,7 +601,7 @@ if (!$outagelessupgrade) {
|
||||
|
||||
// Cancel single plugin installation.
|
||||
if ($abortinstall) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
require_sesskey();
|
||||
if ($confirmabortinstall) {
|
||||
$pluginman->cancel_plugin_installation($abortinstall);
|
||||
redirect($PAGE->url);
|
||||
@@ -442,7 +618,7 @@ if (!$outagelessupgrade) {
|
||||
|
||||
// Cancel all plugins upgrades (that is, restore archived versions).
|
||||
if ($abortupgradex) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
require_sesskey();
|
||||
$restorable = $pluginman->list_restorable_archives();
|
||||
if ($restorable) {
|
||||
upgrade_install_plugins($restorable, $confirmabortupgrade,
|
||||
@@ -455,7 +631,7 @@ if (!$outagelessupgrade) {
|
||||
|
||||
// Cancel single plugin upgrade (that is, install the archived version).
|
||||
if ($abortupgrade) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
require_sesskey();
|
||||
$restorable = $pluginman->list_restorable_archives();
|
||||
if (isset($restorable[$abortupgrade])) {
|
||||
$restorable = array($restorable[$abortupgrade]);
|
||||
@@ -469,7 +645,7 @@ if (!$outagelessupgrade) {
|
||||
|
||||
// Install all available missing dependencies.
|
||||
if ($installdepx) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
require_sesskey();
|
||||
$installable = $pluginman->filter_installable($pluginman->missing_dependencies(true));
|
||||
upgrade_install_plugins($installable, $confirminstalldep,
|
||||
get_string('dependencyinstallhead', 'core_plugin'),
|
||||
@@ -479,7 +655,7 @@ if (!$outagelessupgrade) {
|
||||
|
||||
// Install single available missing dependency.
|
||||
if ($installdep) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
require_sesskey();
|
||||
$installable = $pluginman->filter_installable($pluginman->missing_dependencies(true));
|
||||
if (!empty($installable[$installdep])) {
|
||||
$installable = array($installable[$installdep]);
|
||||
@@ -492,7 +668,7 @@ if (!$outagelessupgrade) {
|
||||
|
||||
// Install all available updates.
|
||||
if ($installupdatex) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
require_sesskey();
|
||||
$installable = $pluginman->filter_installable($pluginman->available_updates());
|
||||
upgrade_install_plugins($installable, $confirminstallupdate,
|
||||
get_string('updateavailableinstallallhead', 'core_admin'),
|
||||
@@ -502,7 +678,7 @@ if (!$outagelessupgrade) {
|
||||
|
||||
// Install single available update.
|
||||
if ($installupdate and $installupdateversion) {
|
||||
// No sesskey support guaranteed here, because sessions might not work yet.
|
||||
require_sesskey();
|
||||
if ($pluginman->is_remote_plugin_installable($installupdate, $installupdateversion)) {
|
||||
$installable = array($pluginman->get_remote_plugin_info($installupdate, $installupdateversion, true));
|
||||
upgrade_install_plugins($installable, $confirminstallupdate,
|
||||
@@ -514,280 +690,83 @@ if (!$outagelessupgrade) {
|
||||
}
|
||||
}
|
||||
|
||||
echo $output->upgrade_plugin_check_page(core_plugin_manager::instance(), \core\update\checker::instance(),
|
||||
$version, $showallplugins, $PAGE->url, new moodle_url($PAGE->url, array('confirmplugincheck' => 1)));
|
||||
// Show plugins info.
|
||||
echo $output->upgrade_plugin_check_page($pluginman, \core\update\checker::instance(),
|
||||
$version, $showallplugins,
|
||||
new moodle_url($PAGE->url),
|
||||
new moodle_url($PAGE->url, array('confirmplugincheck' => 1, 'cache' => 0)));
|
||||
die();
|
||||
|
||||
} else {
|
||||
// Always verify plugin dependencies!
|
||||
$failed = array();
|
||||
if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed, $CFG->branch)) {
|
||||
echo $output->unsatisfied_dependencies_page($version, $failed, new moodle_url($PAGE->url,
|
||||
array('confirmplugincheck' => 0)));
|
||||
die();
|
||||
}
|
||||
unset($failed);
|
||||
|
||||
// Launch main upgrade.
|
||||
upgrade_core($version, true);
|
||||
}
|
||||
} else if ($version < $CFG->version) {
|
||||
// Better stop here, we can not continue with plugin upgrades or anything else.
|
||||
throw new moodle_exception('downgradedcore', 'error', new moodle_url('/admin/'));
|
||||
}
|
||||
|
||||
// Updated human-readable release version if necessary.
|
||||
if (!$cache && $release <> $CFG->release ) { // Update the release version.
|
||||
set_config('release', $release);
|
||||
}
|
||||
|
||||
if (!$cache && $branch <> $CFG->branch) { // Update the branch.
|
||||
set_config('branch', $branch);
|
||||
}
|
||||
|
||||
if (!$cache && moodle_needs_upgrading()) {
|
||||
|
||||
$PAGE->set_url(new moodle_url($PAGE->url, array(
|
||||
'confirmrelease' => $confirmrelease,
|
||||
'confirmplugincheck' => $confirmplugins,
|
||||
)));
|
||||
|
||||
check_upgrade_key($upgradekeyhash);
|
||||
|
||||
if (!$PAGE->headerprinted) {
|
||||
// Means core upgrade or installation was not already done.
|
||||
|
||||
$pluginman = core_plugin_manager::instance();
|
||||
// Make sure plugin dependencies are always checked.
|
||||
$failed = array();
|
||||
if (!$pluginman->all_plugins_ok($version, $failed)) {
|
||||
$output = $PAGE->get_renderer('core', 'admin');
|
||||
|
||||
if (empty($confirmrelease)) {
|
||||
require_once($CFG->libdir . '/environmentlib.php');
|
||||
|
||||
list($envstatus, $environmentresults) = check_moodle_environment($release, ENV_SELECT_RELEASE);
|
||||
$strcurrentrelease = get_string('currentrelease');
|
||||
|
||||
$PAGE->navbar->add($strcurrentrelease);
|
||||
$PAGE->set_title($strcurrentrelease);
|
||||
$PAGE->set_heading($strcurrentrelease);
|
||||
$PAGE->set_cacheable(false);
|
||||
|
||||
echo $output->upgrade_environment_page($release, $envstatus, $environmentresults);
|
||||
die();
|
||||
|
||||
} else if (!$confirmplugins) {
|
||||
$strplugincheck = get_string('plugincheck');
|
||||
|
||||
$PAGE->navbar->add($strplugincheck);
|
||||
$PAGE->set_title($strplugincheck);
|
||||
$PAGE->set_heading($strplugincheck);
|
||||
$PAGE->set_cacheable(false);
|
||||
|
||||
// Check for available updates.
|
||||
if ($fetchupdates) {
|
||||
require_sesskey();
|
||||
$updateschecker = \core\update\checker::instance();
|
||||
if ($updateschecker->enabled()) {
|
||||
$updateschecker->fetch();
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
}
|
||||
|
||||
// Cancel all plugin installations.
|
||||
if ($abortinstallx) {
|
||||
require_sesskey();
|
||||
$abortables = $pluginman->list_cancellable_installations();
|
||||
if ($abortables) {
|
||||
if ($confirmabortinstall) {
|
||||
foreach ($abortables as $plugin) {
|
||||
$pluginman->cancel_plugin_installation($plugin->component);
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
} else {
|
||||
$continue = new moodle_url($PAGE->url, array('abortinstallx' => $abortinstallx,
|
||||
'confirmabortinstall' => 1));
|
||||
echo $output->upgrade_confirm_abort_install_page($abortables, $continue);
|
||||
die();
|
||||
}
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
}
|
||||
|
||||
// Cancel single plugin installation.
|
||||
if ($abortinstall) {
|
||||
require_sesskey();
|
||||
if ($confirmabortinstall) {
|
||||
$pluginman->cancel_plugin_installation($abortinstall);
|
||||
redirect($PAGE->url);
|
||||
} else {
|
||||
$continue = new moodle_url($PAGE->url, array('abortinstall' => $abortinstall, 'confirmabortinstall' => 1));
|
||||
$abortable = $pluginman->get_plugin_info($abortinstall);
|
||||
if ($pluginman->can_cancel_plugin_installation($abortable)) {
|
||||
echo $output->upgrade_confirm_abort_install_page(array($abortable), $continue);
|
||||
die();
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel all plugins upgrades (that is, restore archived versions).
|
||||
if ($abortupgradex) {
|
||||
require_sesskey();
|
||||
$restorable = $pluginman->list_restorable_archives();
|
||||
if ($restorable) {
|
||||
upgrade_install_plugins($restorable, $confirmabortupgrade,
|
||||
get_string('cancelupgradehead', 'core_plugin'),
|
||||
new moodle_url($PAGE->url, array('abortupgradex' => 1, 'confirmabortupgrade' => 1))
|
||||
);
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
}
|
||||
|
||||
// Cancel single plugin upgrade (that is, install the archived version).
|
||||
if ($abortupgrade) {
|
||||
require_sesskey();
|
||||
$restorable = $pluginman->list_restorable_archives();
|
||||
if (isset($restorable[$abortupgrade])) {
|
||||
$restorable = array($restorable[$abortupgrade]);
|
||||
upgrade_install_plugins($restorable, $confirmabortupgrade,
|
||||
get_string('cancelupgradehead', 'core_plugin'),
|
||||
new moodle_url($PAGE->url, array('abortupgrade' => $abortupgrade, 'confirmabortupgrade' => 1))
|
||||
);
|
||||
}
|
||||
redirect($PAGE->url);
|
||||
}
|
||||
|
||||
// Install all available missing dependencies.
|
||||
if ($installdepx) {
|
||||
require_sesskey();
|
||||
$installable = $pluginman->filter_installable($pluginman->missing_dependencies(true));
|
||||
upgrade_install_plugins($installable, $confirminstalldep,
|
||||
get_string('dependencyinstallhead', 'core_plugin'),
|
||||
new moodle_url($PAGE->url, array('installdepx' => 1, 'confirminstalldep' => 1))
|
||||
);
|
||||
}
|
||||
|
||||
// Install single available missing dependency.
|
||||
if ($installdep) {
|
||||
require_sesskey();
|
||||
$installable = $pluginman->filter_installable($pluginman->missing_dependencies(true));
|
||||
if (!empty($installable[$installdep])) {
|
||||
$installable = array($installable[$installdep]);
|
||||
upgrade_install_plugins($installable, $confirminstalldep,
|
||||
get_string('dependencyinstallhead', 'core_plugin'),
|
||||
new moodle_url($PAGE->url, array('installdep' => $installdep, 'confirminstalldep' => 1))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Install all available updates.
|
||||
if ($installupdatex) {
|
||||
require_sesskey();
|
||||
$installable = $pluginman->filter_installable($pluginman->available_updates());
|
||||
upgrade_install_plugins($installable, $confirminstallupdate,
|
||||
get_string('updateavailableinstallallhead', 'core_admin'),
|
||||
new moodle_url($PAGE->url, array('installupdatex' => 1, 'confirminstallupdate' => 1))
|
||||
);
|
||||
}
|
||||
|
||||
// Install single available update.
|
||||
if ($installupdate && $installupdateversion) {
|
||||
require_sesskey();
|
||||
if ($pluginman->is_remote_plugin_installable($installupdate, $installupdateversion)) {
|
||||
$installable = array($pluginman->get_remote_plugin_info($installupdate, $installupdateversion, true));
|
||||
upgrade_install_plugins($installable, $confirminstallupdate,
|
||||
get_string('updateavailableinstallallhead', 'core_admin'),
|
||||
new moodle_url($PAGE->url, array('installupdate' => $installupdate,
|
||||
'installupdateversion' => $installupdateversion, 'confirminstallupdate' => 1)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Show plugins info.
|
||||
echo $output->upgrade_plugin_check_page($pluginman, \core\update\checker::instance(),
|
||||
$version, $showallplugins,
|
||||
new moodle_url($PAGE->url),
|
||||
new moodle_url($PAGE->url, array('confirmplugincheck' => 1, 'cache' => 0)));
|
||||
die();
|
||||
}
|
||||
|
||||
// Make sure plugin dependencies are always checked.
|
||||
$failed = array();
|
||||
if (!$pluginman->all_plugins_ok($version, $failed, $CFG->branch)) {
|
||||
$output = $PAGE->get_renderer('core', 'admin');
|
||||
echo $output->unsatisfied_dependencies_page($version, $failed, new moodle_url($PAGE->url,
|
||||
array('confirmplugincheck' => 0)));
|
||||
die();
|
||||
}
|
||||
unset($failed);
|
||||
echo $output->unsatisfied_dependencies_page($version, $failed, new moodle_url($PAGE->url,
|
||||
array('confirmplugincheck' => 0)));
|
||||
die();
|
||||
}
|
||||
|
||||
// Install/upgrade all plugins and other parts.
|
||||
upgrade_noncore(true);
|
||||
unset($failed);
|
||||
}
|
||||
|
||||
// If this is the first install, indicate that this site is fully configured,
|
||||
// Except the admin password.
|
||||
if (during_initial_install()) {
|
||||
set_config('rolesactive', 1); // After this, during_initial_install will return false.
|
||||
set_config('adminsetuppending', 1);
|
||||
set_config('registrationpending', 1); // Remind to register site after all other setup is finished.
|
||||
// install/upgrade all plugins and other parts
|
||||
upgrade_noncore(true);
|
||||
}
|
||||
|
||||
// Apply default preset, if it is defined in $CFG and has a valid value.
|
||||
if (!empty($CFG->setsitepresetduringinstall)) {
|
||||
\core_adminpresets\helper::change_default_preset($CFG->setsitepresetduringinstall);
|
||||
// If this is the first install, indicate that this site is fully configured
|
||||
// except the admin password
|
||||
if (during_initial_install()) {
|
||||
set_config('rolesactive', 1); // after this, during_initial_install will return false.
|
||||
set_config('adminsetuppending', 1);
|
||||
set_config('registrationpending', 1); // Remind to register site after all other setup is finished.
|
||||
// we need this redirect to setup proper session
|
||||
upgrade_finished("index.php?sessionstarted=1&lang=$CFG->lang");
|
||||
}
|
||||
|
||||
// make sure admin user is created - this is the last step because we need
|
||||
// session to be working properly in order to edit admin account
|
||||
if (!empty($CFG->adminsetuppending)) {
|
||||
$sessionstarted = optional_param('sessionstarted', 0, PARAM_BOOL);
|
||||
if (!$sessionstarted) {
|
||||
redirect("index.php?sessionstarted=1&lang=$CFG->lang");
|
||||
} else {
|
||||
$sessionverify = optional_param('sessionverify', 0, PARAM_BOOL);
|
||||
if (!$sessionverify) {
|
||||
$SESSION->sessionverify = 1;
|
||||
redirect("index.php?sessionstarted=1&sessionverify=1&lang=$CFG->lang");
|
||||
} else {
|
||||
if (empty($SESSION->sessionverify)) {
|
||||
print_error('installsessionerror', 'admin', "index.php?sessionstarted=1&lang=$CFG->lang");
|
||||
}
|
||||
unset($SESSION->sessionverify);
|
||||
}
|
||||
|
||||
// We need this redirect to setup proper session.
|
||||
upgrade_finished("index.php?sessionstarted=1&lang=$CFG->lang");
|
||||
}
|
||||
|
||||
// Make sure admin user is created - this is the last step,
|
||||
// We need session to be working properly in order to edit admin account.
|
||||
if (!empty($CFG->adminsetuppending)) {
|
||||
$sessionstarted = optional_param('sessionstarted', 0, PARAM_BOOL);
|
||||
if (!$sessionstarted) {
|
||||
redirect("index.php?sessionstarted=1&lang=$CFG->lang");
|
||||
} else {
|
||||
$sessionverify = optional_param('sessionverify', 0, PARAM_BOOL);
|
||||
if (!$sessionverify) {
|
||||
$SESSION->sessionverify = 1;
|
||||
redirect("index.php?sessionstarted=1&sessionverify=1&lang=$CFG->lang");
|
||||
} else {
|
||||
if (empty($SESSION->sessionverify)) {
|
||||
throw new \moodle_exception('installsessionerror', 'admin', "index.php?sessionstarted=1&lang=$CFG->lang");
|
||||
}
|
||||
unset($SESSION->sessionverify);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup SESSION to make sure other code does not complain in the future.
|
||||
unset($SESSION->has_timed_out);
|
||||
unset($SESSION->wantsurl);
|
||||
|
||||
// At this stage there can be only one admin unless more were added by install,
|
||||
// Users may change username, so do not rely on that.
|
||||
$adminids = explode(',', $CFG->siteadmins);
|
||||
$adminuser = get_complete_user_data('id', reset($adminids));
|
||||
|
||||
if ($adminuser->password === 'adminsetuppending') {
|
||||
// Prevent installation hijacking.
|
||||
if ($adminuser->lastip !== getremoteaddr()) {
|
||||
throw new \moodle_exception('installhijacked', 'admin');
|
||||
}
|
||||
// Login user and let him set password and admin details.
|
||||
$adminuser->newadminuser = 1;
|
||||
complete_user_login($adminuser);
|
||||
redirect("$CFG->wwwroot/user/editadvanced.php?id=$adminuser->id"); // Edit thyself.
|
||||
|
||||
} else {
|
||||
unset_config('adminsetuppending');
|
||||
// Cleanup SESSION to make sure other code does not complain in the future.
|
||||
unset($SESSION->has_timed_out);
|
||||
unset($SESSION->wantsurl);
|
||||
|
||||
// at this stage there can be only one admin unless more were added by install - users may change username, so do not rely on that
|
||||
$adminids = explode(',', $CFG->siteadmins);
|
||||
$adminuser = get_complete_user_data('id', reset($adminids));
|
||||
|
||||
if ($adminuser->password === 'adminsetuppending') {
|
||||
// prevent installation hijacking
|
||||
if ($adminuser->lastip !== getremoteaddr()) {
|
||||
print_error('installhijacked', 'admin');
|
||||
}
|
||||
// login user and let him set password and admin details
|
||||
$adminuser->newadminuser = 1;
|
||||
complete_user_login($adminuser);
|
||||
redirect("$CFG->wwwroot/user/editadvanced.php?id=$adminuser->id"); // Edit thyself
|
||||
|
||||
} else {
|
||||
// Just make sure upgrade logging is properly terminated.
|
||||
upgrade_finished('upgradesettings.php');
|
||||
unset_config('adminsetuppending');
|
||||
}
|
||||
|
||||
} else {
|
||||
// just make sure upgrade logging is properly terminated
|
||||
upgrade_finished('upgradesettings.php');
|
||||
}
|
||||
|
||||
if (has_capability('moodle/site:config', context_system::instance())) {
|
||||
@@ -840,7 +819,6 @@ $insecuredataroot = is_dataroot_insecure(true);
|
||||
$SESSION->admin_critical_warning = ($insecuredataroot==INSECURE_DATAROOT_ERROR);
|
||||
|
||||
$adminroot = admin_get_root();
|
||||
$PAGE->set_primary_active_tab('siteadminnode');
|
||||
|
||||
// Check if there are any new admin settings which have still yet to be set
|
||||
if (any_new_admin_settings($adminroot)) {
|
||||
@@ -867,9 +845,8 @@ $errorsdisplayed = defined('WARN_DISPLAY_ERRORS_ENABLED');
|
||||
$lastcron = get_config('tool_task', 'lastcronstart');
|
||||
$cronoverdue = ($lastcron < time() - 3600 * 24);
|
||||
$lastcroninterval = get_config('tool_task', 'lastcroninterval');
|
||||
|
||||
$expectedfrequency = $CFG->expectedcronfrequency ?? MINSECS;
|
||||
$croninfrequent = !$cronoverdue && ($lastcroninterval > ($expectedfrequency + MINSECS) || $lastcron < time() - $expectedfrequency);
|
||||
$expectedfrequency = $CFG->expectedcronfrequency ?? 200;
|
||||
$croninfrequent = !$cronoverdue && ($lastcroninterval > $expectedfrequency || $lastcron < time() - $expectedfrequency);
|
||||
$dbproblems = $DB->diagnose();
|
||||
$maintenancemode = !empty($CFG->maintenance_enabled);
|
||||
|
||||
@@ -923,21 +900,6 @@ if (empty($CFG->disabledevlibdirscheck) && (is_dir($CFG->dirroot.'/vendor') || i
|
||||
// Check if the site is being foced onto ssl.
|
||||
$overridetossl = !empty($CFG->overridetossl);
|
||||
|
||||
// Check if moodle campaign content setting is enabled or not.
|
||||
$showcampaigncontent = !isset($CFG->showcampaigncontent) || $CFG->showcampaigncontent;
|
||||
|
||||
// Encourage admins to enable the user feedback feature if it is not enabled already.
|
||||
$showfeedbackencouragement = empty($CFG->enableuserfeedback);
|
||||
|
||||
// Check if the service and support content setting is enabled or not.
|
||||
$servicesandsupportcontent = !isset($CFG->showservicesandsupportcontent) || $CFG->showservicesandsupportcontent;
|
||||
|
||||
// Check whether the XML-RPC protocol is enabled or not.
|
||||
require_once($CFG->libdir . '/environmentlib.php');
|
||||
$result = new environment_results('custom_checks');
|
||||
$result = check_xmlrpc_usage($result);
|
||||
$xmlrpcwarning = !is_null($result) ? get_string($result->getFeedbackStr(), 'admin') : '';
|
||||
|
||||
admin_externalpage_setup('adminnotifications');
|
||||
|
||||
$output = $PAGE->get_renderer('core', 'admin');
|
||||
@@ -945,6 +907,4 @@ $output = $PAGE->get_renderer('core', 'admin');
|
||||
echo $output->admin_notifications_page($maturity, $insecuredataroot, $errorsdisplayed, $cronoverdue, $dbproblems,
|
||||
$maintenancemode, $availableupdates, $availableupdatesfetch, $buggyiconvnomb,
|
||||
$registered, $cachewarnings, $eventshandlers, $themedesignermode, $devlibdir,
|
||||
$mobileconfigured, $overridetossl, $invalidforgottenpasswordurl, $croninfrequent,
|
||||
$showcampaigncontent, $showfeedbackencouragement, $servicesandsupportcontent,
|
||||
$xmlrpcwarning);
|
||||
$mobileconfigured, $overridetossl, $invalidforgottenpasswordurl, $croninfrequent);
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ function admin_page_type_list($pagetype, $parentcontext, $currentcontext) {
|
||||
function core_admin_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = array()) {
|
||||
global $CFG;
|
||||
|
||||
if (in_array($filearea, ['logo', 'logocompact', 'favicon'])) {
|
||||
if (in_array($filearea, ['logo', 'logocompact'])) {
|
||||
$size = array_shift($args); // The path hides the size.
|
||||
$itemid = clean_param(array_shift($args), PARAM_INT);
|
||||
$filename = clean_param(array_shift($args), PARAM_FILE);
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Allows admin to configure licenses.
|
||||
*/
|
||||
|
||||
require_once('../config.php');
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
require_once($CFG->libdir.'/licenselib.php');
|
||||
|
||||
require_admin();
|
||||
|
||||
$returnurl = "$CFG->wwwroot/$CFG->admin/settings.php?section=managelicenses";
|
||||
|
||||
$action = optional_param('action', '', PARAM_ALPHANUMEXT);
|
||||
$license = optional_param('license', '', PARAM_SAFEDIR);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// process actions
|
||||
|
||||
if (!confirm_sesskey()) {
|
||||
redirect($returnurl);
|
||||
}
|
||||
|
||||
$return = true;
|
||||
switch ($action) {
|
||||
case 'disable':
|
||||
license_manager::disable($license);
|
||||
break;
|
||||
|
||||
case 'enable':
|
||||
license_manager::enable($license);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ($return) {
|
||||
redirect ($returnurl);
|
||||
}
|
||||
@@ -45,7 +45,7 @@ if (isset($_ENV['TMPDIR']) && is_dir($_ENV['TMPDIR'])) {
|
||||
$tmpfile = $tmpdir . '/moodle-mailout.log';
|
||||
$fh = fopen($tmpfile, 'a+', false)
|
||||
or mdie("Error openning $tmpfile on append\n");
|
||||
fwrite($fh, "==== ".date("D M d H:i:s Y", time())." ====\n");
|
||||
fwrite($fh, "==== ".strftime("%a %b %e %H:%M:%S %Y", time())." ====\n");
|
||||
fwrite($fh, "==== Commandline: " . implode(' ',$argv) . "\n");
|
||||
|
||||
$stdin = fopen('php://stdin', 'r');
|
||||
|
||||
+29
-24
@@ -23,11 +23,14 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once('../config.php');
|
||||
require_once("{$CFG->libdir}/adminlib.php");
|
||||
define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
$action = required_param('action', PARAM_ALPHANUMEXT);
|
||||
$plugin = required_param('plugin', PARAM_PLUGIN);
|
||||
require_once('../config.php');
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
|
||||
$action = required_param('action', PARAM_ALPHANUMEXT);
|
||||
$media = required_param('media', PARAM_PLUGIN);
|
||||
$confirm = optional_param('confirm', 0, PARAM_BOOL);
|
||||
|
||||
$PAGE->set_url('/admin/media.php');
|
||||
$PAGE->set_context(context_system::instance());
|
||||
@@ -35,38 +38,40 @@ $PAGE->set_context(context_system::instance());
|
||||
require_admin();
|
||||
require_sesskey();
|
||||
|
||||
$return = new moodle_url('/admin/settings.php', [
|
||||
'section' => 'managemediaplayers',
|
||||
]);
|
||||
$plugins = core_plugin_manager::instance()->get_plugins_of_type('media');
|
||||
$sortorder = array_values(\core\plugininfo\media::get_enabled_plugins());
|
||||
|
||||
$return = new moodle_url('/admin/settings.php', array('section' => 'managemediaplayers'));
|
||||
|
||||
if (!array_key_exists($media, $plugins)) {
|
||||
redirect($return);
|
||||
}
|
||||
|
||||
$displayname = get_string('pluginname', "media_{$plugin}");
|
||||
switch ($action) {
|
||||
case 'disable':
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('media');
|
||||
if ($class::enable_plugin($plugin, false)) {
|
||||
\core\notification::add(
|
||||
get_string('plugin_disabled', 'core_admin', $displayname),
|
||||
\core\notification::SUCCESS
|
||||
);
|
||||
}
|
||||
$plugins[$media]->set_enabled(false);
|
||||
break;
|
||||
|
||||
case 'enable':
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('media');
|
||||
if ($class::enable_plugin($plugin, true)) {
|
||||
\core\notification::add(
|
||||
get_string('plugin_enabled', 'core_admin', $displayname),
|
||||
\core\notification::SUCCESS
|
||||
);
|
||||
}
|
||||
$plugins[$media]->set_enabled(true);
|
||||
break;
|
||||
|
||||
case 'up':
|
||||
$class::change_plugin_order($plugin, $class::MOVE_UP);
|
||||
if (($pos = array_search($media, $sortorder)) > 0) {
|
||||
$tmp = $sortorder[$pos - 1];
|
||||
$sortorder[$pos - 1] = $sortorder[$pos];
|
||||
$sortorder[$pos] = $tmp;
|
||||
\core\plugininfo\media::set_enabled_plugins($sortorder);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'down':
|
||||
$class::change_plugin_order($plugin, $class::MOVE_DOWN);
|
||||
if ((($pos = array_search($media, $sortorder)) !== false) && ($pos < count($sortorder) - 1)) {
|
||||
$tmp = $sortorder[$pos + 1];
|
||||
$sortorder[$pos + 1] = $sortorder[$pos];
|
||||
$sortorder[$pos] = $tmp;
|
||||
\core\plugininfo\media::set_enabled_plugins($sortorder);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+58
-46
@@ -48,52 +48,69 @@ if (($form = data_submitted()) && confirm_sesskey()) {
|
||||
foreach ($providers as $provider) {
|
||||
$componentproviderbase = $provider->component.'_'.$provider->name;
|
||||
$disableprovidersetting = $componentproviderbase.'_disable';
|
||||
$providerdisabled = false;
|
||||
if (!isset($form->$disableprovidersetting)) {
|
||||
$providerdisabled = true;
|
||||
$newpreferences[$disableprovidersetting] = 1;
|
||||
} else {
|
||||
$newpreferences[$disableprovidersetting] = 0;
|
||||
}
|
||||
|
||||
$componentprovidersetting = $componentproviderbase.'_locked';
|
||||
foreach ($processors as $processor) {
|
||||
$value = 0;
|
||||
if (isset($form->{$componentprovidersetting}[$processor->name])) {
|
||||
$value = $form->{$componentprovidersetting}[$processor->name];
|
||||
if ($value == 'on') {
|
||||
$value = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Record the site preference.
|
||||
$newpreferences[$processor->name.'_provider_'.$componentprovidersetting] = $value;
|
||||
}
|
||||
|
||||
$componentprovidersetting = $componentproviderbase.'_enabled';
|
||||
$newsettings = [];
|
||||
if (isset($form->$componentprovidersetting)) {
|
||||
// Store defined comma-separated processors as setting value.
|
||||
// Using array_filter eliminates elements set to 0 above.
|
||||
$newsettings = array_keys(array_filter($form->{$componentprovidersetting}));
|
||||
}
|
||||
|
||||
// Let's join existing setting values for disabled processors.
|
||||
$property = 'message_provider_'.$componentprovidersetting;
|
||||
if (property_exists($preferences, $property)) {
|
||||
$existingsetting = $preferences->$property;
|
||||
foreach ($disabledprocessors as $disable) {
|
||||
if (strpos($existingsetting, $disable->name) > -1) {
|
||||
$newsettings[] = $disable->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$value = join(',', $newsettings);
|
||||
if (empty($value)) {
|
||||
foreach (array('permitted', 'loggedin', 'loggedoff') as $setting) {
|
||||
$value = null;
|
||||
}
|
||||
$componentprovidersetting = $componentproviderbase.'_'.$setting;
|
||||
if ($setting == 'permitted') {
|
||||
// If we deal with permitted select element, we need to create individual
|
||||
// setting for each possible processor. Note that this block will
|
||||
// always be processed first after entring parental foreach iteration
|
||||
// so we can change form values on this stage.
|
||||
foreach ($processors as $processor) {
|
||||
$value = '';
|
||||
if (isset($form->{$componentprovidersetting}[$processor->name])) {
|
||||
$value = $form->{$componentprovidersetting}[$processor->name];
|
||||
}
|
||||
// Ensure that loggedin loggedoff options are set correctly for this permission.
|
||||
if (($value == 'disallowed') || $providerdisabled) {
|
||||
// It might be better to unset them, but I can't figure out why that cause error.
|
||||
$form->{$componentproviderbase.'_loggedin'}[$processor->name] = 0;
|
||||
$form->{$componentproviderbase.'_loggedoff'}[$processor->name] = 0;
|
||||
} else if ($value == 'forced') {
|
||||
$form->{$componentproviderbase.'_loggedin'}[$processor->name] = 1;
|
||||
$form->{$componentproviderbase.'_loggedoff'}[$processor->name] = 1;
|
||||
}
|
||||
// Record the site preference.
|
||||
$newpreferences[$processor->name.'_provider_'.$componentprovidersetting] = $value;
|
||||
}
|
||||
} else {
|
||||
$newsettings = array();
|
||||
if (property_exists($form, $componentprovidersetting)) {
|
||||
// We must be processing loggedin or loggedoff checkboxes.
|
||||
// Store defained comma-separated processors as setting value.
|
||||
// Using array_filter eliminates elements set to 0 above.
|
||||
$newsettings = array_keys(array_filter($form->{$componentprovidersetting}));
|
||||
}
|
||||
|
||||
// Record the site preference.
|
||||
$newpreferences['message_provider_'.$componentprovidersetting] = $value;
|
||||
// Let's join existing setting values for disabled processors.
|
||||
$property = 'message_provider_'.$componentprovidersetting;
|
||||
if (property_exists($preferences, $property)) {
|
||||
$existingsetting = $preferences->$property;
|
||||
foreach ($disabledprocessors as $disable) {
|
||||
if (strpos($existingsetting, $disable->name) > -1) {
|
||||
$newsettings[] = $disable->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$value = join(',', $newsettings);
|
||||
if (empty($value)) {
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
if ($setting != 'permitted') {
|
||||
// We have already recoded site preferences for 'permitted' type.
|
||||
$newpreferences['message_provider_'.$componentprovidersetting] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update database.
|
||||
@@ -102,17 +119,10 @@ if (($form = data_submitted()) && confirm_sesskey()) {
|
||||
// Save processors enabled/disabled status.
|
||||
foreach ($allprocessors as $processor) {
|
||||
$enabled = isset($form->{$processor->name});
|
||||
$class = \core_plugin_manager::resolve_plugininfo_class('message');
|
||||
$class::enable_plugin($processor->name, $enabled);
|
||||
\core_message\api::update_processor_status($processor, $enabled);
|
||||
}
|
||||
|
||||
foreach ($newpreferences as $name => $value) {
|
||||
$old = isset($preferences->$name) ? $preferences->$name : '';
|
||||
|
||||
if ($old != $value) {
|
||||
add_to_config_log($name, $old, $value, 'core');
|
||||
}
|
||||
|
||||
set_config($name, $value, 'message');
|
||||
}
|
||||
$transaction->allow_commit();
|
||||
@@ -125,6 +135,8 @@ if (($form = data_submitted()) && confirm_sesskey()) {
|
||||
|
||||
// Page settings
|
||||
$PAGE->set_context(context_system::instance());
|
||||
$PAGE->requires->js_init_call('M.core_message.init_defaultoutputs');
|
||||
|
||||
$renderer = $PAGE->get_renderer('core', 'message');
|
||||
|
||||
// Display the page.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user