Compare commits
607 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c88d732e39 | |||
| 09323d63f1 | |||
| 61d76db67b | |||
| 86efefc1eb | |||
| acc147bb29 | |||
| 7f1c38ea92 | |||
| 2451ef2ba5 | |||
| 5e9dd950a1 | |||
| 1aa9422c9f | |||
| c53515f170 | |||
| 5aaf5d99d7 | |||
| a337b28093 | |||
| 002437681b | |||
| ee905caef5 | |||
| a76b3ea7c8 | |||
| 82002907b5 | |||
| edf7b72716 | |||
| f4f079fa42 | |||
| fb1b60d0b4 | |||
| 57f681365d | |||
| 45a5c7bfa5 | |||
| a2bd8bec84 | |||
| 821c578ff3 | |||
| 6b4a4fba82 | |||
| 8a81a31fd8 | |||
| d2015975ed | |||
| d51f3d032e | |||
| 5622de1179 | |||
| 6faeafa53d | |||
| af45515fe1 | |||
| f3a81b5b3d | |||
| 393e396080 | |||
| 54a250b5fa | |||
| 5fa331fc19 | |||
| 5b502e1c04 | |||
| 48a860601e | |||
| 061f29c7ef | |||
| b48c1715df | |||
| 3a8a5c7968 | |||
| a7547d531f | |||
| cbebe6810c | |||
| 390d427b2a | |||
| 839da4abbe | |||
| b9cda33903 | |||
| df4aa5380f | |||
| 20083e193d | |||
| e2c7bd75ef | |||
| 0ecea81e24 | |||
| f058a0ae79 | |||
| ea99ff8801 | |||
| 885b98991b | |||
| 1ba6407a66 | |||
| 824840cf1f | |||
| 12ffbefe4a | |||
| e60503f77c | |||
| 6be471293b | |||
| 03e8d1d8f8 | |||
| 616755e129 | |||
| 6585acf84d | |||
| 97516416ad | |||
| 2681c83fb8 | |||
| d6adc76343 | |||
| 232168c8f2 | |||
| 2eef46fb46 | |||
| 4f8912ac4e | |||
| d3552b500f | |||
| 0af69b05b1 | |||
| f9d5f0bedc | |||
| ff0847e74b | |||
| ec1f3ffb7a | |||
| a63a6643e5 | |||
| f7c13d3996 | |||
| e39814b41a | |||
| b92a61734c | |||
| 79421e92f2 | |||
| 106294da28 | |||
| acb53bb1f0 | |||
| 2dfa8e8d74 | |||
| f056ce4e0d | |||
| be3ed34e68 | |||
| d3c46e052e | |||
| 8215efe451 | |||
| af1a436b2b | |||
| ad8a4d8252 | |||
| 3cb2c5657a | |||
| 1f1a74a431 | |||
| 20a3b1b194 | |||
| 99d4117301 | |||
| 360920f41b | |||
| 1d6b2c6e08 | |||
| 15e212555e | |||
| cde9e41fc2 | |||
| 164c68f3e5 | |||
| a1069966fd | |||
| ba82320484 | |||
| e597b88315 | |||
| 662c071f65 | |||
| 0243fd369b | |||
| 81b386b9ae | |||
| 97bfff5c12 | |||
| b4bec70f7d | |||
| e1255d9a68 | |||
| efb364d5ff | |||
| 7f682a5392 | |||
| 8e7363baa6 | |||
| 3f40483351 | |||
| 11e1774de7 | |||
| c68f118e35 | |||
| fffbcd1641 | |||
| fd7738bc17 | |||
| 48efb6d287 | |||
| c82b6e01b1 | |||
| 43c501a6da | |||
| 99ed7c1161 | |||
| fe2bc7f69e | |||
| 176457450a | |||
| 07caf1b933 | |||
| fb4769c30a | |||
| 0e94568f01 | |||
| d49423d131 | |||
| 3d61b21764 | |||
| 8b7c7ae5d2 | |||
| 939b429054 | |||
| 086dd3c269 | |||
| 4b2d298d3e | |||
| 023a8a778f | |||
| c4c0589a91 | |||
| 3703f2eec6 | |||
| ae6616a7b1 | |||
| da0762f95d | |||
| 5a4937049c | |||
| 0380b49e1b | |||
| b7d9f9e827 | |||
| 20227eee86 | |||
| f203630918 | |||
| 92708b3f8b | |||
| 48bb66e3d3 | |||
| 4f64c3f57c | |||
| 1575e0551d | |||
| 3a37d96479 | |||
| 5a4f04c54f | |||
| 3a4603871a | |||
| f447d028f7 | |||
| 20d6fcf956 | |||
| 115a493b7f | |||
| 200818f912 | |||
| 0eed941c68 | |||
| 4295445828 | |||
| 0cbc68d58b | |||
| 76162e5ed2 | |||
| bc7a35db6a | |||
| 792d9c26d3 | |||
| 748d9bd512 | |||
| 7730b4b575 | |||
| 4e78dd9921 | |||
| 2f3ac0e51c | |||
| fecba4307a | |||
| 2e58ffbe4c | |||
| 7bd829fa56 | |||
| 124ac3bcf7 | |||
| 77d98b8215 | |||
| 697de172a1 | |||
| ebdc693048 | |||
| fd37432a6e | |||
| 4bafd61443 | |||
| ff6112f675 | |||
| fe4a9a28e4 | |||
| 75dacc6329 | |||
| 72436b5a0b | |||
| 8a01a9dbe6 | |||
| df597afcfb | |||
| 9131d460b8 | |||
| 350cb520b8 | |||
| 3eec169ce6 | |||
| 131da11055 | |||
| 2273ca23ff | |||
| 814c969f70 | |||
| 18d595089b | |||
| 600166489c | |||
| 1b3bb25b6a | |||
| 4c74b7d17b | |||
| 183bce3a77 | |||
| 0b92419829 | |||
| 42ce94472f | |||
| e011a40e21 | |||
| d69e87b2ba | |||
| ec5d9e968a | |||
| bc6bb763aa | |||
| 8f03499b61 | |||
| 0e2dec33de | |||
| c62871aa9d | |||
| baebbcc341 | |||
| 66eca74041 | |||
| 9ee809d685 | |||
| 40acd3985c | |||
| ae0fc8eef2 | |||
| f467121ce0 | |||
| e793a8e368 | |||
| d4fe51c7d7 | |||
| a97610ac5b | |||
| 92a8b46a79 | |||
| 0925ecdbd4 | |||
| 0bff3507f7 | |||
| 957e78388a | |||
| 3377d26e2a | |||
| d3f44d65d3 | |||
| 3853eb7bb8 | |||
| a41c7cd69a | |||
| ecc9ffd797 | |||
| 1928283c91 | |||
| 474e5c539d | |||
| 7074cdc2c8 | |||
| 9772b2e138 | |||
| ad4aad6c29 | |||
| aad80c974d | |||
| 20025aadf5 | |||
| df6a6ed97e | |||
| ec67afb944 | |||
| bdd12dc368 | |||
| 3f1e909ae8 | |||
| 060e6702e7 | |||
| 14a8a5d649 | |||
| f7ced50b6b | |||
| b90ac61667 | |||
| 9cc1dfda95 | |||
| f29ba322b8 | |||
| 1ad4891169 | |||
| 89722fc4d9 | |||
| 43a40d987c | |||
| e316048830 | |||
| 3083a5a80d | |||
| 7a79046285 | |||
| 7ad4cd3728 | |||
| 940608f34f | |||
| 60a9b9736d | |||
| 6e65843268 | |||
| ede4c60b54 | |||
| ed674e43c2 | |||
| f47a36f707 | |||
| d8e0afc38a | |||
| d305985604 | |||
| ca8cf5444e | |||
| 4972dd43dd | |||
| 2f1ed94388 | |||
| f49a92efa5 | |||
| 7fb7ab13dc | |||
| 34d9787dec | |||
| d890ed6adf | |||
| ed10f401df | |||
| df5e6e00fb | |||
| 84eed6a5a4 | |||
| cb857db4f2 | |||
| ff2435d89c | |||
| 111bca500e | |||
| 4701196ce8 | |||
| 8d4f985112 | |||
| ac3f1bdecd | |||
| 953195bf38 | |||
| fa6a5a4b29 | |||
| 6be75c49ec | |||
| 5d9bf8fe28 | |||
| c1143b3932 | |||
| e2df9eabc5 | |||
| 42481b9ca0 | |||
| ecd0e011ee | |||
| e82c80f706 | |||
| a8c51871f4 | |||
| d5067bffd2 | |||
| a3d19efab4 | |||
| 888f55e41e | |||
| f22174be37 | |||
| eff5fd4984 | |||
| 09543c0123 | |||
| 29924b65e3 | |||
| 7819b928b1 | |||
| 151fc538de | |||
| 391d53f871 | |||
| 64f1f9ed5a | |||
| c6631a119c | |||
| 6af01fb2e4 | |||
| 83af30b285 | |||
| 37858eb4a8 | |||
| 569b5eeac1 | |||
| 43a538205f | |||
| 5557ec004c | |||
| fe3d98f0a0 | |||
| 2b9c3f1089 | |||
| e6b2cb22d0 | |||
| 6f1ffbdf1f | |||
| 65faf38134 | |||
| a3b8e03c1c | |||
| daea8b596f | |||
| 84d3af595b | |||
| a402077cf2 | |||
| d199355889 | |||
| bdf593e235 | |||
| 2ca8477af4 | |||
| 947a322df7 | |||
| 0442f44049 | |||
| 43b55f1354 | |||
| 24bcd5b772 | |||
| 07c0e0e090 | |||
| dde68b8c47 | |||
| 2b918bc767 | |||
| f411cdd29a | |||
| f3b6ddec28 | |||
| 091cad4be8 | |||
| d941f7a2bd | |||
| cf91bb38cf | |||
| c487276abc | |||
| 687af3bae7 | |||
| 525db9e050 | |||
| 07d60fe334 | |||
| 3a7b1b4d6e | |||
| 3332b5ed21 | |||
| f05770656d | |||
| 61dec4c904 | |||
| e7bc8b35ee | |||
| 17a25fbe04 | |||
| 168ac5c80a | |||
| b0d6391ab7 | |||
| c74b156127 | |||
| a11e641a3a | |||
| 832292c29e | |||
| f5dedb71e1 | |||
| e7e5880c0f | |||
| 4d6a6fb033 | |||
| f86abcf932 | |||
| 42c6dadcd4 | |||
| cb49d89ba4 | |||
| f2c4f5cd07 | |||
| 3bbf743330 | |||
| edad20f8e4 | |||
| 9c5af7f9ec | |||
| 9a6022b235 | |||
| 90f26d99ab | |||
| 35ed170068 | |||
| c106bcfc54 | |||
| f4e974d2fc | |||
| 9075a96e5d | |||
| a1afcb2e7a | |||
| ed35b34461 | |||
| baa7be6a86 | |||
| acc98001f0 | |||
| e1282ad929 | |||
| e24d2a78e5 | |||
| 7cc659b2b9 | |||
| 030ed9e798 | |||
| 98c19db4c4 | |||
| 344f8adeed | |||
| 506317da3a | |||
| 1e8361252d | |||
| d437812525 | |||
| 87f24237dc | |||
| 927c441b10 | |||
| 138460e98b | |||
| c42a9a49b4 | |||
| 3f80870619 | |||
| 9c52e5ea99 | |||
| cafb171c3a | |||
| e8b9a60126 | |||
| 748117eb47 | |||
| 6c948829cd | |||
| 605d3e8d98 | |||
| ba61f2f91b | |||
| 8cd7ebf239 | |||
| eae54c38f7 | |||
| 6bfe5a229d | |||
| 937b5c4c69 | |||
| cab2c6ed9b | |||
| 808c1d1407 | |||
| 6a6da30b4d | |||
| 5413077874 | |||
| 6ffae61d70 | |||
| ab20b56894 | |||
| e2d400a136 | |||
| 0f7c44657e | |||
| 4c9c07a855 | |||
| 7e60d9f35f | |||
| 25e6630c5d | |||
| d4f2b45455 | |||
| bc270ce303 | |||
| 90667b82e8 | |||
| 09d7853909 | |||
| 179f527ae3 | |||
| e2c4522e8c | |||
| 57ec34a9cc | |||
| 7d02fe560a | |||
| 408efe43c5 | |||
| ea210f78e4 | |||
| debc37f3c2 | |||
| e642c550c4 | |||
| dfc4176c4a | |||
| 796a7c9b67 | |||
| 972146bb1e | |||
| eaba78a8b2 | |||
| e9a8d6e781 | |||
| da59e61407 | |||
| 7bdb7736b3 | |||
| dfd771cddb | |||
| 2327fedbea | |||
| 5c0c0f1ceb | |||
| 2711a4013a | |||
| 5e650c09fc | |||
| f62ab50109 | |||
| 85e6250179 | |||
| 808a369677 | |||
| 29e6d9d9eb | |||
| 604796406c | |||
| 25f240bd6f | |||
| 29c5b2b01f | |||
| dd971a0a45 | |||
| 0a1a11aae4 | |||
| a017a672bb | |||
| 0df5a25aca | |||
| 39a920802e | |||
| 23d1362718 | |||
| 58b6b184b3 | |||
| dcaa783741 | |||
| ad55a7047e | |||
| 550c3afcd8 | |||
| efcf91a9a1 | |||
| 04a30bd4df | |||
| eeb0c2f410 | |||
| 4831903987 | |||
| 991ecfec28 | |||
| f32bb17861 | |||
| 828b40dca1 | |||
| 01872fd505 | |||
| a64fdeacd8 | |||
| 1118af2a3b | |||
| 018aecdeb5 | |||
| fadc44f298 | |||
| 1987a409b8 | |||
| b93b0d6355 | |||
| e3b664447b | |||
| 6155dd23d2 | |||
| 0553a6d90c | |||
| 9223d556fb | |||
| 8e1ab8a382 | |||
| 4cb5500048 | |||
| 9cc403d680 | |||
| 8d98d2c9fd | |||
| 44160cf213 | |||
| 1b57e6b87a | |||
| 885cf9f06a | |||
| 8923fb3bb1 | |||
| e246171b56 | |||
| 1ecc0f0769 | |||
| 21573d41f7 | |||
| b553489c13 | |||
| b7fe40f96a | |||
| 3efb002aaa | |||
| 2bcef03b09 | |||
| 7a13dabb1a | |||
| 97e3be3cf6 | |||
| 7388f721e9 | |||
| 3f9247df88 | |||
| 4ad8be5c62 | |||
| a6b98fe716 | |||
| 435d91e4bb | |||
| baccabf20b | |||
| 9dcb7d057d | |||
| 77fe79ff6d | |||
| 21e0be247c | |||
| 564019efff | |||
| b5dfcb9e07 | |||
| 49681cd03d | |||
| cdcc3ab5da | |||
| 165baf349b | |||
| 5b898cb5e0 | |||
| 24e4f1d1c8 | |||
| 26e2d21925 | |||
| bc1d3f2e2a | |||
| b185b38288 | |||
| 88426ca6bf | |||
| 644ecdcaf3 | |||
| 07b01e4ff7 | |||
| 9b7293b190 | |||
| 8c515e95b7 | |||
| 06c88be18f | |||
| 8cb4579884 | |||
| b53ec2bb74 | |||
| a9883c6ee7 | |||
| 1446f3f89c | |||
| 709cf42e06 | |||
| 0d493433d3 | |||
| 318fd402e1 | |||
| 1671a9f7fd | |||
| 72f0984c4e | |||
| 7da687624c | |||
| 4ecc15449c | |||
| b05f3cdc4b | |||
| 4c627b6a57 | |||
| d0770f8154 | |||
| 25d5103a4c | |||
| 98ab2d0a44 | |||
| d57c35472a | |||
| ed88c64a5c | |||
| f1d125b08e | |||
| 9d84af2a52 | |||
| 1c52897f85 | |||
| 0a9117bd61 | |||
| abe0e8fbad | |||
| a05eb5558e | |||
| 2bf03fc2e0 | |||
| 17aaad9033 | |||
| 22f75de983 | |||
| a825339917 | |||
| 0e91160efe | |||
| 6b1ce97894 | |||
| 38ce5978dd | |||
| 5b4c20944d | |||
| 12c256a78b | |||
| 6b45969dc6 | |||
| 4abab60161 | |||
| 5bdbf3185d | |||
| 159f20a93e | |||
| 24335e6c2b | |||
| fe4763e560 | |||
| 8a80d01b70 | |||
| ee21767d92 | |||
| d1e488ab9b | |||
| 5863a07a46 | |||
| 50a8ba5661 | |||
| 673922a628 | |||
| 3282dda765 | |||
| 5da126bc67 | |||
| b503eca0d4 | |||
| 95e75e70b5 | |||
| d86ad34353 | |||
| c0b2cf55e4 | |||
| bdb8dec713 | |||
| d4fc00666b | |||
| 248f5a1efc | |||
| 036e58c5b5 | |||
| ba7cf5ad88 | |||
| 3f8bd31bf3 | |||
| de8c665fe3 | |||
| 23cd28f38c | |||
| 00c706dc8a | |||
| 93973e0472 | |||
| 3e46ac327a | |||
| a9afba6a38 | |||
| 95764ed91d | |||
| b1dbc661bb | |||
| 809901f7c8 | |||
| f8c209cf25 | |||
| 564ad754a8 | |||
| d940ccbe0b | |||
| cbf62420e0 | |||
| 066673fb37 | |||
| 0bec5987c8 | |||
| 7c5c76bcdf | |||
| 4ef107fccb | |||
| 9addd44bb4 | |||
| 0bc2276751 | |||
| 3612fa390d | |||
| 76e06c15c6 | |||
| ba22230a4f | |||
| cf77a2d72d | |||
| e8fd0db959 | |||
| 99c7dc1929 | |||
| ceba417524 | |||
| 4e5cd5956d | |||
| c116d32f39 | |||
| e2338e14a7 | |||
| 057a4fc85b | |||
| dfcad939ff | |||
| db6be2a885 | |||
| 5dfaf89ba2 | |||
| 0d72003e6e | |||
| 47b4f21994 | |||
| 8e6e56c066 | |||
| f3ad037584 | |||
| 13898c79db | |||
| 323801ad4b | |||
| c013bb2cbc | |||
| 8789bc4fa6 | |||
| f02688e713 | |||
| 5eefd56230 | |||
| 51ee36d039 | |||
| db65a2ca7e | |||
| 6eef6e3ab9 | |||
| 3dbced7596 | |||
| 86bef7c3b5 | |||
| 2436022f22 | |||
| 7fbf87df43 | |||
| ce2e9f1cfb | |||
| 5bf54816e8 | |||
| 560b5b0efc | |||
| b211eb7afe | |||
| fc6d1fe719 | |||
| 93f39f3db6 | |||
| c47c19634e | |||
| 5806a1f251 | |||
| 803d87c60f | |||
| ff27e31a89 | |||
| 949282574a | |||
| c42c551cff | |||
| d3507a690a | |||
| 36a5774a81 | |||
| ace9ed3fc8 | |||
| 9042827e3f | |||
| 8b7eb8620a | |||
| 8223305818 | |||
| eaaaeface3 |
@@ -11,6 +11,8 @@ notifications:
|
||||
|
||||
language: php
|
||||
|
||||
dist: trusty
|
||||
|
||||
php:
|
||||
# We only run the highest and lowest supported versions to reduce the load on travis-ci.org.
|
||||
- 7.2
|
||||
|
||||
@@ -710,7 +710,7 @@ if ($interactive) {
|
||||
cli_separator();
|
||||
cli_heading(get_string('cliadminemail', 'install'));
|
||||
$prompt = get_string('clitypevaluedefault', 'admin', $options['adminemail']);
|
||||
$options['adminemail'] = cli_input($prompt);
|
||||
$options['adminemail'] = cli_input($prompt, $options['adminemail']);
|
||||
}
|
||||
|
||||
// Validate that the address provided was an e-mail address.
|
||||
|
||||
@@ -2799,4 +2799,185 @@
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.8" requires="3.2">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="5.5.31" />
|
||||
<VENDOR name="mysql" version="5.6" />
|
||||
<VENDOR name="postgres" version="9.4" />
|
||||
<VENDOR name="mssql" version="11.0" />
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.1.0" level="required">
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="pcreunicodewarning" />
|
||||
</FEEDBACK>
|
||||
</PCREUNICODE>
|
||||
<PHP_EXTENSIONS>
|
||||
<PHP_EXTENSION name="iconv" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="iconvrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="mbstring" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="mbstringrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="curl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="curlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="openssl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="opensslrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="tokenizer" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="tokenizerrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlrpc" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="xmlrpcrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="soap" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="soaprecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="ctype" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ctyperequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zip" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="ziprequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="zlib" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="gd" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="gdrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="simplexml" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="simplexmlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="spl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="splrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="pcre" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="dom" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xml" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="xmlreader" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="intl" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="intlrequired" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_sixtyfour_bits" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="sixtyfourbitswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
+1
-2
@@ -823,8 +823,7 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
*/
|
||||
protected function registration_warning($registered) {
|
||||
|
||||
if (!$registered) {
|
||||
|
||||
if (!$registered && site_is_public()) {
|
||||
if (has_capability('moodle/site:config', context_system::instance())) {
|
||||
$registerbutton = $this->single_button(new moodle_url('/admin/registration/index.php'),
|
||||
get_string('register', 'admin'));
|
||||
|
||||
@@ -69,8 +69,11 @@ class core_role_check_users_selector extends user_selector_base {
|
||||
|
||||
if ($coursecontext and $coursecontext != SITEID) {
|
||||
$sql1 = " FROM {user} u
|
||||
JOIN {user_enrolments} ue ON (ue.userid = u.id)
|
||||
JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid1)
|
||||
JOIN (SELECT DISTINCT subu.id
|
||||
FROM {user} subu
|
||||
JOIN {user_enrolments} ue ON (ue.userid = subu.id)
|
||||
JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid1)
|
||||
) subq ON subq.id = u.id
|
||||
WHERE $wherecondition";
|
||||
$params['courseid1'] = $coursecontext->instanceid;
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ class core_role_permissions_table extends core_role_capability_table_base {
|
||||
"linkclass" => "preventlink", "adminurl" => $adminurl->out(), "icon" => "", "iconalt" => "");
|
||||
if (isset($overridableroles[$id]) and ($allowoverrides or ($allowsafeoverrides and is_safe_capability($capability)))) {
|
||||
$templatecontext['icon'] = 't/delete';
|
||||
$templatecontext['iconalt'] = get_string('delete');
|
||||
$templatecontext['iconalt'] = get_string('deletexrole', 'core_role', $name);
|
||||
}
|
||||
$neededroles[$id] = $renderer->render_from_template('core/permissionmanager_role', $templatecontext);
|
||||
}
|
||||
@@ -109,7 +109,7 @@ class core_role_permissions_table extends core_role_capability_table_base {
|
||||
"icon" => "", "iconalt" => "");
|
||||
if (isset($overridableroles[$id]) and prohibit_is_removable($id, $context, $capability->name)) {
|
||||
$templatecontext['icon'] = 't/delete';
|
||||
$templatecontext['iconalt'] = get_string('delete');
|
||||
$templatecontext['iconalt'] = get_string('deletexrole', 'core_role', $name);
|
||||
}
|
||||
$forbiddenroles[$id] = $renderer->render_from_template('core/permissionmanager_role', $templatecontext);
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ $arguments = array('contextid' => $contextid,
|
||||
$PAGE->requires->strings_for_js(
|
||||
array('roleprohibitinfo', 'roleprohibitheader', 'roleallowinfo', 'roleallowheader',
|
||||
'confirmunassigntitle', 'confirmroleunprohibit', 'confirmroleprevent', 'confirmunassignyes',
|
||||
'confirmunassignno'), 'core_role');
|
||||
'confirmunassignno', 'deletexrole'), 'core_role');
|
||||
$PAGE->requires->js_call_amd('core/permissionmanager', 'initialize', array($arguments));
|
||||
$table = new core_role_permissions_table($context, $contextname, $allowoverrides, $allowsafeoverrides, $overridableroles);
|
||||
echo $OUTPUT->box_start('generalbox capbox');
|
||||
|
||||
@@ -330,7 +330,7 @@ if ($hassiteconfig or has_any_capability($capabilities, $systemcontext)) {
|
||||
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_skip_modif_prev', new lang_string('skipmodifprev', 'backup'), new lang_string('skipmodifprevhelp', 'backup'), 0));
|
||||
|
||||
// Automated defaults section.
|
||||
$temp->add(new admin_setting_heading('automatedsettings', new lang_string('automatedsettings','backup'), ''));
|
||||
$temp->add(new admin_setting_heading('automatedsettings', new lang_string('automatedsettings','backup'), new lang_string('recyclebin_desc', 'backup')));
|
||||
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_users', new lang_string('generalusers', 'backup'), new lang_string('configgeneralusers', 'backup'), 1));
|
||||
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_role_assignments', new lang_string('generalroleassignments','backup'), new lang_string('configgeneralroleassignments','backup'), 1));
|
||||
$temp->add(new admin_setting_configcheckbox('backup/backup_auto_activities', new lang_string('generalactivities','backup'), new lang_string('configgeneralactivities','backup'), 1));
|
||||
|
||||
@@ -58,7 +58,7 @@ class behat_admin extends behat_base {
|
||||
// We expect admin block to be visible, otherwise go to homepage.
|
||||
if (!$this->getSession()->getPage()->find('css', '.block_settings')) {
|
||||
$this->getSession()->visit($this->locate_path('/'));
|
||||
$this->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
|
||||
$this->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
|
||||
}
|
||||
|
||||
// Search by label.
|
||||
@@ -67,7 +67,7 @@ class behat_admin extends behat_base {
|
||||
$submitsearch = $this->find('css', 'form.adminsearchform input[type=submit]');
|
||||
$submitsearch->press();
|
||||
|
||||
$this->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
|
||||
$this->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
|
||||
|
||||
// Admin settings does not use the same DOM structure than other moodle forms
|
||||
// but we also need to use lib/behat/form_field/* to deal with the different moodle form elements.
|
||||
|
||||
@@ -1,57 +1,74 @@
|
||||
@core @core_admin
|
||||
Feature: Enable multiple accounts to have the same email address
|
||||
In order to have multiple accounts registerd on the system with the same email address
|
||||
@core @core_admin @core_user
|
||||
Feature: Allowing multiple accounts to have the same email address
|
||||
In order to manage user accounts
|
||||
As an admin
|
||||
I need to enable multiple accounts to be registered with the same email address and verify it is applied
|
||||
I need to be able to set whether to allow multiple accounts with the same email or not
|
||||
|
||||
Background:
|
||||
Given I log in as "admin"
|
||||
|
||||
Scenario: Enable registration of multiple accounts with the same email address
|
||||
Scenario Outline: Create a user with the same email as an existing user
|
||||
Given the following config values are set as admin:
|
||||
| allowaccountssameemail | 1 |
|
||||
When I navigate to "Users > Accounts > Add a new user" in site administration
|
||||
| allowaccountssameemail | <allowsameemail> |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| s1 | John | Doe | s1@example.com |
|
||||
When I log in as "admin"
|
||||
And I navigate to "Users > Accounts > Add a new user" in site administration
|
||||
And I set the following fields to these values:
|
||||
| Username | testmultiemailuser1 |
|
||||
| Choose an authentication method | Manual accounts |
|
||||
| New password | test@User1 |
|
||||
| First name | Test |
|
||||
| Surname | Multi1 |
|
||||
| Email address | testmultiemailuser@example.com |
|
||||
| Username | s2 |
|
||||
| First name | Jane |
|
||||
| Surname | Doe |
|
||||
| Email address | <email> |
|
||||
| New password | test |
|
||||
And I press "Create user"
|
||||
And I should see "Test Multi1"
|
||||
And I press "Add a new user"
|
||||
And I set the following fields to these values:
|
||||
| Username | testmultiemailuser2 |
|
||||
| Choose an authentication method | Manual accounts |
|
||||
| New password | test@User2 |
|
||||
| First name | Test |
|
||||
| Surname | Multi2 |
|
||||
| Email address | testmultiemailuser@example.com |
|
||||
And I press "Create user"
|
||||
Then I should see "Test Multi2"
|
||||
And I should not see "This email address is already registered"
|
||||
Then I should <expect> "This email address is already registered."
|
||||
|
||||
Scenario: Disable registration of multiple accounts with the same email address
|
||||
Examples:
|
||||
| allowsameemail | email | expect |
|
||||
| 0 | s1@example.com | see |
|
||||
| 0 | S1@EXAMPLE.COM | see |
|
||||
| 1 | s1@example.com | not see |
|
||||
| 1 | S1@EXAMPLE.COM | not see |
|
||||
|
||||
Scenario Outline: Update a user with the same email as an existing user
|
||||
Given the following config values are set as admin:
|
||||
| allowaccountssameemail | 0 |
|
||||
When I navigate to "Users > Accounts > Add a new user" in site administration
|
||||
And I set the following fields to these values:
|
||||
| Username | testmultiemailuser1 |
|
||||
| Choose an authentication method | Manual accounts |
|
||||
| New password | test@User1 |
|
||||
| First name | Test |
|
||||
| Surname | Multi1 |
|
||||
| Email address | testmultiemailuser@example.com |
|
||||
And I press "Create user"
|
||||
And I should see "Test Multi1"
|
||||
And I press "Add a new user"
|
||||
And I set the following fields to these values:
|
||||
| Username | testmultiemailuser2 |
|
||||
| Choose an authentication method | Manual accounts |
|
||||
| New password | test@User2 |
|
||||
| First name | Test |
|
||||
| Surname | Multi2 |
|
||||
| Email address | testmultiemailuser@example.com |
|
||||
And I press "Create user"
|
||||
Then I should see "This email address is already registered"
|
||||
| allowaccountssameemail | <allowsameemail> |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| s1 | John | Doe | s1@example.com |
|
||||
| s2 | Jane | Doe | s2@example.com |
|
||||
When I log in as "admin"
|
||||
And I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
And I click on "Edit" "link" in the "Jane Doe" "table_row"
|
||||
And I set the field "Email address" to "<email>"
|
||||
And I press "Update profile"
|
||||
Then I should <expect> "This email address is already registered."
|
||||
|
||||
Examples:
|
||||
| allowsameemail | email | expect |
|
||||
| 0 | s1@example.com | see |
|
||||
| 0 | S1@EXAMPLE.COM | see |
|
||||
| 1 | s1@example.com | not see |
|
||||
| 1 | S1@EXAMPLE.COM | not see |
|
||||
| 0 | S2@EXAMPLE.COM | not see |
|
||||
| 1 | S2@EXAMPLE.COM | not see |
|
||||
|
||||
Scenario Outline: Update own user profile with the same email as an existing user
|
||||
Given the following config values are set as admin:
|
||||
| allowaccountssameemail | <allowsameemail> |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| s1 | John | Doe | s1@example.com |
|
||||
| s2 | Jane | Doe | s2@example.com |
|
||||
When I log in as "s2"
|
||||
And I open my profile in edit mode
|
||||
And I set the field "Email address" to "<email>"
|
||||
And I press "Update profile"
|
||||
Then I should <expect> "This email address is already registered."
|
||||
|
||||
Examples:
|
||||
| allowsameemail | email | expect |
|
||||
| 0 | s1@example.com | see |
|
||||
| 0 | S1@EXAMPLE.COM | see |
|
||||
| 1 | s1@example.com | not see |
|
||||
| 1 | S1@EXAMPLE.COM | not see |
|
||||
| 0 | S2@EXAMPLE.COM | not see |
|
||||
| 1 | S2@EXAMPLE.COM | not see |
|
||||
|
||||
@@ -141,7 +141,7 @@ class models_list implements \renderable, \templatable {
|
||||
if ($contextid == SYSCONTEXTID) {
|
||||
$contextname = get_string('allpredictions', 'tool_analytics');
|
||||
} else {
|
||||
$contextname = shorten_text($context->get_context_name(true, true), 90);
|
||||
$contextname = shorten_text($context->get_context_name(false, true), 40);
|
||||
}
|
||||
$predictioncontexts[$contextid] = $contextname;
|
||||
}
|
||||
|
||||
@@ -173,7 +173,8 @@ function tool_analytics_calculate_course_dates($course, $options) {
|
||||
$formatoptions = $format->get_format_options();
|
||||
|
||||
// Change this for a course formats API level call in MDL-60702.
|
||||
if (method_exists($format, 'update_end_date') && $formatoptions['automaticenddate']) {
|
||||
if ((get_class($format) == 'format_weeks' || is_subclass_of($format, 'format_weeks')) &&
|
||||
method_exists($format, 'update_end_date') && $formatoptions['automaticenddate']) {
|
||||
// Special treatment for weeks-based formats with automatic end date.
|
||||
|
||||
if ($options['update']) {
|
||||
|
||||
@@ -30,7 +30,7 @@ $string['errordataroot'] = '$CFG->behat_dataroot is not set or is invalid.';
|
||||
$string['errorsetconfig'] = '$CFG->behat_dataroot, $CFG->behat_prefix and $CFG->behat_wwwroot need to be set in config.php.';
|
||||
$string['erroruniqueconfig'] = '$CFG->behat_dataroot, $CFG->behat_prefix and $CFG->behat_wwwroot values need to be different than $CFG->dataroot, $CFG->prefix, $CFG->wwwroot, $CFG->phpunit_dataroot and $CFG->phpunit_prefix values.';
|
||||
$string['fieldvalueargument'] = 'Field value arguments';
|
||||
$string['fieldvalueargument_help'] = 'This argument should be completed by a field value. There are many field types, including simple ones like checkboxes, selects or textareas, or complex ones like date selectors. See the dev documentation <a href="http://docs.moodle.org/dev/Acceptance_testing" target="_blank">Acceptance_testing</a> for details of expected field values.';
|
||||
$string['fieldvalueargument_help'] = 'This argument should be completed by a field value. There are many field types, including simple ones like checkboxes, selects or textareas, or complex ones like date selectors. See the dev documentation <a href="https://docs.moodle.org/dev/Acceptance_testing" target="_blank">Acceptance_testing</a> for details of expected field values.';
|
||||
$string['giveninfo'] = 'Given. Processes to set up the environment';
|
||||
$string['infoheading'] = 'Info';
|
||||
$string['installinfo'] = 'Read {$a} for installation and tests execution info';
|
||||
|
||||
@@ -162,11 +162,11 @@ class tool_behat_renderer extends plugin_renderer_base {
|
||||
$html .= $this->output->heading($title);
|
||||
|
||||
// Info.
|
||||
$installurl = behat_command::DOCS_URL . '#Installation';
|
||||
$installurl = behat_command::DOCS_URL;
|
||||
$installlink = html_writer::tag('a', $installurl, array('href' => $installurl, 'target' => '_blank'));
|
||||
$writetestsurl = behat_command::DOCS_URL . '#Writing_features';
|
||||
$writetestsurl = 'https://docs.moodle.org/dev/Writing acceptance tests';
|
||||
$writetestslink = html_writer::tag('a', $writetestsurl, array('href' => $writetestsurl, 'target' => '_blank'));
|
||||
$writestepsurl = behat_command::DOCS_URL . '#Adding_steps_definitions';
|
||||
$writestepsurl = 'https://docs.moodle.org/dev/Writing_new_acceptance_test_step_definitions';
|
||||
$writestepslink = html_writer::tag('a', $writestepsurl, array('href' => $writestepsurl, 'target' => '_blank'));
|
||||
$infos = array(
|
||||
get_string('installinfo', 'tool_behat', $installlink),
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
}} data-status="{{status}}"{{!
|
||||
}}>
|
||||
<td>{{typename}}</td>
|
||||
<td>{{#userdate}} {{timecreated}}, {{#str}} strftimedatetime {{/str}} {{/userdate}}</td>
|
||||
<td>{{#userdate}} {{timecreated}}, {{#str}} strftimedatetime, core_langconfig {{/str}} {{/userdate}}</td>
|
||||
<td><a href="{{requestedbyuser.profileurl}}" title="{{#str}}viewprofile{{/str}}">{{requestedbyuser.fullname}}</a></td>
|
||||
<td>
|
||||
<span class="label {{statuslabelclass}}" title="{{statuslabeltitle}}">{{statuslabel}}</span>
|
||||
|
||||
@@ -67,7 +67,8 @@
|
||||
<a href="mailto:{{foruser.email}}">{{foruser.email}}</a>
|
||||
<div class="clearfix m-t-1 m-b-1">
|
||||
<span class="pull-left m-r-1">
|
||||
<strong>{{#str}}daterequesteddetail, tool_dataprivacy{{/str}}</strong> {{#userdate}} {{timecreated}}, {{#str}} strftimedatetime {{/str}} {{/userdate}}
|
||||
<strong>{{#str}}daterequesteddetail, tool_dataprivacy{{/str}}</strong>
|
||||
{{#userdate}} {{timecreated}}, {{#str}} strftimedatetime, core_langconfig {{/str}} {{/userdate}}
|
||||
</span>
|
||||
<span class="pull-left m-r-1">
|
||||
<strong>{{#str}}statusdetail, tool_dataprivacy{{/str}}</strong>
|
||||
|
||||
@@ -137,14 +137,14 @@ abstract class restore_tool_log_logstore_subplugin extends restore_subplugin {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now we want to serialize it so we can store it in the DB.
|
||||
$data->other = serialize($data->other);
|
||||
} else {
|
||||
$message = "Event class not found: \"$eventclass\". Skipping log record.";
|
||||
$this->log($message, backup::LOG_DEBUG);
|
||||
return; // No such class, can not restore.
|
||||
}
|
||||
}
|
||||
// Now we want to serialize it so we can store it in the DB.
|
||||
$data->other = serialize($data->other);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
$string['actlogshdr'] = 'Available log stores';
|
||||
$string['configlogplugins'] = 'Please enable all required plugins and arrange them in appropriate order.';
|
||||
$string['exportlog'] = 'Include logs when exporting.';
|
||||
$string['exportlog'] = 'Include logs when exporting';
|
||||
$string['exportlogdetail'] = 'Include logs that relate to the user when exporting.';
|
||||
$string['logging'] = 'Logging';
|
||||
$string['managelogging'] = 'Manage log stores';
|
||||
|
||||
@@ -422,6 +422,7 @@ class external extends external_api {
|
||||
'comppath' => competency_path_exporter::get_read_structure(),
|
||||
))),
|
||||
'manageurl' => new external_value(PARAM_LOCALURL, 'Url to the manage competencies page.'),
|
||||
'pluginbaseurl' => new external_value(PARAM_LOCALURL, 'Url to the course competencies page.'),
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ class course_competencies_page implements renderable, templatable {
|
||||
$data->courseid = $this->courseid;
|
||||
$data->pagecontextid = $this->context->id;
|
||||
$data->competencies = array();
|
||||
$data->pluginbaseurl = (new moodle_url('/admin/tool/lp'))->out(true);
|
||||
|
||||
$gradable = is_enrolled($this->context, $USER, 'moodle/competency:coursecompetencygradable');
|
||||
if ($gradable) {
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<div class="clearfix"></div>
|
||||
{{/canmanagecoursecompetencies}}
|
||||
{{#competency}}
|
||||
<a href="{{pluginbaseurl}}user_competency_in_course.php?courseid={{courseid}}&competencyid={{competency.id}}&userid={{gradableuserid}}"
|
||||
<a href="{{pluginbaseurl}}/user_competency_in_course.php?courseid={{courseid}}&competencyid={{competency.id}}&userid={{gradableuserid}}"
|
||||
id="competency-info-link-{{competency.id}}"
|
||||
title="{{#str}}viewdetails, tool_lp{{/str}}">
|
||||
<p><strong>{{{competency.shortname}}} <em>{{competency.idnumber}}</em></strong></p>
|
||||
|
||||
@@ -672,7 +672,7 @@ class manager {
|
||||
$attachment->charset = $partdata->getCharset();
|
||||
$attachment->description = $partdata->getDescription();
|
||||
$attachment->contentid = $partdata->getContentId();
|
||||
$attachment->filesize = $messagedata->getBodyPartSize($part);
|
||||
$attachment->filesize = $partdata->getBytes();
|
||||
|
||||
if (!empty($CFG->antiviruses)) {
|
||||
mtrace("--> Attempting virus scan of '{$attachment->filename}'");
|
||||
|
||||
@@ -99,7 +99,7 @@ class api {
|
||||
$lang = array();
|
||||
if (!empty($addoninfo['lang'])) {
|
||||
$stringmanager = get_string_manager();
|
||||
$langs = $stringmanager->get_list_of_translations();
|
||||
$langs = $stringmanager->get_list_of_translations(true);
|
||||
foreach ($langs as $langid => $langname) {
|
||||
foreach ($addoninfo['lang'] as $stringinfo) {
|
||||
$lang[$langid][$stringinfo[0]] =
|
||||
|
||||
@@ -66,6 +66,12 @@ function tool_mobile_create_app_download_url() {
|
||||
}
|
||||
|
||||
$downloadurl = new moodle_url($mobilesettings->setuplink);
|
||||
|
||||
// Do not update the URL if it is a custom one (we may break it completely).
|
||||
if ($mobilesettings->setuplink != 'https://download.moodle.org/mobile') {
|
||||
return $downloadurl;
|
||||
}
|
||||
|
||||
$downloadurl->param('version', $CFG->version);
|
||||
$downloadurl->param('lang', current_language());
|
||||
|
||||
|
||||
@@ -301,6 +301,8 @@ class page_agreedocs implements renderable, templatable {
|
||||
redirect(new moodle_url('/admin/tool/policy/view.php', $urlparams));
|
||||
}
|
||||
} else {
|
||||
// Update the policyagreed for the user to avoid infinite loop because there are no policies to-be-accepted.
|
||||
api::update_policyagreed($userid);
|
||||
$this->redirect_to_previous_url();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ Feature: Viewing acceptances reports and accepting on behalf of other users
|
||||
And I press "Next"
|
||||
And I set the field "I agree to the This site policy" to "1"
|
||||
And I press "Next"
|
||||
And I should not see "Next"
|
||||
And I should see "Course overview"
|
||||
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
|
||||
And I click on "1 of 4 (25%)" "link" in the "This site policy" "table_row"
|
||||
And I click on "Accept This site policy" "link" in the "User One" "table_row"
|
||||
@@ -292,3 +292,23 @@ Feature: Viewing acceptances reports and accepting on behalf of other users
|
||||
When I press "Give consent"
|
||||
Then "Accepted on user's behalf" "text" should exist in the "User One" "table_row"
|
||||
And "Accepted on user's behalf" "text" should exist in the "User Two" "table_row"
|
||||
|
||||
Scenario: View acceptances made by users on their own after inactivating a policy
|
||||
Given I log in as "user1"
|
||||
And I should see "This site policy"
|
||||
And I should not see "Course overview"
|
||||
And I press "Next"
|
||||
And I set the field "I agree to the This site policy" to "1"
|
||||
And I press "Next"
|
||||
And I should see "Course overview"
|
||||
And I log out
|
||||
And I log in as "admin"
|
||||
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
|
||||
And I click on "Actions" "link_or_button" in the "This privacy policy" "table_row"
|
||||
And I click on "Set status to \"Active\"" "link" in the "This privacy policy" "table_row"
|
||||
And I press "Continue"
|
||||
And I click on "Set status to \"Inactive\"" "link" in the "This privacy policy" "table_row"
|
||||
And I press "Continue"
|
||||
And I log out
|
||||
When I log in as "user1"
|
||||
Then I should see "Course overview"
|
||||
|
||||
@@ -108,6 +108,16 @@ class category_bin extends base_bin {
|
||||
|
||||
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
|
||||
|
||||
// As far as recycle bin is using MODE_AUTOMATED, it observes the backup_auto_storage
|
||||
// setting (storing backups @ real location. For recycle bin we want to ensure that
|
||||
// backup files are always stored in Moodle file area. In order to achieve that, we
|
||||
// hack the setting here via $CFG->forced_plugin_settings, so it won't interfere other
|
||||
// operations. See MDL-65218 for more information.
|
||||
// This hack will be removed once recycle bin switches to use its own backup mode, with
|
||||
// own preferences and 100% appart from MODLE_AUTOMATED.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
$CFG->forced_plugin_settings['backup'] = ['backup_auto_storage' => 0];
|
||||
|
||||
// Backup the course.
|
||||
$user = get_admin();
|
||||
$controller = new \backup_controller(
|
||||
@@ -115,11 +125,15 @@ class category_bin extends base_bin {
|
||||
$course->id,
|
||||
\backup::FORMAT_MOODLE,
|
||||
\backup::INTERACTIVE_NO,
|
||||
\backup::MODE_GENERAL,
|
||||
\backup::MODE_AUTOMATED,
|
||||
$user->id
|
||||
);
|
||||
$controller->execute_plan();
|
||||
|
||||
// We don't need the forced setting anymore, hence unsetting it.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
unset($CFG->forced_plugin_settings['backup']);
|
||||
|
||||
// Grab the result.
|
||||
$result = $controller->get_results();
|
||||
if (!isset($result['backup_destination'])) {
|
||||
@@ -233,7 +247,7 @@ class category_bin extends base_bin {
|
||||
$tempdir,
|
||||
$course->id,
|
||||
\backup::INTERACTIVE_NO,
|
||||
\backup::MODE_GENERAL,
|
||||
\backup::MODE_AUTOMATED,
|
||||
$user->id,
|
||||
\backup::TARGET_NEW_COURSE
|
||||
);
|
||||
|
||||
@@ -112,6 +112,16 @@ class course_bin extends base_bin {
|
||||
return;
|
||||
}
|
||||
|
||||
// As far as recycle bin is using MODE_AUTOMATED, it observes the backup_auto_storage
|
||||
// setting (storing backups @ real location. For recycle bin we want to ensure that
|
||||
// backup files are always stored in Moodle file area. In order to achieve that, we
|
||||
// hack the setting here via $CFG->forced_plugin_settings, so it won't interfere other
|
||||
// operations. See MDL-65218 for more information.
|
||||
// This hack will be removed once recycle bin switches to use its own backup mode, with
|
||||
// own preferences and 100% appart from MODLE_AUTOMATED.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
$CFG->forced_plugin_settings['backup'] = ['backup_auto_storage' => 0];
|
||||
|
||||
// Backup the activity.
|
||||
$user = get_admin();
|
||||
$controller = new \backup_controller(
|
||||
@@ -119,11 +129,15 @@ class course_bin extends base_bin {
|
||||
$cm->id,
|
||||
\backup::FORMAT_MOODLE,
|
||||
\backup::INTERACTIVE_NO,
|
||||
\backup::MODE_GENERAL,
|
||||
\backup::MODE_AUTOMATED,
|
||||
$user->id
|
||||
);
|
||||
$controller->execute_plan();
|
||||
|
||||
// We don't need the forced setting anymore, hence unsetting it.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
unset($CFG->forced_plugin_settings['backup']);
|
||||
|
||||
// Grab the result.
|
||||
$result = $controller->get_results();
|
||||
if (!isset($result['backup_destination'])) {
|
||||
@@ -224,7 +238,7 @@ class course_bin extends base_bin {
|
||||
$tempdir,
|
||||
$this->_courseid,
|
||||
\backup::INTERACTIVE_NO,
|
||||
\backup::MODE_GENERAL,
|
||||
\backup::MODE_AUTOMATED,
|
||||
$user->id,
|
||||
\backup::TARGET_EXISTING_ADDING
|
||||
);
|
||||
|
||||
@@ -176,4 +176,116 @@ class tool_recyclebin_category_bin_tests extends advanced_testcase {
|
||||
$course = reset($courses);
|
||||
$this->assertEquals('Test course 2', $course->fullname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider for test_course_restore_with_userdata() and test_course_restore_without_userdata()
|
||||
*
|
||||
* Used to verify that recycle bin is immune to various settings. Provides plugin, name, value for
|
||||
* direct usage with set_config()
|
||||
*/
|
||||
public function recycle_bin_settings_provider() {
|
||||
return [
|
||||
'backup/backup_auto_storage moodle' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 0],
|
||||
]],
|
||||
|
||||
'backup/backup_auto_storage external' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 1],
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
|
||||
]],
|
||||
|
||||
'backup/backup_auto_storage mixed' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 2],
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
|
||||
]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user data is restored when course is restored.
|
||||
*
|
||||
* @dataProvider recycle_bin_settings_provider
|
||||
* @param array $settings array of plugin, name, value stdClass().
|
||||
*/
|
||||
public function test_course_restore_with_userdata($settings) {
|
||||
global $DB;
|
||||
|
||||
// Force configuration changes from provider.
|
||||
foreach ($settings as $setting) {
|
||||
// Need to create a directory for backup_auto_destination.
|
||||
if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
|
||||
$setting->value = make_request_directory();
|
||||
}
|
||||
set_config($setting->name, $setting->value, $setting->plugin);
|
||||
}
|
||||
|
||||
// We want user data to be included for this test.
|
||||
set_config('backup_auto_users', true, 'backup');
|
||||
|
||||
$student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
|
||||
|
||||
// Delete course.
|
||||
delete_course($this->course, false);
|
||||
$this->assertFalse($DB->record_exists('course', ['id' => $this->course->id]));
|
||||
|
||||
// Verify there is now a backup @ cat recycle bin file area.
|
||||
$recyclebin = new \tool_recyclebin\category_bin($this->course->category);
|
||||
$this->assertEquals(1, count($recyclebin->get_items()));
|
||||
|
||||
// Restore the recycle bin item.
|
||||
$recyclebin->restore_item(current($recyclebin->get_items()));
|
||||
|
||||
// Get the new course.
|
||||
$newcourse = $DB->get_record('course', ['shortname' => $this->course->shortname], '*', MUST_EXIST);
|
||||
|
||||
// Check that it was removed from the recycle bin.
|
||||
$this->assertEquals(0, count($recyclebin->get_items()));
|
||||
|
||||
// Verify that student DOES continue enrolled.
|
||||
$this->assertTrue(is_enrolled(context_course::instance($newcourse->id), $student->id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user data is not restored when course is restored.
|
||||
*
|
||||
* @dataProvider recycle_bin_settings_provider
|
||||
* @param array $settings array of plugin, name, value stdClass().
|
||||
*/
|
||||
public function test_course_restore_without_userdata($settings) {
|
||||
global $DB;
|
||||
|
||||
// Force configuration changes from provider.
|
||||
foreach ($settings as $setting) {
|
||||
// Need to create a directory for backup_auto_destination.
|
||||
if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
|
||||
$setting->value = make_request_directory();
|
||||
}
|
||||
set_config($setting->name, $setting->value, $setting->plugin);
|
||||
}
|
||||
|
||||
// We want user data to be included for this test.
|
||||
set_config('backup_auto_users', false, 'backup');
|
||||
|
||||
$student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
|
||||
|
||||
// Delete course.
|
||||
delete_course($this->course, false);
|
||||
$this->assertFalse($DB->record_exists('course', ['id' => $this->course->id]));
|
||||
|
||||
// Verify there is now a backup @ cat recycle bin file area.
|
||||
$recyclebin = new \tool_recyclebin\category_bin($this->course->category);
|
||||
$this->assertEquals(1, count($recyclebin->get_items()));
|
||||
|
||||
// Restore the recycle bin item.
|
||||
$recyclebin->restore_item(current($recyclebin->get_items()));
|
||||
|
||||
// Get the new course.
|
||||
$newcourse = $DB->get_record('course', ['shortname' => $this->course->shortname], '*', MUST_EXIST);
|
||||
|
||||
// Check that it was removed from the recycle bin.
|
||||
$this->assertEquals(0, count($recyclebin->get_items()));
|
||||
|
||||
// Verify that student DOES NOT continue enrolled.
|
||||
$this->assertFalse(is_enrolled(context_course::instance($newcourse->id), $student->id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class tool_recyclebin_course_bin_tests extends advanced_testcase {
|
||||
|
||||
$this->course = $this->getDataGenerator()->create_course();
|
||||
$this->quiz = $this->getDataGenerator()->get_plugin_generator('mod_quiz')->create_instance(array(
|
||||
'course' => $this->course->id
|
||||
'course' => $this->course->id, 'grade' => 100.0, 'sumgrades' => 1
|
||||
));
|
||||
}
|
||||
|
||||
@@ -173,4 +173,147 @@ class tool_recyclebin_course_bin_tests extends advanced_testcase {
|
||||
$deletedbook = reset($items);
|
||||
$this->assertEquals($book->name, $deletedbook->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider for test_coursemodule_restore_with_userdata() and test_coursemodule_restore_without_userdata()
|
||||
*
|
||||
* Used to verify that recycle bin is immune to various settings. Provides plugin, name, value for
|
||||
* direct usage with set_config()
|
||||
*/
|
||||
public function recycle_bin_settings_provider() {
|
||||
return [
|
||||
'backup/backup_auto_storage moodle' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 0],
|
||||
]],
|
||||
|
||||
'backup/backup_auto_storage external' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 1],
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
|
||||
]],
|
||||
|
||||
'backup/backup_auto_storage mixed' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 2],
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
|
||||
]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user data is restored when module is restored.
|
||||
*
|
||||
* @dataProvider recycle_bin_settings_provider
|
||||
* @param array $settings array of plugin, name, value stdClass().
|
||||
*/
|
||||
public function test_coursemodule_restore_with_userdata($settings) {
|
||||
// Force configuration changes from provider.
|
||||
foreach ($settings as $setting) {
|
||||
// Need to create a directory for backup_auto_destination.
|
||||
if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
|
||||
$setting->value = make_request_directory();
|
||||
}
|
||||
set_config($setting->name, $setting->value, $setting->plugin);
|
||||
}
|
||||
|
||||
$student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
|
||||
$this->setUser($student);
|
||||
|
||||
set_config('backup_auto_users', true, 'backup');
|
||||
$this->create_quiz_attempt($this->quiz, $student);
|
||||
|
||||
// Delete quiz.
|
||||
$cm = get_coursemodule_from_instance('quiz', $this->quiz->id);
|
||||
course_delete_module($cm->id);
|
||||
phpunit_util::run_all_adhoc_tasks();
|
||||
$quizzes = get_coursemodules_in_course('quiz', $this->course->id);
|
||||
$this->assertEquals(0, count($quizzes));
|
||||
|
||||
// Restore quiz.
|
||||
$recyclebin = new \tool_recyclebin\course_bin($this->course->id);
|
||||
foreach ($recyclebin->get_items() as $item) {
|
||||
$recyclebin->restore_item($item);
|
||||
}
|
||||
$quizzes = get_coursemodules_in_course('quiz', $this->course->id);
|
||||
$this->assertEquals(1, count($quizzes));
|
||||
$cm = array_pop($quizzes);
|
||||
|
||||
// Check if user quiz attempt data is restored.
|
||||
$attempts = quiz_get_user_attempts($cm->instance, $student->id);
|
||||
$this->assertEquals(1, count($attempts));
|
||||
$attempt = array_pop($attempts);
|
||||
$attemptobj = quiz_attempt::create($attempt->id);
|
||||
$this->assertEquals($student->id, $attemptobj->get_userid());
|
||||
$this->assertEquals(true, $attemptobj->is_finished());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user data is not restored when module is restored.
|
||||
*
|
||||
* @dataProvider recycle_bin_settings_provider
|
||||
* @param array $settings array of plugin, name, value stdClass().
|
||||
*/
|
||||
public function test_coursemodule_restore_without_userdata($settings) {
|
||||
// Force configuration changes from provider.
|
||||
foreach ($settings as $setting) {
|
||||
// Need to create a directory for backup_auto_destination.
|
||||
if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
|
||||
$setting->value = make_request_directory();
|
||||
}
|
||||
set_config($setting->name, $setting->value, $setting->plugin);
|
||||
}
|
||||
|
||||
$student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
|
||||
$this->setUser($student);
|
||||
|
||||
set_config('backup_auto_users', false, 'backup');
|
||||
$this->create_quiz_attempt($this->quiz, $student);
|
||||
|
||||
// Delete quiz.
|
||||
$cm = get_coursemodule_from_instance('quiz', $this->quiz->id);
|
||||
course_delete_module($cm->id);
|
||||
phpunit_util::run_all_adhoc_tasks();
|
||||
$quizzes = get_coursemodules_in_course('quiz', $this->course->id);
|
||||
$this->assertEquals(0, count($quizzes));
|
||||
|
||||
// Restore quiz.
|
||||
$recyclebin = new \tool_recyclebin\course_bin($this->course->id);
|
||||
foreach ($recyclebin->get_items() as $item) {
|
||||
$recyclebin->restore_item($item);
|
||||
}
|
||||
$quizzes = get_coursemodules_in_course('quiz', $this->course->id);
|
||||
$this->assertEquals(1, count($quizzes));
|
||||
$cm = array_pop($quizzes);
|
||||
|
||||
// Check if user quiz attempt data is restored.
|
||||
$attempts = quiz_get_user_attempts($cm->instance, $student->id);
|
||||
$this->assertEquals(0, count($attempts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a question to quiz and create a quiz attempt.
|
||||
* @param \stdClass $quiz Quiz
|
||||
* @param \stdClass $student User
|
||||
* @throws coding_exception
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
private function create_quiz_attempt($quiz, $student) {
|
||||
// Add Question.
|
||||
$questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
$cat = $questiongenerator->create_question_category();
|
||||
$numq = $questiongenerator->create_question('numerical', null, array('category' => $cat->id));
|
||||
quiz_add_quiz_question($numq->id, $quiz);
|
||||
|
||||
// Create quiz attempt.
|
||||
$quizobj = quiz::create($quiz->id, $student->id);
|
||||
$quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
|
||||
$quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
|
||||
$timenow = time();
|
||||
$attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $student->id);
|
||||
quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
|
||||
quiz_attempt_save_started($quizobj, $quba, $attempt);
|
||||
$attemptobj = quiz_attempt::create($attempt->id);
|
||||
$tosubmit = array(1 => array('answer' => '0'));
|
||||
$attemptobj->process_submitted_actions($timenow, false, $tosubmit);
|
||||
$attemptobj = quiz_attempt::create($attempt->id);
|
||||
$attemptobj->process_finish($timenow, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ $string['spamauto'] = 'Autodetect common spam patterns';
|
||||
$string['spamcannotdelete'] = 'Cannot delete this user';
|
||||
$string['spamcannotfinduser'] = 'No users matching your search';
|
||||
$string['spamcleanerintro'] = '<p>This script allows you to search all user profiles for certain strings and then delete those accounts which are obviously created by spammers. You can search for multiple keywords using commas (e.g. casino, porn).</p>
|
||||
<p>For further information, see the documentation <a href="http://docs.moodle.org/en/Reducing_spam_in_Moodle">Reducing spam in Moodle</a>.</p>';
|
||||
<p>For further information, see the documentation <a href="https://docs.moodle.org/en/Reducing_spam_in_Moodle">Reducing spam in Moodle</a>.</p>';
|
||||
$string['spamdeleteall'] = 'Delete all these user accounts';
|
||||
$string['spamdeleteallconfirm'] = 'Are you sure you want to delete all these user accounts? You can not undo this.';
|
||||
$string['spamdeleteconfirm'] = 'Are you sure you want to delete this entry? You can not undo this.';
|
||||
|
||||
@@ -50,7 +50,7 @@ Options:
|
||||
-h, --help Print out this help
|
||||
|
||||
Example:
|
||||
\$sudo -u www-data /usr/bin/php admin/tool/task/cli/scheduled_task.php --execute=\\\\core\\\\task\\\\session_cleanup_task
|
||||
\$sudo -u www-data /usr/bin/php admin/tool/task/cli/schedule_task.php --execute=\\\\core\\\\task\\\\session_cleanup_task
|
||||
|
||||
";
|
||||
|
||||
|
||||
@@ -703,10 +703,18 @@ class tool_uploadcourse_course {
|
||||
$coursedata += $courseformat->validate_course_format_options($this->rawdata);
|
||||
}
|
||||
|
||||
// Special case, 'numsections' is not a course format option any more but still should apply from defaults.
|
||||
// Special case, 'numsections' is not a course format option any more but still should apply from the template course,
|
||||
// if any, and otherwise from defaults.
|
||||
if (!$exists || !array_key_exists('numsections', $coursedata)) {
|
||||
if (isset($this->rawdata['numsections']) && is_numeric($this->rawdata['numsections'])) {
|
||||
$coursedata['numsections'] = (int)$this->rawdata['numsections'];
|
||||
} else if (isset($this->options['templatecourse'])) {
|
||||
$numsections = tool_uploadcourse_helper::get_coursesection_count($this->options['templatecourse']);
|
||||
if ($numsections != 0) {
|
||||
$coursedata['numsections'] = $numsections;
|
||||
} else {
|
||||
$coursedata['numsections'] = get_config('moodlecourse', 'numsections');
|
||||
}
|
||||
} else {
|
||||
$coursedata['numsections'] = get_config('moodlecourse', 'numsections');
|
||||
}
|
||||
|
||||
@@ -286,6 +286,25 @@ class tool_uploadcourse_helper {
|
||||
return $roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to detect how many sections a course with a given shortname has.
|
||||
*
|
||||
* @param string $shortname shortname of a course to count sections from.
|
||||
* @return integer count of sections.
|
||||
*/
|
||||
public static function get_coursesection_count($shortname) {
|
||||
global $DB;
|
||||
if (!empty($shortname) || is_numeric($shortname)) {
|
||||
// Creating restore from an existing course.
|
||||
$course = $DB->get_record('course', array('shortname' => $shortname));
|
||||
}
|
||||
if (!empty($course)) {
|
||||
$courseformat = course_get_format($course);
|
||||
return $courseformat->get_last_section_number();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the role renaming data from the passed data.
|
||||
*
|
||||
|
||||
@@ -183,6 +183,7 @@ function uu_validate_user_upload_columns(csv_import_reader $cir, $stdfields, $pr
|
||||
$processed = array();
|
||||
foreach ($columns as $key=>$unused) {
|
||||
$field = $columns[$key];
|
||||
$field = trim($field);
|
||||
$lcfield = core_text::strtolower($field);
|
||||
if (in_array($field, $stdfields) or in_array($lcfield, $stdfields)) {
|
||||
// standard fields are only lowercase
|
||||
@@ -352,6 +353,7 @@ function uu_allowed_roles() {
|
||||
*/
|
||||
function uu_allowed_roles_cache() {
|
||||
$allowedroles = get_assignable_roles(context_course::instance(SITEID), ROLENAME_SHORT);
|
||||
$rolecache = [];
|
||||
foreach ($allowedroles as $rid=>$rname) {
|
||||
$rolecache[$rid] = new stdClass();
|
||||
$rolecache[$rid]->id = $rid;
|
||||
@@ -371,6 +373,7 @@ function uu_allowed_roles_cache() {
|
||||
*/
|
||||
function uu_allowed_sysroles_cache() {
|
||||
$allowedroles = get_assignable_roles(context_system::instance(), ROLENAME_SHORT);
|
||||
$rolecache = [];
|
||||
foreach ($allowedroles as $rid => $rname) {
|
||||
$rolecache[$rid] = new stdClass();
|
||||
$rolecache[$rid]->id = $rid;
|
||||
|
||||
@@ -796,8 +796,7 @@ class manager {
|
||||
// the format filename => version. The version value needs to
|
||||
// be increased if the tour has been updated.
|
||||
$shippedtours = [
|
||||
'36_dashboard.json' => 3,
|
||||
'36_messaging.json' => 3,
|
||||
'36_dashboard.json' => 3
|
||||
];
|
||||
|
||||
// These are tours that we used to ship but don't ship any longer.
|
||||
@@ -807,6 +806,12 @@ class manager {
|
||||
'boost_course_view.json' => 1,
|
||||
];
|
||||
|
||||
if ($CFG->messaging) {
|
||||
$shippedtours['36_messaging.json'] = 3;
|
||||
} else {
|
||||
$unshippedtours['36_messaging.json'] = 3;
|
||||
}
|
||||
|
||||
$existingtourrecords = $DB->get_recordset('tool_usertours_tours');
|
||||
|
||||
// Get all of the existing shipped tours and check if they need to be
|
||||
|
||||
@@ -57,5 +57,12 @@ function xmldb_tool_usertours_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.6.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2018120301) {
|
||||
// Update the tours shipped with Moodle.
|
||||
manager::update_shipped_tours();
|
||||
|
||||
upgrade_plugin_savepoint(true, 2018120301, 'tool', 'usertours');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018120300; // The current module version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2018120301; // The current module version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2018112800; // Requires this Moodle version.
|
||||
$plugin->component = 'tool_usertours'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -38,9 +38,6 @@ class load_xml_file extends XMLDBAction {
|
||||
$this->can_subaction = ACTION_NONE;
|
||||
//$this->can_subaction = ACTION_HAVE_SUBACTIONS;
|
||||
|
||||
// Set own custom attributes
|
||||
$this->sesskey_protected = false; // This action doesn't need sesskey protection
|
||||
|
||||
// Get needed strings
|
||||
$this->loadStrings(array(
|
||||
// 'key' => 'module',
|
||||
|
||||
@@ -169,7 +169,7 @@ class main_view extends XMLDBAction {
|
||||
file_exists($key . '/install.xml') &&
|
||||
is_readable($key . '/install.xml') &&
|
||||
empty($dbdir->xml_loaded)) {
|
||||
$b .= '<a href="index.php?action=load_xml_file&dir=' . urlencode(str_replace($CFG->dirroot, '', $key)) . '&time=' . time() . '&postaction=main_view#lastused">[' . $this->str['load'] . ']</a>';
|
||||
$b .= '<a href="index.php?action=load_xml_file&sesskey=' . sesskey() . '&dir=' . urlencode(str_replace($CFG->dirroot, '', $key)) . '&time=' . time() . '&postaction=main_view#lastused">[' . $this->str['load'] . ']</a>';
|
||||
} else {
|
||||
$b .= '[' . $this->str['load'] . ']';
|
||||
}
|
||||
@@ -239,7 +239,7 @@ class main_view extends XMLDBAction {
|
||||
is_readable($key . '/install.xml') &&
|
||||
!empty($dbdir->xml_loaded) &&
|
||||
empty($dbdir->xml_changed)) {
|
||||
$b .= '<a href="index.php?action=unload_xml_file&dir=' . urlencode(str_replace($CFG->dirroot, '', $key)) . '&time=' . time() . '&postaction=main_view#lastused">[' . $this->str['unload'] . ']</a>';
|
||||
$b .= '<a href="index.php?action=unload_xml_file&sesskey=' . sesskey() . '&dir=' . urlencode(str_replace($CFG->dirroot, '', $key)) . '&time=' . time() . '&postaction=main_view#lastused">[' . $this->str['unload'] . ']</a>';
|
||||
} else {
|
||||
$b .= '[' . $this->str['unload'] . ']';
|
||||
}
|
||||
|
||||
@@ -35,9 +35,6 @@ class unload_xml_file extends XMLDBAction {
|
||||
function init() {
|
||||
parent::init();
|
||||
|
||||
// Set own custom attributes
|
||||
$this->sesskey_protected = false; // This action doesn't need sesskey protection
|
||||
|
||||
// Get needed strings
|
||||
$this->loadStrings(array(
|
||||
// 'key' => 'module',
|
||||
|
||||
@@ -33,7 +33,7 @@ $string['confirmdeleteindex'] = 'Are you absolutely sure that you want to delete
|
||||
$string['confirmdeletekey'] = 'Are you absolutely sure that you want to delete the key:';
|
||||
$string['confirmdeletetable'] = 'Are you absolutely sure that you want to delete the table:';
|
||||
$string['confirmdeletexmlfile'] = 'Are you absolutely sure that you want to delete the file:';
|
||||
$string['confirmcheckbigints'] = 'This functionality will search for <a href="http://tracker.moodle.org/browse/MDL-11038">potential wrong integer fields</a> in your Moodle server, generating (but not executing!) automatically the needed SQL statements to have all the integers in your DB properly defined.
|
||||
$string['confirmcheckbigints'] = 'This functionality will search for <a href="https://tracker.moodle.org/browse/MDL-11038">potential wrong integer fields</a> in your Moodle server, generating (but not executing!) automatically the needed SQL statements to have all the integers in your DB properly defined.
|
||||
|
||||
Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).
|
||||
|
||||
@@ -59,7 +59,7 @@ Once generated you can copy such statements and execute them safely with your fa
|
||||
It\'s highly recommended to be running the latest (+ version) available of your Moodle release before executing the search of missing indexes.
|
||||
|
||||
This functionality doesn\'t perform any action against the DB (just reads from it), so can be safely executed at any moment.';
|
||||
$string['confirmcheckoraclesemantics'] = 'This functionality will search for <a href="http://tracker.moodle.org/browse/MDL-29322">Oracle varchar2 columns using BYTE semantics</a> in your Moodle server, generating (but not executing!) automatically the needed SQL statements to have all the columns converted to use CHAR semantics instead (better for cross-db compatibility and increased contents max. length).
|
||||
$string['confirmcheckoraclesemantics'] = 'This functionality will search for <a href="https://tracker.moodle.org/browse/MDL-29322">Oracle varchar2 columns using BYTE semantics</a> in your Moodle server, generating (but not executing!) automatically the needed SQL statements to have all the columns converted to use CHAR semantics instead (better for cross-db compatibility and increased contents max. length).
|
||||
|
||||
Once generated you can copy such statements and execute them safely with your favourite SQL interface (don\'t forget to backup your data before doing that).
|
||||
|
||||
@@ -139,7 +139,7 @@ $string['incorrecttablename'] = 'Incorrect table name';
|
||||
$string['key'] = 'Key';
|
||||
$string['keynameempty'] = 'The key name cannot be empty';
|
||||
$string['keys'] = 'Keys';
|
||||
$string['listreservedwords'] = 'List of Reserved Words<br />(used to keep <a href="http://docs.moodle.org/en/XMLDB_reserved_words" target="_blank">XMLDB_reserved_words</a> updated)';
|
||||
$string['listreservedwords'] = 'List of reserved words<br />(used to keep <a href="https://docs.moodle.org/en/XMLDB_reserved_words" target="_blank">XMLDB reserved words</a> updated)';
|
||||
$string['load'] = 'Load';
|
||||
$string['main_view'] = 'Main view';
|
||||
$string['masterprimaryuniqueordernomatch'] = 'The fields in your foreign key must be listed in the same order as they are listed in the UNIQUE KEY on the referenced table.';
|
||||
|
||||
@@ -577,9 +577,14 @@ abstract class base {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Remove samples the target consider invalid.
|
||||
$this->analysabletarget->add_sample_data($samplesdata);
|
||||
$this->analysabletarget->filter_out_invalid_samples($sampleids, $analysable, $includetarget);
|
||||
try {
|
||||
// Remove samples the target consider invalid.
|
||||
$this->analysabletarget->add_sample_data($samplesdata);
|
||||
$this->analysabletarget->filter_out_invalid_samples($sampleids, $analysable, $includetarget);
|
||||
} catch (\Throwable $e) {
|
||||
$dataset->close_process();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (!$sampleids) {
|
||||
$result->status = \core_analytics\model::NO_DATASET;
|
||||
@@ -588,19 +593,26 @@ abstract class base {
|
||||
return $result;
|
||||
}
|
||||
|
||||
foreach ($this->indicators as $key => $indicator) {
|
||||
// The analyser attaches the main entities the sample depends on and are provided to the
|
||||
// indicator to calculate the sample.
|
||||
$this->indicators[$key]->add_sample_data($samplesdata);
|
||||
}
|
||||
try {
|
||||
|
||||
// Here we start the memory intensive process that will last until $data var is
|
||||
// unset (until the method is finished basically).
|
||||
if ($includetarget) {
|
||||
$data = $timesplitting->calculate($sampleids, $this->get_samples_origin(), $this->indicators, $ranges,
|
||||
$this->analysabletarget);
|
||||
} else {
|
||||
$data = $timesplitting->calculate($sampleids, $this->get_samples_origin(), $this->indicators, $ranges);
|
||||
// Instantiate empty indicators to ensure that no garbage is dragged from previous analyses.
|
||||
foreach ($this->instantiate_indicators() as $key => $indicator) {
|
||||
// The analyser attaches the main entities the sample depends on and are provided to the
|
||||
// indicator to calculate the sample.
|
||||
$this->indicators[$key]->add_sample_data($samplesdata);
|
||||
}
|
||||
|
||||
// Here we start the memory intensive process that will last until $data var is
|
||||
// unset (until the method is finished basically).
|
||||
if ($includetarget) {
|
||||
$data = $timesplitting->calculate($sampleids, $this->get_samples_origin(), $this->indicators, $ranges,
|
||||
$this->analysabletarget);
|
||||
} else {
|
||||
$data = $timesplitting->calculate($sampleids, $this->get_samples_origin(), $this->indicators, $ranges);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$dataset->close_process();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (!$data) {
|
||||
@@ -610,11 +622,16 @@ abstract class base {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Add extra metadata.
|
||||
$this->add_model_metadata($data);
|
||||
try {
|
||||
// Add extra metadata.
|
||||
$this->add_model_metadata($data);
|
||||
|
||||
// Write all calculated data to a file.
|
||||
$file = $dataset->store($data);
|
||||
// Write all calculated data to a file.
|
||||
$file = $dataset->store($data);
|
||||
} catch (\Throwable $e) {
|
||||
$dataset->close_process();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// Flag the model + analysable + timesplitting as analysed.
|
||||
$dataset->close_process();
|
||||
@@ -636,6 +653,18 @@ abstract class base {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate the indicators.
|
||||
*
|
||||
* @return \core_analytics\local\indicator\base[]
|
||||
*/
|
||||
public function instantiate_indicators() {
|
||||
foreach ($this->indicators as $key => $indicator) {
|
||||
$this->indicators[$key] = call_user_func(array($indicator, 'instance'));
|
||||
}
|
||||
return $this->indicators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most recent range that can be used to predict.
|
||||
*
|
||||
|
||||
@@ -836,7 +836,8 @@ abstract class community_of_inquiry_activity extends linear {
|
||||
// When the course is using format weeks we use the week's end date.
|
||||
$format = course_get_format($activity->get_modinfo()->get_course());
|
||||
// We should change this in MDL-60702.
|
||||
if (method_exists($format, 'get_section_dates')) {
|
||||
if ((get_class($format) == 'format_weeks' || is_subclass_of($format, 'format_weeks'))
|
||||
&& method_exists($format, 'get_section_dates')) {
|
||||
$dates = $format->get_section_dates($section);
|
||||
|
||||
// We need to consider the +2 hours added by get_section_dates.
|
||||
|
||||
@@ -115,8 +115,10 @@ abstract class base extends \core_analytics\calculable {
|
||||
global $PAGE;
|
||||
|
||||
$predictionid = $prediction->get_prediction_data()->id;
|
||||
$contextid = $prediction->get_prediction_data()->contextid;
|
||||
$modelid = $prediction->get_prediction_data()->modelid;
|
||||
|
||||
$PAGE->requires->js_call_amd('report_insights/actions', 'init', array($predictionid));
|
||||
$PAGE->requires->js_call_amd('report_insights/actions', 'init', array($predictionid, $contextid, $modelid));
|
||||
|
||||
$actions = array();
|
||||
|
||||
@@ -196,7 +198,7 @@ abstract class base extends \core_analytics\calculable {
|
||||
$message->component = 'moodle';
|
||||
$message->name = 'insights';
|
||||
|
||||
$message->userfrom = get_admin();
|
||||
$message->userfrom = \core_user::get_noreply_user();
|
||||
$message->userto = $user;
|
||||
|
||||
$insighturl = new \moodle_url('/report/insights/insights.php?modelid=' . $modelid . '&contextid=' . $context->id);
|
||||
|
||||
@@ -1524,7 +1524,10 @@ class model {
|
||||
// 1 db read per context.
|
||||
$this->purge_insights_cache();
|
||||
|
||||
$this->model->trained = 0;
|
||||
if (!$this->is_static()) {
|
||||
$this->model->trained = 0;
|
||||
}
|
||||
|
||||
$this->model->timemodified = time();
|
||||
$this->model->usermodified = $USER->id;
|
||||
$DB->update_record('analytics_models', $this->model);
|
||||
|
||||
@@ -168,10 +168,34 @@ class analytics_model_testcase extends advanced_testcase {
|
||||
$this->assertEmpty($DB->count_records('analytics_predict_samples'));
|
||||
$this->assertEmpty($DB->count_records('analytics_used_files'));
|
||||
|
||||
// Check that the model is marked as not trained after clearing (as it is not a static one).
|
||||
$this->assertEquals(0, $DB->get_field('analytics_models', 'trained', array('id' => $this->modelobj->id)));
|
||||
|
||||
set_config('enabled_stores', '', 'tool_log');
|
||||
get_log_manager(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test behaviour of {\core_analytics\model::clear()} for static models.
|
||||
*/
|
||||
public function test_clear_static() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$statictarget = new test_static_target_shortname();
|
||||
$indicators['test_indicator_max'] = \core_analytics\manager::get_indicator('test_indicator_max');
|
||||
$model = \core_analytics\model::create($statictarget, $indicators, '\core\analytics\time_splitting\quarters');
|
||||
$modelobj = $model->get_model_obj();
|
||||
|
||||
// Static models are always considered trained.
|
||||
$this->assertEquals(1, $DB->get_field('analytics_models', 'trained', array('id' => $modelobj->id)));
|
||||
|
||||
$model->clear();
|
||||
|
||||
// Check that the model is still marked as trained even after clearing.
|
||||
$this->assertEquals(1, $DB->get_field('analytics_models', 'trained', array('id' => $modelobj->id)));
|
||||
}
|
||||
|
||||
public function test_model_manager() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
|
||||
@@ -99,6 +99,13 @@ class auth_db_testcase extends advanced_testcase {
|
||||
case 'mssql':
|
||||
set_config('type', 'mssqlnative', 'auth_db');
|
||||
set_config('sybasequoting', '1', 'auth_db');
|
||||
|
||||
// The native sqlsrv driver uses a comma as separator between host and port.
|
||||
$dbhost = $CFG->dbhost;
|
||||
if (!empty($dboptions['dbport'])) {
|
||||
$dbhost .= ',' . $dboptions['dbport'];
|
||||
}
|
||||
set_config('host', $dbhost, 'auth_db');
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
$string['auth_emaildescription'] = '<p>Email-based self-registration enables a user to create their own account via a \'Create new account\' button on the login page. The user then receives an email containing a secure link to a page where they can confirm their account. Future logins just check the username and password against the stored values in the Moodle database.</p><p>Note: In addition to enabling the plugin, email-based self-registration must also be selected from the self registration drop-down menu on the \'Manage authentication\' page.</p>';
|
||||
$string['auth_emailnoemail'] = 'Tried to send you an email but failed!';
|
||||
$string['auth_emailrecaptcha'] = 'Adds a visual/audio confirmation form element to the sign-up page for email self-registering users. This protects your site against spammers and contributes to a worthwhile cause. See http://www.google.com/recaptcha for more details.';
|
||||
$string['auth_emailrecaptcha'] = 'Adds a visual/audio confirmation form element to the sign-up page for email self-registering users. This protects your site against spammers and contributes to a worthwhile cause. See https://www.google.com/recaptcha for more details.';
|
||||
$string['auth_emailrecaptcha_key'] = 'Enable reCAPTCHA element';
|
||||
$string['auth_emailsettings'] = 'Settings';
|
||||
$string['pluginname'] = 'Email-based self-registration';
|
||||
|
||||
@@ -63,3 +63,36 @@ Feature: User must accept policy when logging in and signing up
|
||||
And I log in as "user1"
|
||||
And I open my profile in edit mode
|
||||
And the field "First name" matches value "User1"
|
||||
|
||||
Scenario Outline: Email validation during email registration
|
||||
Given the following config values are set as admin:
|
||||
| allowaccountssameemail | <allowsameemail> |
|
||||
| registerauth | email |
|
||||
| passwordpolicy | 0 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| s1 | John | Doe | s1@example.com |
|
||||
And I am on site homepage
|
||||
And I follow "Log in"
|
||||
When I press "Create new account"
|
||||
And I set the following fields to these values:
|
||||
| Username | s2 |
|
||||
| Password | test |
|
||||
| Email address | <email1> |
|
||||
| Email (again) | <email2> |
|
||||
| First name | Jane |
|
||||
| Surname | Doe |
|
||||
And I press "Create my new account"
|
||||
Then I should <expect> "This email address is already registered. Perhaps you created an account in the past?"
|
||||
And I should <expect2> "Invalid email address"
|
||||
|
||||
Examples:
|
||||
| allowsameemail | email1 | email2 | expect | expect2 |
|
||||
| 0 | s1@example.com | s1@example.com | see | not see |
|
||||
| 0 | S1@EXAMPLE.COM | S1@EXAMPLE.COM | see | not see |
|
||||
| 0 | s1@example.com | S1@EXAMPLE.COM | see | not see |
|
||||
| 0 | s2@example.com | s1@example.com | not see | see |
|
||||
| 1 | s1@example.com | s1@example.com | not see | not see |
|
||||
| 1 | S1@EXAMPLE.COM | S1@EXAMPLE.COM | not see | not see |
|
||||
| 1 | s1@example.com | S1@EXAMPLE.COM | not see | not see |
|
||||
| 1 | s1@example.com | s2@example.com | not see | see |
|
||||
|
||||
@@ -36,11 +36,7 @@ $string['auth_ldap_contexts_key'] = 'Contexts';
|
||||
$string['auth_ldap_create_context'] = 'If you enable user creation with email confirmation, specify the context where users are created. This context should be different from other users to prevent security issues. You don\'t need to add this context to ldap_context-variable, Moodle will search for users from this context automatically.<br /><b>Note!</b> You have to modify the method user_create() in file auth/ldap/auth.php to make user creation work';
|
||||
$string['auth_ldap_create_context_key'] = 'Context for new users';
|
||||
$string['auth_ldap_create_error'] = 'Error creating user in LDAP.';
|
||||
$string['auth_ldapdescription'] = 'This method provides authentication against an external LDAP server.
|
||||
If the given username and password are valid, Moodle creates a new user
|
||||
entry in its database. This module can read user attributes from LDAP and prefill
|
||||
wanted fields in Moodle. For following logins only the username and
|
||||
password are checked.';
|
||||
$string['auth_ldapdescription'] = 'This method provides authentication against an external LDAP server. If the given username and password are valid, Moodle creates a new user entry in its database. This plugin can read user attributes from LDAP and prefill wanted fields in Moodle. For following logins only the username and password are checked.';
|
||||
$string['auth_ldap_expiration_desc'] = 'Select \'{$a->no}\' to disable expired password checking or \'{$a->ldapserver}\' to read the password expiry time directly from the LDAP server.';
|
||||
$string['auth_ldap_expiration_key'] = 'Expiry';
|
||||
$string['auth_ldap_expiration_warning_desc'] = 'Number of days before password expiry warning is issued.';
|
||||
@@ -98,7 +94,7 @@ $string['auth_ldap_usertypeundefined2'] = 'config.user_type not defined or funct
|
||||
$string['auth_ldap_version'] = 'The version of the LDAP protocol your server is using.';
|
||||
$string['auth_ldap_version_key'] = 'Version';
|
||||
$string['auth_ntlmsso'] = 'NTLM SSO';
|
||||
$string['auth_ntlmsso_enabled'] = 'Set to yes to attempt Single Sign On with the NTLM domain. <strong>Note:</strong> this requires additional setup on the webserver to work, see <a href="http://docs.moodle.org/en/NTLM_authentication">http://docs.moodle.org/en/NTLM_authentication</a>';
|
||||
$string['auth_ntlmsso_enabled'] = 'Set to yes to attempt Single Sign On with the NTLM domain. Note that this requires additional setup on the server to work. For further details, see the documentation <a href="https://docs.moodle.org/en/NTLM_authentication">NTLM authentication</a>.';
|
||||
$string['auth_ntlmsso_enabled_key'] = 'Enable';
|
||||
$string['auth_ntlmsso_ie_fastpath'] = 'Set to enable the NTLM SSO fast path (bypasses certain steps if the client\'s browser is MS Internet Explorer).';
|
||||
$string['auth_ntlmsso_ie_fastpath_key'] = 'MS IE fast path?';
|
||||
|
||||
@@ -28,7 +28,7 @@ $string['auth_shib_auth_method_description'] = 'Provide a name for the Shibbolet
|
||||
$string['auth_shib_auth_logo'] = 'Authentication method logo';
|
||||
$string['auth_shib_auth_logo_description'] = 'Provide a logo for the Shibboleth authentication method that is familiar to your users. This could be the logo of your Shibboleth federation, e.g. <tt>SWITCHaai Login</tt> or <tt>InCommon Login</tt> or similar.';
|
||||
$string['auth_shib_contact_administrator'] = 'In case you are not associated with the given organizations and you need access to a course on this server, please contact the <a href="mailto:{$a}">Moodle Administrator</a>.';
|
||||
$string['auth_shibbolethdescription'] = 'Using this method users are created and authenticated using <a href="http://shibboleth.internet2.edu/">Shibboleth</a>.<br />Be sure to read the <a href="../auth/shibboleth/README.txt">README</a> for Shibboleth on how to set up your Moodle with Shibboleth';
|
||||
$string['auth_shibbolethdescription'] = 'Using this method users are created and authenticated using Shibboleth. For set-up details, see the <a href="../auth/shibboleth/README.txt">Shibboleth README</a>.';
|
||||
$string['auth_shibboleth_errormsg'] = 'Please select the organization you are member of!';
|
||||
$string['auth_shibboleth_login'] = 'Shibboleth login';
|
||||
$string['auth_shibboleth_login_long'] = 'Login to Moodle via Shibboleth';
|
||||
|
||||
@@ -311,11 +311,11 @@ class backup_controller extends base_controller {
|
||||
// Basic/initial prevention against time/memory limits
|
||||
core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted
|
||||
raise_memory_limit(MEMORY_EXTRA);
|
||||
// If this is not a course backup, inform the plan we are not
|
||||
// If this is not a course backup, or single activity backup (e.g. duplicate) inform the plan we are not
|
||||
// including all the activities for sure. This will affect any
|
||||
// task/step executed conditionally to stop including information
|
||||
// for section and activity backup. MDL-28180.
|
||||
if ($this->get_type() !== backup::TYPE_1COURSE) {
|
||||
if ($this->get_type() !== backup::TYPE_1COURSE && $this->get_type() !== backup::TYPE_1ACTIVITY) {
|
||||
$this->log('notifying plan about excluded activities by type', backup::LOG_DEBUG);
|
||||
$this->plan->set_excluding_activities();
|
||||
}
|
||||
|
||||
@@ -326,11 +326,11 @@ class restore_controller extends base_controller {
|
||||
// Basic/initial prevention against time/memory limits
|
||||
core_php_time_limit::raise(1 * 60 * 60); // 1 hour for 1 course initially granted
|
||||
raise_memory_limit(MEMORY_EXTRA);
|
||||
// If this is not a course restore, inform the plan we are not
|
||||
// If this is not a course restore or single activity restore (e.g. duplicate), inform the plan we are not
|
||||
// including all the activities for sure. This will affect any
|
||||
// task/step executed conditionally to stop processing information
|
||||
// for section and activity restore. MDL-28180.
|
||||
if ($this->get_type() !== backup::TYPE_1COURSE) {
|
||||
if ($this->get_type() !== backup::TYPE_1COURSE && $this->get_type() !== backup::TYPE_1ACTIVITY) {
|
||||
$this->log('notifying plan about excluded activities by type', backup::LOG_DEBUG);
|
||||
$this->plan->set_excluding_activities();
|
||||
}
|
||||
|
||||
@@ -794,12 +794,25 @@ class backup_badges_structure_step extends backup_structure_step {
|
||||
$badge = new backup_nested_element('badge', array('id'), array('name', 'description',
|
||||
'timecreated', 'timemodified', 'usercreated', 'usermodified', 'issuername',
|
||||
'issuerurl', 'issuercontact', 'expiredate', 'expireperiod', 'type', 'courseid',
|
||||
'message', 'messagesubject', 'attachment', 'notification', 'status', 'nextcron'));
|
||||
'message', 'messagesubject', 'attachment', 'notification', 'status', 'nextcron',
|
||||
'version', 'language', 'imageauthorname', 'imageauthoremail', 'imageauthorurl',
|
||||
'imagecaption'));
|
||||
|
||||
$criteria = new backup_nested_element('criteria');
|
||||
$criterion = new backup_nested_element('criterion', array('id'), array('badgeid',
|
||||
'criteriatype', 'method', 'description', 'descriptionformat'));
|
||||
|
||||
$endorsement = new backup_nested_element('endorsement', array('id'), array('badgeid',
|
||||
'issuername', 'issuerurl', 'issueremail', 'claimid', 'claimcomment', 'dateissued'));
|
||||
|
||||
$alignments = new backup_nested_element('alignments');
|
||||
$alignment = new backup_nested_element('alignment', array('id'), array('badgeid',
|
||||
'targetname', 'targeturl', 'targetdescription', 'targetframework', 'targetcode'));
|
||||
|
||||
$relatedbadges = new backup_nested_element('relatedbadges');
|
||||
$relatedbadge = new backup_nested_element('relatedbadge', array('id'), array('badgeid',
|
||||
'relatedbadgeid'));
|
||||
|
||||
$parameters = new backup_nested_element('parameters');
|
||||
$parameter = new backup_nested_element('parameter', array('id'), array('critid',
|
||||
'name', 'value', 'criteriatype'));
|
||||
@@ -815,6 +828,11 @@ class backup_badges_structure_step extends backup_structure_step {
|
||||
$criteria->add_child($criterion);
|
||||
$criterion->add_child($parameters);
|
||||
$parameters->add_child($parameter);
|
||||
$badge->add_child($endorsement);
|
||||
$badge->add_child($alignments);
|
||||
$alignments->add_child($alignment);
|
||||
$badge->add_child($relatedbadges);
|
||||
$relatedbadges->add_child($relatedbadge);
|
||||
$badge->add_child($manual_awards);
|
||||
$manual_awards->add_child($manual_award);
|
||||
|
||||
@@ -822,6 +840,10 @@ class backup_badges_structure_step extends backup_structure_step {
|
||||
|
||||
$badge->set_source_table('badge', array('courseid' => backup::VAR_COURSEID));
|
||||
$criterion->set_source_table('badge_criteria', array('badgeid' => backup::VAR_PARENTID));
|
||||
$endorsement->set_source_table('badge_endorsement', array('badgeid' => backup::VAR_PARENTID));
|
||||
|
||||
$alignment->set_source_table('badge_competencies', array('badgeid' => backup::VAR_PARENTID));
|
||||
$relatedbadge->set_source_table('badge_related', array('badgeid' => backup::VAR_PARENTID));
|
||||
|
||||
$parametersql = 'SELECT cp.*, c.criteriatype
|
||||
FROM {badge_criteria_param} cp JOIN {badge_criteria} c
|
||||
@@ -838,6 +860,10 @@ class backup_badges_structure_step extends backup_structure_step {
|
||||
$badge->annotate_ids('user', 'usermodified');
|
||||
$criterion->annotate_ids('badge', 'badgeid');
|
||||
$parameter->annotate_ids('criterion', 'critid');
|
||||
$endorsement->annotate_ids('badge', 'badgeid');
|
||||
$alignment->annotate_ids('badge', 'badgeid');
|
||||
$relatedbadge->annotate_ids('badge', 'badgeid');
|
||||
$relatedbadge->annotate_ids('badge', 'relatedbadgeid');
|
||||
$badge->annotate_files('badges', 'badgeimage', 'id');
|
||||
$manual_award->annotate_ids('badge', 'badgeid');
|
||||
$manual_award->annotate_ids('user', 'recipientid');
|
||||
@@ -2270,7 +2296,11 @@ class backup_questions_structure_step extends backup_structure_step {
|
||||
FROM {tag} t
|
||||
JOIN {tag_instance} ti ON ti.tagid = t.id
|
||||
WHERE ti.itemid = ?
|
||||
AND ti.itemtype = 'question'", array(backup::VAR_PARENTID));
|
||||
AND ti.itemtype = 'question'
|
||||
AND ti.component = 'core_question'",
|
||||
[
|
||||
backup::VAR_PARENTID
|
||||
]);
|
||||
|
||||
// don't need to annotate ids nor files
|
||||
// (already done by {@link backup_annotate_all_question_files}
|
||||
|
||||
@@ -1969,6 +1969,12 @@ class restore_course_structure_step extends restore_structure_step {
|
||||
}
|
||||
|
||||
$capability = 'mod/' . $modname . ':addinstance';
|
||||
|
||||
if (!get_capability_info($capability)) {
|
||||
$this->log("Capability '{$capability}' was not found!", backup::LOG_WARNING);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($roleids as $roleid) {
|
||||
assign_capability($capability, CAP_PREVENT, $roleid, $context);
|
||||
}
|
||||
@@ -2096,9 +2102,12 @@ class restore_ras_and_caps_structure_step extends restore_structure_step {
|
||||
$newroleid = $this->get_mappingid('role', $data->roleid);
|
||||
// If newroleid and context are valid assign it via API (it handles dupes and so on)
|
||||
if ($newroleid && $this->task->get_contextid()) {
|
||||
// TODO: assign_capability() needs one userid param to be able to specify our restore userid
|
||||
// TODO: it seems that assign_capability() doesn't check for valid capabilities at all ???
|
||||
assign_capability($data->capability, $data->permission, $newroleid, $this->task->get_contextid());
|
||||
if (!get_capability_info($data->capability)) {
|
||||
$this->log("Capability '{$data->capability}' was not found!", backup::LOG_WARNING);
|
||||
} else {
|
||||
// TODO: assign_capability() needs one userid param to be able to specify our restore userid.
|
||||
assign_capability($data->capability, $data->permission, $newroleid, $this->task->get_contextid());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2506,6 +2515,9 @@ class restore_badges_structure_step extends restore_structure_step {
|
||||
$paths[] = new restore_path_element('badge', '/badges/badge');
|
||||
$paths[] = new restore_path_element('criterion', '/badges/badge/criteria/criterion');
|
||||
$paths[] = new restore_path_element('parameter', '/badges/badge/criteria/criterion/parameters/parameter');
|
||||
$paths[] = new restore_path_element('endorsement', '/badges/badge/endorsement');
|
||||
$paths[] = new restore_path_element('alignment', '/badges/badge/alignments/alignment');
|
||||
$paths[] = new restore_path_element('relatedbadge', '/badges/badge/relatedbadges/relatedbadge');
|
||||
$paths[] = new restore_path_element('manual_award', '/badges/badge/manual_awards/manual_award');
|
||||
|
||||
return $paths;
|
||||
@@ -2550,13 +2562,87 @@ class restore_badges_structure_step extends restore_structure_step {
|
||||
'attachment' => $data->attachment,
|
||||
'notification' => $data->notification,
|
||||
'status' => BADGE_STATUS_INACTIVE,
|
||||
'nextcron' => $data->nextcron
|
||||
'nextcron' => $data->nextcron,
|
||||
'version' => $data->version,
|
||||
'language' => $data->language,
|
||||
'imageauthorname' => $data->imageauthorname,
|
||||
'imageauthoremail' => $data->imageauthoremail,
|
||||
'imageauthorurl' => $data->imageauthorurl,
|
||||
'imagecaption' => $data->imagecaption
|
||||
);
|
||||
|
||||
$newid = $DB->insert_record('badge', $params);
|
||||
$this->set_mapping('badge', $data->id, $newid, $restorefiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an endorsement for a badge.
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return void
|
||||
*/
|
||||
public function process_endorsement($data) {
|
||||
global $DB;
|
||||
|
||||
$data = (object)$data;
|
||||
|
||||
$params = array(
|
||||
'badgeid' => $this->get_new_parentid('badge'),
|
||||
'issuername' => $data->issuername,
|
||||
'issuerurl' => $data->issuerurl,
|
||||
'issueremail' => $data->issueremail,
|
||||
'claimid' => $data->claimid,
|
||||
'claimcomment' => $data->claimcomment,
|
||||
'dateissued' => $this->apply_date_offset($data->dateissued)
|
||||
);
|
||||
$newid = $DB->insert_record('badge_endorsement', $params);
|
||||
$this->set_mapping('endorsement', $data->id, $newid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Link to related badges for a badge. This relies on post processing in after_execute().
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return void
|
||||
*/
|
||||
public function process_relatedbadge($data) {
|
||||
global $DB;
|
||||
|
||||
$data = (object)$data;
|
||||
$relatedbadgeid = $data->relatedbadgeid;
|
||||
|
||||
if ($relatedbadgeid) {
|
||||
// Only backup and restore related badges if they are contained in the backup file.
|
||||
$params = array(
|
||||
'badgeid' => $this->get_new_parentid('badge'),
|
||||
'relatedbadgeid' => $relatedbadgeid
|
||||
);
|
||||
$newid = $DB->insert_record('badge_related', $params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Link to an alignment for a badge.
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return void
|
||||
*/
|
||||
public function process_alignment($data) {
|
||||
global $DB;
|
||||
|
||||
$data = (object)$data;
|
||||
$params = array(
|
||||
'badgeid' => $this->get_new_parentid('badge'),
|
||||
'targetname' => $data->targetname,
|
||||
'targeturl' => $data->targeturl,
|
||||
'targetdescription' => $data->targetdescription,
|
||||
'targetframework' => $data->targetframework,
|
||||
'targetcode' => $data->targetcode
|
||||
);
|
||||
$newid = $DB->insert_record('badge_competencies', $params);
|
||||
$this->set_mapping('alignment', $data->id, $newid);
|
||||
}
|
||||
|
||||
public function process_criterion($data) {
|
||||
global $DB;
|
||||
|
||||
@@ -2569,6 +2655,7 @@ class restore_badges_structure_step extends restore_structure_step {
|
||||
'description' => isset($data->description) ? $data->description : '',
|
||||
'descriptionformat' => isset($data->descriptionformat) ? $data->descriptionformat : 0,
|
||||
);
|
||||
|
||||
$newid = $DB->insert_record('badge_criteria', $params);
|
||||
$this->set_mapping('criterion', $data->id, $newid);
|
||||
}
|
||||
@@ -2634,8 +2721,38 @@ class restore_badges_structure_step extends restore_structure_step {
|
||||
}
|
||||
|
||||
protected function after_execute() {
|
||||
global $DB;
|
||||
// Add related files.
|
||||
$this->add_related_files('badges', 'badgeimage', 'badge');
|
||||
|
||||
$badgeid = $this->get_new_parentid('badge');
|
||||
// Remap any related badges.
|
||||
// We do this in the DB directly because this is backup/restore it is not valid to call into
|
||||
// the component API.
|
||||
$params = array('badgeid' => $badgeid);
|
||||
$query = "SELECT DISTINCT br.id, br.badgeid, br.relatedbadgeid
|
||||
FROM {badge_related} br
|
||||
WHERE (br.badgeid = :badgeid)";
|
||||
$relatedbadges = $DB->get_records_sql($query, $params);
|
||||
$newrelatedids = [];
|
||||
foreach ($relatedbadges as $relatedbadge) {
|
||||
$relatedid = $this->get_mappingid('badge', $relatedbadge->relatedbadgeid);
|
||||
$params['relatedbadgeid'] = $relatedbadge->relatedbadgeid;
|
||||
$DB->delete_records_select('badge_related', '(badgeid = :badgeid AND relatedbadgeid = :relatedbadgeid)', $params);
|
||||
if ($relatedid) {
|
||||
$newrelatedids[] = $relatedid;
|
||||
}
|
||||
}
|
||||
if (!empty($newrelatedids)) {
|
||||
$relatedbadges = [];
|
||||
foreach ($newrelatedids as $relatedid) {
|
||||
$relatedbadge = new stdClass();
|
||||
$relatedbadge->badgeid = $badgeid;
|
||||
$relatedbadge->relatedbadgeid = $relatedid;
|
||||
$relatedbadges[] = $relatedbadge;
|
||||
}
|
||||
$DB->insert_records('badge_related', $relatedbadges);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -580,6 +580,17 @@ abstract class backup_controller_dbops extends backup_dbops {
|
||||
'backup_import_competencies' => 'competencies'
|
||||
);
|
||||
self::apply_admin_config_defaults($controller, $settings, true);
|
||||
if ((!$controller->get_interactive()) &&
|
||||
$controller->get_type() == backup::TYPE_1ACTIVITY) {
|
||||
// This is duplicate - there is no concept of defaults - these settings must be on.
|
||||
$settings = array(
|
||||
'activities',
|
||||
'blocks',
|
||||
'filters',
|
||||
'questionbank'
|
||||
);
|
||||
self::force_enable_settings($controller, $settings);
|
||||
}
|
||||
break;
|
||||
case backup::MODE_AUTOMATED:
|
||||
// Load the automated defaults.
|
||||
@@ -607,6 +618,30 @@ abstract class backup_controller_dbops extends backup_dbops {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn these settings on. No defaults from admin settings.
|
||||
*
|
||||
* @param backup_controller $controller
|
||||
* @param array $settings a map from admin config names to setting names (Config name => Setting name)
|
||||
*/
|
||||
private static function force_enable_settings(backup_controller $controller, array $settings) {
|
||||
$plan = $controller->get_plan();
|
||||
foreach ($settings as $config => $settingname) {
|
||||
$value = true;
|
||||
if ($plan->setting_exists($settingname)) {
|
||||
$setting = $plan->get_setting($settingname);
|
||||
// We do not allow this setting to be locked for a duplicate function.
|
||||
if ($setting->get_status() !== base_setting::NOT_LOCKED) {
|
||||
$setting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
$setting->set_value($value);
|
||||
$setting->set_status(base_setting::LOCKED_BY_CONFIG);
|
||||
} else {
|
||||
$controller->log('Unknown setting: ' . $setting, BACKUP::LOG_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the controller settings default values from the admin config.
|
||||
*
|
||||
|
||||
@@ -183,6 +183,18 @@ abstract class restore_controller_dbops extends restore_dbops {
|
||||
);
|
||||
self::apply_admin_config_defaults($controller, $settings, true);
|
||||
}
|
||||
if ($controller->get_mode() == backup::MODE_IMPORT &&
|
||||
(!$controller->get_interactive()) &&
|
||||
$controller->get_type() == backup::TYPE_1ACTIVITY) {
|
||||
// This is duplicate - there is no concept of defaults - these settings must be on.
|
||||
$settings = array(
|
||||
'activities',
|
||||
'blocks',
|
||||
'filters',
|
||||
'questionbank'
|
||||
);
|
||||
self::force_enable_settings($controller, $settings);
|
||||
};
|
||||
|
||||
// Add some dependencies.
|
||||
$plan = $controller->get_plan();
|
||||
@@ -233,6 +245,30 @@ abstract class restore_controller_dbops extends restore_dbops {
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn these settings on. No defaults from admin settings.
|
||||
*
|
||||
* @param restore_controller $controller
|
||||
* @param array $settings a map from admin config names to setting names (Config name => Setting name)
|
||||
*/
|
||||
private static function force_enable_settings(restore_controller $controller, array $settings) {
|
||||
$plan = $controller->get_plan();
|
||||
foreach ($settings as $config => $settingname) {
|
||||
$value = true;
|
||||
if ($plan->setting_exists($settingname)) {
|
||||
$setting = $plan->get_setting($settingname);
|
||||
// We do not allow this setting to be locked for a duplicate function.
|
||||
if ($setting->get_status() !== base_setting::NOT_LOCKED) {
|
||||
$setting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
$setting->set_value($value);
|
||||
$setting->set_status(base_setting::LOCKED_BY_CONFIG);
|
||||
} else {
|
||||
$controller->log('Unknown setting: ' . $settingname, BACKUP::LOG_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the controller settings default values from the admin config.
|
||||
*
|
||||
|
||||
@@ -238,12 +238,12 @@ class restore_log_rule implements processable {
|
||||
protected function build_regexp($expression, $tokens) {
|
||||
// Replace to temp (and preg_quote() safe) placeholders
|
||||
foreach ($tokens as $token) {
|
||||
$expression = preg_replace('~' . preg_quote($token, '~') . '~', '#@@#@@#', $expression, 1);
|
||||
$expression = preg_replace('~' . preg_quote($token, '~') . '~', '%@@%@@%', $expression, 1);
|
||||
}
|
||||
// quote the expression
|
||||
$expression = preg_quote($expression, '~');
|
||||
// Replace all the placeholders
|
||||
$expression = preg_replace('~#@@#@@#~', '(.*)', $expression);
|
||||
$expression = preg_replace('~%@@%@@%~', '(.*)', $expression);
|
||||
return '~' . $expression . '~';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,4 +49,20 @@ class backup_restore_log_rule_testcase extends basic_testcase {
|
||||
// But the original log has been kept unmodified by the process() call.
|
||||
$this->assertEquals($originallog, $log);
|
||||
}
|
||||
|
||||
public function test_build_regexp() {
|
||||
$original = 'Any (string) with [placeholders] like {this} and {this}. [end].';
|
||||
$expectation = '~Any \(string\) with (.*) like (.*) and (.*)\. (.*)\.~';
|
||||
|
||||
$lr = new restore_log_rule('this', 'doesnt', 'matter', 'here');
|
||||
$class = new ReflectionClass('restore_log_rule');
|
||||
|
||||
$method = $class->getMethod('extract_tokens');
|
||||
$method->setAccessible(true);
|
||||
$tokens = $method->invoke($lr, $original);
|
||||
|
||||
$method = $class->getMethod('build_regexp');
|
||||
$method->setAccessible(true);
|
||||
$this->assertSame($expectation, $method->invoke($lr, $original, $tokens));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@@ -82,7 +81,7 @@ abstract class setting_dependency {
|
||||
* Destroy all circular references. It helps PHP 5.2 a lot!
|
||||
*/
|
||||
public function destroy() {
|
||||
// No need to destroy anything recursively here, direct reset
|
||||
// No need to destroy anything recursively here, direct reset.
|
||||
$this->setting = null;
|
||||
$this->dependentsetting = null;
|
||||
}
|
||||
@@ -94,16 +93,19 @@ abstract class setting_dependency {
|
||||
* @return bool
|
||||
*/
|
||||
final public function process_change($changetype, $oldvalue) {
|
||||
// Check the type of change requested
|
||||
// Check the type of change requested.
|
||||
switch ($changetype) {
|
||||
// Process a status change
|
||||
case base_setting::CHANGED_STATUS: return $this->process_status_change($oldvalue);
|
||||
// Process a visibility change
|
||||
case base_setting::CHANGED_VISIBILITY: return $this->process_visibility_change($oldvalue);
|
||||
// Process a value change
|
||||
case base_setting::CHANGED_VALUE: return $this->process_value_change($oldvalue);
|
||||
// Process a status change.
|
||||
case base_setting::CHANGED_STATUS:
|
||||
return $this->process_status_change($oldvalue);
|
||||
// Process a visibility change.
|
||||
case base_setting::CHANGED_VISIBILITY:
|
||||
return $this->process_visibility_change($oldvalue);
|
||||
// Process a value change.
|
||||
case base_setting::CHANGED_VALUE:
|
||||
return $this->process_value_change($oldvalue);
|
||||
}
|
||||
// Throw an exception if we get this far
|
||||
// Throw an exception if we get this far.
|
||||
throw new backup_ui_exception('unknownchangetype');
|
||||
}
|
||||
/**
|
||||
@@ -112,11 +114,11 @@ abstract class setting_dependency {
|
||||
* @return bool
|
||||
*/
|
||||
protected function process_visibility_change($oldvisibility) {
|
||||
// Store the current dependent settings visibility for comparison
|
||||
// Store the current dependent settings visibility for comparison.
|
||||
$prevalue = $this->dependentsetting->get_visibility();
|
||||
// Set it regardless of whether we need to
|
||||
// Set it regardless of whether we need to.
|
||||
$this->dependentsetting->set_visibility($this->setting->get_visibility());
|
||||
// Return true if it changed
|
||||
// Return true if it changed.
|
||||
return ($prevalue != $this->dependentsetting->get_visibility());
|
||||
}
|
||||
/**
|
||||
@@ -182,15 +184,16 @@ class setting_dependency_disabledif_equals extends setting_dependency {
|
||||
*/
|
||||
public function __construct(base_setting $setting, base_setting $dependentsetting, $value, $defaultvalue = false) {
|
||||
parent::__construct($setting, $dependentsetting, $defaultvalue);
|
||||
$this->value = ($value)?(string)$value:0;
|
||||
$this->value = ($value) ? (string)$value : 0;
|
||||
}
|
||||
/**
|
||||
* Returns true if the dependent setting is locked by this setting_dependency.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_locked() {
|
||||
// If the setting is locked or the dependent setting should be locked then return true
|
||||
if ($this->setting->get_status() !== base_setting::NOT_LOCKED || $this->setting->get_value() == $this->value) {
|
||||
// If the setting is locked or the dependent setting should be locked then return true.
|
||||
if ($this->setting->get_status() !== base_setting::NOT_LOCKED ||
|
||||
$this->evaluate_disabled_condition($this->setting->get_value())) {
|
||||
return true;
|
||||
}
|
||||
// Else the dependent setting is not locked by this setting_dependency.
|
||||
@@ -208,17 +211,25 @@ class setting_dependency_disabledif_equals extends setting_dependency {
|
||||
return false;
|
||||
}
|
||||
$prevalue = $this->dependentsetting->get_value();
|
||||
// If the setting is the desired value enact the dependency
|
||||
if ($this->setting->get_value() == $this->value) {
|
||||
// If the setting is the desired value enact the dependency.
|
||||
$settingvalue = $this->setting->get_value();
|
||||
if ($this->evaluate_disabled_condition($settingvalue)) {
|
||||
// The dependent setting needs to be locked by hierachy and set to the
|
||||
// default value.
|
||||
$this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
|
||||
$this->dependentsetting->set_value($this->defaultvalue);
|
||||
|
||||
// For checkboxes the default value is false, but when the setting is
|
||||
// locked, the value should inherit from the parent setting.
|
||||
if ($this->defaultvalue === false) {
|
||||
$this->dependentsetting->set_value($settingvalue);
|
||||
} else {
|
||||
$this->dependentsetting->set_value($this->defaultvalue);
|
||||
}
|
||||
} else if ($this->dependentsetting->get_status() == base_setting::LOCKED_BY_HIERARCHY) {
|
||||
// We can unlock the dependent setting
|
||||
// We can unlock the dependent setting.
|
||||
$this->dependentsetting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
// Return true if the value has changed for the dependent setting
|
||||
// Return true if the value has changed for the dependent setting.
|
||||
return ($prevalue != $this->dependentsetting->get_value());
|
||||
}
|
||||
/**
|
||||
@@ -227,17 +238,18 @@ class setting_dependency_disabledif_equals extends setting_dependency {
|
||||
* @return bool
|
||||
*/
|
||||
protected function process_status_change($oldstatus) {
|
||||
// Store the dependent status
|
||||
// Store the dependent status.
|
||||
$prevalue = $this->dependentsetting->get_status();
|
||||
// Store the current status
|
||||
// Store the current status.
|
||||
$currentstatus = $this->setting->get_status();
|
||||
if ($currentstatus == base_setting::NOT_LOCKED) {
|
||||
if ($prevalue == base_setting::LOCKED_BY_HIERARCHY && $this->setting->get_value() != $this->value) {
|
||||
// Dependency has changes, is not fine, unlock the dependent setting
|
||||
if ($prevalue == base_setting::LOCKED_BY_HIERARCHY &&
|
||||
!$this->evaluate_disabled_condition($this->setting->get_value())) {
|
||||
// Dependency has changes, is not fine, unlock the dependent setting.
|
||||
$this->dependentsetting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
} else {
|
||||
// Make sure the dependent setting is also locked, in this case by hierarchy
|
||||
// Make sure the dependent setting is also locked, in this case by hierarchy.
|
||||
$this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
|
||||
}
|
||||
// Return true if the dependent setting has changed.
|
||||
@@ -248,17 +260,17 @@ class setting_dependency_disabledif_equals extends setting_dependency {
|
||||
* @return bool True if there were changes
|
||||
*/
|
||||
public function enforce() {
|
||||
// This will be set to true if ANYTHING changes
|
||||
// This will be set to true if ANYTHING changes.
|
||||
$changes = false;
|
||||
// First process any value changes
|
||||
// First process any value changes.
|
||||
if ($this->process_value_change($this->setting->get_value())) {
|
||||
$changes = true;
|
||||
}
|
||||
// Second process any status changes
|
||||
// Second process any status changes.
|
||||
if ($this->process_status_change($this->setting->get_status())) {
|
||||
$changes = true;
|
||||
}
|
||||
// Finally process visibility changes
|
||||
// Finally process visibility changes.
|
||||
if ($this->process_visibility_change($this->setting->get_visibility())) {
|
||||
$changes = true;
|
||||
}
|
||||
@@ -271,152 +283,72 @@ class setting_dependency_disabledif_equals extends setting_dependency {
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting'=>$this->dependentsetting->get_ui_name(),
|
||||
'dependenton'=>$this->setting->get_ui_name(),
|
||||
'condition'=>'eq',
|
||||
'value'=>$this->value
|
||||
'setting' => $this->dependentsetting->get_ui_name(),
|
||||
'dependenton' => $this->setting->get_ui_name(),
|
||||
'condition' => 'eq',
|
||||
'value' => $this->value
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the current value of the setting and return true if the dependent setting should be locked or false.
|
||||
* This function should be abstract, but there will probably be existing sub-classes so we must provide a default
|
||||
* implementation.
|
||||
* @param mixed $value The value of the parent setting.
|
||||
* @return bool
|
||||
*/
|
||||
protected function evaluate_disabled_condition($value) {
|
||||
return $value == $this->value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dependency that disables the secondary setting if the primary setting is
|
||||
* not equal to the provided value
|
||||
*
|
||||
* @copyright 2011 Darko Miletic <dmiletic@moodlerooms.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class setting_dependency_disabledif_not_equals extends setting_dependency_disabledif_equals {
|
||||
|
||||
/**
|
||||
* Evaluate the current value of the setting and return true if the dependent setting should be locked or false.
|
||||
* @param mixed $value The value of the parent setting.
|
||||
* @return bool
|
||||
*/
|
||||
protected function evaluate_disabled_condition($value) {
|
||||
return $value != $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of properties suitable to be used to define a moodleforms
|
||||
* disabled command
|
||||
* @return array
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting' => $this->dependentsetting->get_ui_name(),
|
||||
'dependenton' => $this->setting->get_ui_name(),
|
||||
'condition' => 'notequal',
|
||||
'value' => $this->value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dependency that disables the secondary setting if the primary setting is
|
||||
* not equal to the provided value
|
||||
*
|
||||
* @copyright 2011 Darko Miletic <dmiletic@moodlerooms.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class setting_dependency_disabledif_not_equals extends setting_dependency_disabledif_equals {
|
||||
/**
|
||||
* Enforces the dependency if required.
|
||||
* @return bool True if there were changes
|
||||
*/
|
||||
public function enforce() {
|
||||
// This will be set to true if ANYTHING changes
|
||||
$changes = false;
|
||||
// First process any value changes
|
||||
if (!$this->process_value_change($this->setting->get_value())) {
|
||||
$changes = true;
|
||||
}
|
||||
// Second process any status changes
|
||||
if ($this->process_status_change($this->setting->get_status())) {
|
||||
$changes = true;
|
||||
}
|
||||
// Finally process visibility changes
|
||||
if ($this->process_visibility_change($this->setting->get_visibility())) {
|
||||
$changes = true;
|
||||
}
|
||||
return $changes;
|
||||
}
|
||||
/**
|
||||
* Returns an array of properties suitable to be used to define a moodleforms
|
||||
* disabled command
|
||||
* @return array
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting'=>$this->dependentsetting->get_ui_name(),
|
||||
'dependenton'=>$this->setting->get_ui_name(),
|
||||
'condition'=>'notequal',
|
||||
'value'=>$this->value
|
||||
);
|
||||
}
|
||||
}
|
||||
* Disable if a value is in a list.
|
||||
*/
|
||||
class setting_dependency_disabledif_in_array extends setting_dependency_disabledif_equals {
|
||||
|
||||
//with array
|
||||
class setting_dependency_disabledif_equals2 extends setting_dependency {
|
||||
/**
|
||||
* The value to compare to
|
||||
* @var mixed
|
||||
*/
|
||||
protected $value;
|
||||
/**
|
||||
* Creates the dependency
|
||||
*
|
||||
* @param base_setting $setting
|
||||
* @param base_setting $dependentsetting
|
||||
* @param mixed $value
|
||||
* @param mixed $defaultvalue
|
||||
*/
|
||||
public function __construct(base_setting $setting, base_setting $dependentsetting, array $value, $defaultvalue = false) {
|
||||
parent::__construct($setting, $dependentsetting, $defaultvalue);
|
||||
$this->value = $value;
|
||||
}
|
||||
/**
|
||||
* Returns true if the dependent setting is locked by this setting_dependency.
|
||||
* Evaluate the current value of the setting and return true if the dependent setting should be locked or false.
|
||||
* @param mixed $value The value of the parent setting.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_locked() {
|
||||
// If the setting is locked or the dependent setting should be locked then return true
|
||||
if ($this->setting->get_status() !== base_setting::NOT_LOCKED || in_array($this->setting->get_value(), $this->value)) {
|
||||
return true;
|
||||
}
|
||||
// Else the dependent setting is not locked by this setting_dependency.
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Processes a value change in the primary setting
|
||||
* @param mixed $oldvalue
|
||||
* @return bool
|
||||
*/
|
||||
protected function process_value_change($oldvalue) {
|
||||
$prevalue = $this->dependentsetting->get_value();
|
||||
// If the setting is the desired value enact the dependency
|
||||
if (in_array($this->setting->get_value(), $this->value)) {
|
||||
// The dependent setting needs to be locked by hierachy and set to the
|
||||
// default value.
|
||||
$this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
|
||||
$this->dependentsetting->set_value($this->defaultvalue);
|
||||
} else if ($this->dependentsetting->get_status() == base_setting::LOCKED_BY_HIERARCHY) {
|
||||
// We can unlock the dependent setting
|
||||
$this->dependentsetting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
// Return true if the value has changed for the dependent setting
|
||||
return ($prevalue != $this->dependentsetting->get_value());
|
||||
}
|
||||
/**
|
||||
* Processes a status change in the primary setting
|
||||
* @param mixed $oldstatus
|
||||
* @return bool
|
||||
*/
|
||||
protected function process_status_change($oldstatus) {
|
||||
// Store the dependent status
|
||||
$prevalue = $this->dependentsetting->get_status();
|
||||
// Store the current status
|
||||
$currentstatus = $this->setting->get_status();
|
||||
if ($currentstatus == base_setting::NOT_LOCKED) {
|
||||
if ($prevalue == base_setting::LOCKED_BY_HIERARCHY && !in_array($this->setting->get_value(), $this->value)) {
|
||||
// Dependency has changes, is not fine, unlock the dependent setting
|
||||
$this->dependentsetting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
} else {
|
||||
// Make sure the dependent setting is also locked, in this case by hierarchy
|
||||
$this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
|
||||
}
|
||||
// Return true if the dependent setting has changed.
|
||||
return ($prevalue != $this->dependentsetting->get_status());
|
||||
}
|
||||
/**
|
||||
* Enforces the dependency if required.
|
||||
* @return bool True if there were changes
|
||||
*/
|
||||
public function enforce() {
|
||||
// This will be set to true if ANYTHING changes
|
||||
$changes = false;
|
||||
// First process any value changes
|
||||
if ($this->process_value_change($this->setting->get_value())) {
|
||||
$changes = true;
|
||||
}
|
||||
// Second process any status changes
|
||||
if ($this->process_status_change($this->setting->get_status())) {
|
||||
$changes = true;
|
||||
}
|
||||
// Finally process visibility changes
|
||||
if ($this->process_visibility_change($this->setting->get_visibility())) {
|
||||
$changes = true;
|
||||
}
|
||||
return $changes;
|
||||
protected function evaluate_disabled_condition($value) {
|
||||
return in_array($value, $this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of properties suitable to be used to define a moodleforms
|
||||
* disabled command
|
||||
@@ -424,14 +356,19 @@ class setting_dependency_disabledif_equals2 extends setting_dependency {
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting'=>$this->dependentsetting->get_ui_name(),
|
||||
'dependenton'=>$this->setting->get_ui_name(),
|
||||
'condition'=>'eq',
|
||||
'value'=>$this->value
|
||||
'setting' => $this->dependentsetting->get_ui_name(),
|
||||
'dependenton' => $this->setting->get_ui_name(),
|
||||
'condition' => 'eq',
|
||||
'value' => $this->value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is here for backwards compatibility (terrible name).
|
||||
*/
|
||||
class setting_dependency_disabledif_equals2 extends setting_dependency_disabledif_in_array {
|
||||
}
|
||||
|
||||
/**
|
||||
* A dependency that disables the secondary element if the primary element is
|
||||
@@ -452,9 +389,9 @@ class setting_dependency_disabledif_checked extends setting_dependency_disabledi
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting'=>$this->dependentsetting->get_ui_name(),
|
||||
'dependenton'=>$this->setting->get_ui_name(),
|
||||
'condition'=>'checked'
|
||||
'setting' => $this->dependentsetting->get_ui_name(),
|
||||
'dependenton' => $this->setting->get_ui_name(),
|
||||
'condition' => 'checked'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -478,9 +415,9 @@ class setting_dependency_disabledif_not_checked extends setting_dependency_disab
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting'=>$this->dependentsetting->get_ui_name(),
|
||||
'dependenton'=>$this->setting->get_ui_name(),
|
||||
'condition'=>'notchecked'
|
||||
'setting' => $this->dependentsetting->get_ui_name(),
|
||||
'dependenton' => $this->setting->get_ui_name(),
|
||||
'condition' => 'notchecked'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -497,6 +434,16 @@ class setting_dependency_disabledif_not_empty extends setting_dependency_disable
|
||||
parent::__construct($setting, $dependentsetting, false, $defaultvalue);
|
||||
$this->value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the current value of the setting and return true if the dependent setting should be locked or false.
|
||||
* @param mixed $value The value of the parent setting.
|
||||
* @return bool
|
||||
*/
|
||||
protected function evaluate_disabled_condition($value) {
|
||||
return !empty($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of properties suitable to be used to define a moodleforms
|
||||
* disabled command
|
||||
@@ -504,50 +451,12 @@ class setting_dependency_disabledif_not_empty extends setting_dependency_disable
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting'=>$this->dependentsetting->get_ui_name(),
|
||||
'dependenton'=>$this->setting->get_ui_name(),
|
||||
'condition'=>'notequal',
|
||||
'value'=>''
|
||||
'setting' => $this->dependentsetting->get_ui_name(),
|
||||
'dependenton' => $this->setting->get_ui_name(),
|
||||
'condition' => 'notequal',
|
||||
'value' => ''
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Processes a value change in the primary setting
|
||||
* @param mixed $oldvalue
|
||||
* @return bool
|
||||
*/
|
||||
protected function process_value_change($oldvalue) {
|
||||
$prevalue = $this->dependentsetting->get_value();
|
||||
// If the setting is the desired value enact the dependency
|
||||
$value = $this->setting->get_value();
|
||||
if (!empty($value)) {
|
||||
// The dependent setting needs to be locked by hierachy and set to the
|
||||
// default value.
|
||||
$this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
|
||||
if ($this->defaultvalue === false) {
|
||||
$this->dependentsetting->set_value($value);
|
||||
} else {
|
||||
$this->dependentsetting->set_value($this->defaultvalue);
|
||||
}
|
||||
} else if ($this->dependentsetting->get_status() == base_setting::LOCKED_BY_HIERARCHY) {
|
||||
// We can unlock the dependent setting
|
||||
$this->dependentsetting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
// Return true if the value has changed for the dependent setting
|
||||
return ($prevalue != $this->dependentsetting->get_value());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the dependent setting is locked by this setting_dependency.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_locked() {
|
||||
// If the setting is locked or the dependent setting should be locked then return true
|
||||
if ($this->setting->get_status() !== base_setting::NOT_LOCKED || !empty($value)) {
|
||||
return true;
|
||||
}
|
||||
// Else the dependent setting is not locked by this setting_dependency.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -562,6 +471,16 @@ class setting_dependency_disabledif_empty extends setting_dependency_disabledif_
|
||||
parent::__construct($setting, $dependentsetting, false, $defaultvalue);
|
||||
$this->value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the current value of the setting and return true if the dependent setting should be locked or false.
|
||||
* @param mixed $value The value of the parent setting.
|
||||
* @return bool
|
||||
*/
|
||||
protected function evaluate_disabled_condition($value) {
|
||||
return empty($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of properties suitable to be used to define a moodleforms
|
||||
* disabled command
|
||||
@@ -569,47 +488,10 @@ class setting_dependency_disabledif_empty extends setting_dependency_disabledif_
|
||||
*/
|
||||
public function get_moodleform_properties() {
|
||||
return array(
|
||||
'setting'=>$this->dependentsetting->get_ui_name(),
|
||||
'dependenton'=>$this->setting->get_ui_name(),
|
||||
'condition'=>'notequal',
|
||||
'value'=>''
|
||||
'setting' => $this->dependentsetting->get_ui_name(),
|
||||
'dependenton' => $this->setting->get_ui_name(),
|
||||
'condition' => 'notequal',
|
||||
'value' => ''
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Processes a value change in the primary setting
|
||||
* @param mixed $oldvalue
|
||||
* @return bool
|
||||
*/
|
||||
protected function process_value_change($oldvalue) {
|
||||
$prevalue = $this->dependentsetting->get_value();
|
||||
// If the setting is the desired value enact the dependency
|
||||
$value = $this->setting->get_value();
|
||||
if (empty($value)) {
|
||||
// The dependent setting needs to be locked by hierachy and set to the
|
||||
// default value.
|
||||
$this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
|
||||
if ($this->defaultvalue === false) {
|
||||
$this->dependentsetting->set_value($value);
|
||||
} else {
|
||||
$this->dependentsetting->set_value($this->defaultvalue);
|
||||
}
|
||||
} else if ($this->dependentsetting->get_status() == base_setting::LOCKED_BY_HIERARCHY) {
|
||||
// We can unlock the dependent setting
|
||||
$this->dependentsetting->set_status(base_setting::NOT_LOCKED);
|
||||
}
|
||||
// Return true if the value has changed for the dependent setting
|
||||
return ($prevalue != $this->dependentsetting->get_value());
|
||||
}
|
||||
/**
|
||||
* Returns true if the dependent setting is locked by this setting_dependency.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_locked() {
|
||||
// If the setting is locked or the dependent setting should be locked then return true
|
||||
if ($this->setting->get_status() !== base_setting::NOT_LOCKED || empty($value)) {
|
||||
return true;
|
||||
}
|
||||
// Else the dependent setting is not locked by this setting_dependency.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class backp_settings_testcase extends basic_testcase {
|
||||
/**
|
||||
* test base_setting class
|
||||
*/
|
||||
function test_base_setting() {
|
||||
public function test_base_setting() {
|
||||
// Instantiate base_setting and check everything
|
||||
$bs = new mock_base_setting('test', base_setting::IS_BOOLEAN);
|
||||
$this->assertTrue($bs instanceof base_setting);
|
||||
@@ -290,10 +290,54 @@ class backp_settings_testcase extends basic_testcase {
|
||||
$this->assertEquals($ubs3->get_status(), $ubs1->get_status());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that locked and unlocked states on dependent backup settings at the same level
|
||||
* correctly do not flow from the parent to the child setting when the setting is locked by permissions.
|
||||
*/
|
||||
public function test_dependency_empty_locked_by_permission_child_is_not_unlocked() {
|
||||
// Check dependencies are working ok.
|
||||
$bs1 = new mock_backup_setting('test1', base_setting::IS_INTEGER, 2);
|
||||
$bs1->set_level(1);
|
||||
$bs2 = new mock_backup_setting('test2', base_setting::IS_INTEGER, 2);
|
||||
$bs2->set_level(1); // Same level *must* work.
|
||||
$bs1->add_dependency($bs2, setting_dependency::DISABLED_EMPTY);
|
||||
|
||||
$bs1->set_status(base_setting::LOCKED_BY_PERMISSION);
|
||||
$this->assertEquals(base_setting::LOCKED_BY_HIERARCHY, $bs2->get_status());
|
||||
$this->assertEquals(base_setting::LOCKED_BY_PERMISSION, $bs1->get_status());
|
||||
$bs2->set_status(base_setting::LOCKED_BY_PERMISSION);
|
||||
$this->assertEquals(base_setting::LOCKED_BY_PERMISSION, $bs1->get_status());
|
||||
|
||||
// Unlocking the parent should NOT unlock the child.
|
||||
$bs1->set_status(base_setting::NOT_LOCKED);
|
||||
|
||||
$this->assertEquals(base_setting::LOCKED_BY_PERMISSION, $bs2->get_status());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that locked and unlocked states on dependent backup settings at the same level
|
||||
* correctly do flow from the parent to the child setting when the setting is locked by config.
|
||||
*/
|
||||
public function test_dependency_not_empty_locked_by_config_parent_is_unlocked() {
|
||||
$bs1 = new mock_backup_setting('test1', base_setting::IS_INTEGER, 0);
|
||||
$bs1->set_level(1);
|
||||
$bs2 = new mock_backup_setting('test2', base_setting::IS_INTEGER, 0);
|
||||
$bs2->set_level(1); // Same level *must* work.
|
||||
$bs1->add_dependency($bs2, setting_dependency::DISABLED_NOT_EMPTY);
|
||||
|
||||
$bs1->set_status(base_setting::LOCKED_BY_CONFIG);
|
||||
$this->assertEquals(base_setting::LOCKED_BY_HIERARCHY, $bs2->get_status());
|
||||
$this->assertEquals(base_setting::LOCKED_BY_CONFIG, $bs1->get_status());
|
||||
|
||||
// Unlocking the parent should unlock the child.
|
||||
$bs1->set_status(base_setting::NOT_LOCKED);
|
||||
$this->assertEquals(base_setting::NOT_LOCKED, $bs2->get_status());
|
||||
}
|
||||
|
||||
/**
|
||||
* test backup_setting class
|
||||
*/
|
||||
function test_backup_setting() {
|
||||
public function test_backup_setting() {
|
||||
// Instantiate backup_setting class and set level
|
||||
$bs = new mock_backup_setting('test', base_setting::IS_INTEGER, null);
|
||||
$bs->set_level(1);
|
||||
@@ -340,7 +384,7 @@ class backp_settings_testcase extends basic_testcase {
|
||||
/**
|
||||
* test activity_backup_setting class
|
||||
*/
|
||||
function test_activity_backup_setting() {
|
||||
public function test_activity_backup_setting() {
|
||||
$bs = new mock_activity_backup_setting('test', base_setting::IS_INTEGER, null);
|
||||
$this->assertEquals($bs->get_level(), backup_setting::ACTIVITY_LEVEL);
|
||||
|
||||
@@ -355,7 +399,7 @@ class backp_settings_testcase extends basic_testcase {
|
||||
/**
|
||||
* test section_backup_setting class
|
||||
*/
|
||||
function test_section_backup_setting() {
|
||||
public function test_section_backup_setting() {
|
||||
$bs = new mock_section_backup_setting('test', base_setting::IS_INTEGER, null);
|
||||
$this->assertEquals($bs->get_level(), backup_setting::SECTION_LEVEL);
|
||||
|
||||
@@ -370,7 +414,7 @@ class backp_settings_testcase extends basic_testcase {
|
||||
/**
|
||||
* test course_backup_setting class
|
||||
*/
|
||||
function test_course_backup_setting() {
|
||||
public function test_course_backup_setting() {
|
||||
$bs = new mock_course_backup_setting('test', base_setting::IS_INTEGER, null);
|
||||
$this->assertEquals($bs->get_level(), backup_setting::COURSE_LEVEL);
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ Feature: Duplicate activities
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
And I log in as "admin"
|
||||
And I set the following administration settings values:
|
||||
| backup_import_activities | 0 |
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
And I add a "Database" to section "1" and I fill the form with:
|
||||
|
||||
@@ -95,7 +95,7 @@ if ($badge->status != BADGE_STATUS_INACTIVE) {
|
||||
$json['endorsement'] = $endorsementurl->out(false);
|
||||
}
|
||||
|
||||
$competencies = $badge->get_alignment();
|
||||
$competencies = $badge->get_alignments();
|
||||
if (!empty($competencies)) {
|
||||
foreach ($competencies as $item) {
|
||||
$alignment = array('targetName' => $item->targetname, 'targetUrl' => $item->targeturl);
|
||||
@@ -123,4 +123,4 @@ if ($badge->status != BADGE_STATUS_INACTIVE) {
|
||||
$json['error'] = get_string('error:relatedbadgedoesntexist', 'badges');
|
||||
}
|
||||
echo $OUTPUT->header();
|
||||
echo json_encode($json);
|
||||
echo json_encode($json);
|
||||
|
||||
@@ -150,7 +150,7 @@ class core_badges_external extends external_api {
|
||||
// Create a badge instance to be able to get the endorsement and other info.
|
||||
$badgeinstance = new badge($badge->id);
|
||||
$endorsement = $badgeinstance->get_endorsement();
|
||||
$competencies = $badgeinstance->get_alignment();
|
||||
$competencies = $badgeinstance->get_alignments();
|
||||
$relatedbadges = $badgeinstance->get_related_badges();
|
||||
|
||||
if (!$canconfiguredetails) {
|
||||
|
||||
@@ -376,18 +376,40 @@ class provider implements
|
||||
|
||||
// Export the badges.
|
||||
$uniqueid = $DB->sql_concat_join("'-'", ['b.id', 'COALESCE(bc.id, 0)', 'COALESCE(bi.id, 0)',
|
||||
'COALESCE(bma.id, 0)', 'COALESCE(bcm.id, 0)']);
|
||||
'COALESCE(bma.id, 0)', 'COALESCE(bcm.id, 0)', 'COALESCE(brb.id, 0)', 'COALESCE(ba.id, 0)']);
|
||||
$sql = "
|
||||
SELECT $uniqueid AS uniqueid, b.id,
|
||||
bi.id AS biid, bi.dateissued, bi.dateexpire, bi.uniquehash,
|
||||
bma.id AS bmaid, bma.datemet, bma.issuerid,
|
||||
bcm.id AS bcmid,
|
||||
c.fullname AS coursename,
|
||||
be.id AS beid,
|
||||
be.issuername AS beissuername,
|
||||
be.issuerurl AS beissuerurl,
|
||||
be.issueremail AS beissueremail,
|
||||
be.claimid AS beclaimid,
|
||||
be.claimcomment AS beclaimcomment,
|
||||
be.dateissued AS bedateissued,
|
||||
brb.id as rbid,
|
||||
brb.badgeid as rbbadgeid,
|
||||
brb.relatedbadgeid as rbrelatedbadgeid,
|
||||
ba.id as baid,
|
||||
ba.targetname as batargetname,
|
||||
ba.targeturl as batargeturl,
|
||||
ba.targetdescription as batargetdescription,
|
||||
ba.targetframework as batargetframework,
|
||||
ba.targetcode as batargetcode,
|
||||
$ctxfields
|
||||
FROM {badge} b
|
||||
LEFT JOIN {badge_issued} bi
|
||||
ON bi.badgeid = b.id
|
||||
AND bi.userid = :userid1
|
||||
LEFT JOIN {badge_related} brb
|
||||
ON ( b.id = brb.badgeid OR b.id = brb.relatedbadgeid )
|
||||
LEFT JOIN {badge_competencies} ba
|
||||
ON ( b.id = ba.badgeid )
|
||||
LEFT JOIN {badge_endorsement} be
|
||||
ON be.badgeid = b.id
|
||||
LEFT JOIN {badge_manual_award} bma
|
||||
ON bma.badgeid = b.id
|
||||
AND bma.recipientid = :userid2
|
||||
@@ -421,15 +443,32 @@ class provider implements
|
||||
if ($carry === null) {
|
||||
$carry = [
|
||||
'name' => $badge->name,
|
||||
'version' => $badge->version,
|
||||
'language' => $badge->language,
|
||||
'imageauthorname' => $badge->imageauthorname,
|
||||
'imageauthoremail' => $badge->imageauthoremail,
|
||||
'imageauthorurl' => $badge->imageauthorurl,
|
||||
'imagecaption' => $badge->imagecaption,
|
||||
'issued' => null,
|
||||
'manual_award' => null,
|
||||
'criteria_met' => []
|
||||
'criteria_met' => [],
|
||||
'endorsement' => null,
|
||||
];
|
||||
|
||||
if ($badge->type == BADGE_TYPE_COURSE) {
|
||||
context_helper::preload_from_record($record);
|
||||
$carry['course'] = format_string($record->coursename, true, ['context' => $badge->get_context()]);
|
||||
}
|
||||
if (!empty($record->beid)) {
|
||||
$carry['endorsement'] = [
|
||||
'issuername' => $record->beissuername,
|
||||
'issuerurl' => $record->beissuerurl,
|
||||
'issueremail' => $record->beissueremail,
|
||||
'claimid' => $record->beclaimid,
|
||||
'claimcomment' => $record->beclaimcomment,
|
||||
'dateissued' => $record->bedateissued ? transform::datetime($record->bedateissued) : null
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($record->biid)) {
|
||||
$carry['issued'] = [
|
||||
@@ -446,6 +485,52 @@ class provider implements
|
||||
];
|
||||
}
|
||||
}
|
||||
if (!empty($record->rbid)) {
|
||||
if (empty($carry['related_badge'])) {
|
||||
$carry['related_badge'] = [];
|
||||
}
|
||||
$rbid = $record->rbbadgeid;
|
||||
if ($rbid == $record->id) {
|
||||
$rbid = $record->rbrelatedbadgeid;
|
||||
}
|
||||
$exists = false;
|
||||
foreach ($carry['related_badge'] as $related) {
|
||||
if ($related['badgeid'] == $rbid) {
|
||||
$exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$exists) {
|
||||
$relatedbadge = new badge($rbid);
|
||||
$carry['related_badge'][] = [
|
||||
'badgeid' => $rbid,
|
||||
'badgename' => $relatedbadge->name
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($record->baid)) {
|
||||
if (empty($carry['alignment'])) {
|
||||
$carry['alignment'] = [];
|
||||
}
|
||||
$exists = false;
|
||||
$newalignment = [
|
||||
'targetname' => $record->batargetname,
|
||||
'targeturl' => $record->batargeturl,
|
||||
'targetdescription' => $record->batargetdescription,
|
||||
'targetframework' => $record->batargetframework,
|
||||
'targetcode' => $record->batargetcode,
|
||||
];
|
||||
foreach ($carry['alignment'] as $alignment) {
|
||||
if ($alignment == $newalignment) {
|
||||
$exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$exists) {
|
||||
$carry['alignment'][] = $newalignment;
|
||||
}
|
||||
}
|
||||
|
||||
// Export the details of the criteria met.
|
||||
// We only do that once, when we find that a least one criteria was met.
|
||||
|
||||
@@ -97,7 +97,7 @@ if ($alignmentid || $action == 'add' || $action == 'edit') {
|
||||
$urlbutton = new moodle_url('/badges/competency.php', array('id' => $badge->id, 'action' => 'add'));
|
||||
echo $OUTPUT->box($OUTPUT->single_button($urlbutton, get_string('addalignment', 'badges')), 'clearfix mdl-align');
|
||||
}
|
||||
$alignment = $badge->get_alignment();
|
||||
$alignment = $badge->get_alignments();
|
||||
if (count($alignment) > 0) {
|
||||
$renderrelated = new badge_competencies_alignment($alignment, $badgeid);
|
||||
echo $output->render($renderrelated);
|
||||
|
||||
+6
-5
@@ -50,14 +50,15 @@ function badge_review_cron() {
|
||||
if (empty($CFG->badges_allowcoursebadges)) {
|
||||
$coursesql = '';
|
||||
} else {
|
||||
$coursesql = ' OR EXISTS (SELECT id FROM {course} WHERE visible = :visible AND startdate < :current) ';
|
||||
$coursesql = ' OR EXISTS (SELECT c.id FROM {course} c WHERE c.visible = :visible AND c.startdate < :current'
|
||||
. ' AND c.id = b.courseid)';
|
||||
$courseparams = array('visible' => true, 'current' => time());
|
||||
}
|
||||
|
||||
$sql = 'SELECT id
|
||||
FROM {badge}
|
||||
WHERE (status = :active OR status = :activelocked)
|
||||
AND (type = :site ' . $coursesql . ')';
|
||||
$sql = 'SELECT b.id
|
||||
FROM {badge} b
|
||||
WHERE (b.status = :active OR b.status = :activelocked)
|
||||
AND (b.type = :site ' . $coursesql . ')';
|
||||
$badgeparams = array(
|
||||
'active' => BADGE_STATUS_ACTIVE,
|
||||
'activelocked' => BADGE_STATUS_ACTIVE_LOCKED,
|
||||
|
||||
@@ -96,6 +96,7 @@ if (!has_any_capability(array(
|
||||
'moodle/badges:viewawarded',
|
||||
'moodle/badges:createbadge',
|
||||
'moodle/badges:awardbadge',
|
||||
'moodle/badges:configurecriteria',
|
||||
'moodle/badges:configuremessages',
|
||||
'moodle/badges:configuredetails',
|
||||
'moodle/badges:deletebadge'), $PAGE->context)) {
|
||||
|
||||
+2
-2
@@ -428,7 +428,7 @@ class core_badges_renderer extends plugin_renderer_base {
|
||||
}
|
||||
$output .= html_writer::alist($items, array(), 'ul');
|
||||
}
|
||||
$competencies = $badge->get_alignment();
|
||||
$competencies = $badge->get_alignments();
|
||||
if (!empty($competencies)) {
|
||||
$output .= $this->heading(get_string('alignment', 'badges'), 3);
|
||||
$items = array();
|
||||
@@ -1106,7 +1106,7 @@ class core_badges_renderer extends plugin_renderer_base {
|
||||
protected function print_badge_competencies(badge $badge) {
|
||||
$output = '';
|
||||
$output .= $this->heading(get_string('alignment', 'badges'), 3);
|
||||
$competencies = $badge->get_alignment();
|
||||
$competencies = $badge->get_alignments();
|
||||
if (!empty($competencies)) {
|
||||
$items = array();
|
||||
foreach ($competencies as $competency) {
|
||||
|
||||
@@ -813,7 +813,7 @@ class core_badges_badgeslib_testcase extends advanced_testcase {
|
||||
$competency2 = clone $competency1;
|
||||
$newid1 = $badge->save_alignment($competency1);
|
||||
$newid2 = $badge->save_alignment($competency2);
|
||||
$competencies1 = $badge->get_alignment();
|
||||
$competencies1 = $badge->get_alignments();
|
||||
$this->assertCount(2, $competencies1);
|
||||
|
||||
$this->assertEquals($competency1->badgeid, $competencies1[$newid1]->badgeid);
|
||||
@@ -826,12 +826,12 @@ class core_badges_badgeslib_testcase extends advanced_testcase {
|
||||
// Update competency aligment.
|
||||
$competencies1[$newid1]->targetname = 'CCSS.ELA-Literacy.RST.11-12.3 update';
|
||||
$badge->save_alignment($competencies1[$newid1], $competencies1[$newid1]->id);
|
||||
$competencies2 = $badge->get_alignment();
|
||||
$competencies2 = $badge->get_alignments();
|
||||
$this->assertEquals($competencies1[$newid1]->id, $competencies2[$newid1]->id);
|
||||
$this->assertEquals($competencies1[$newid1]->targetname, $competencies2[$newid1]->targetname);
|
||||
|
||||
// Delete competency alignment.
|
||||
$badge->delete_alignment($competencies1[$newid2]->id);
|
||||
$this->assertCount(1, $badge->get_alignment());
|
||||
$this->assertCount(1, $badge->get_alignments());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ class core_badges_external_testcase extends externallib_advanced_testcase {
|
||||
// Get the endorsement, competencies and related badges.
|
||||
$badgeinstance = new badge($badge->id);
|
||||
$endorsement = $badgeinstance->get_endorsement();
|
||||
$competencies = $badgeinstance->get_alignment();
|
||||
$competencies = $badgeinstance->get_alignments();
|
||||
$relatedbadges = $badgeinstance->get_related_badges();
|
||||
$badge->competencies = array();
|
||||
$badge->relatedbadges = array();
|
||||
|
||||
@@ -306,7 +306,11 @@ class core_badges_privacy_testcase extends provider_testcase {
|
||||
$u2ctx = context_user::instance($u2->id);
|
||||
|
||||
$b1 = $this->create_badge(['usercreated' => $u3->id]);
|
||||
$this->endorse_badge(['badgeid' => $b1->id]);
|
||||
$this->align_badge(['badgeid' => $b1->id], ' (1)');
|
||||
$this->align_badge(['badgeid' => $b1->id], ' (2)');
|
||||
$b2 = $this->create_badge(['type' => BADGE_TYPE_COURSE, 'courseid' => $c1->id, 'usermodified' => $u3->id]);
|
||||
$this->relate_badge($b1->id, $b2->id);
|
||||
$b3 = $this->create_badge();
|
||||
$b3crit = $this->create_criteria_manual($b3->id);
|
||||
$b4 = $this->create_badge();
|
||||
@@ -333,6 +337,12 @@ class core_badges_privacy_testcase extends provider_testcase {
|
||||
$path = [get_string('badges', 'core_badges'), "{$b1->name} ({$b1->id})"];
|
||||
$data = writer::with_context($u1ctx)->get_data($path);
|
||||
$this->assertEquals($b1->name, $data->name);
|
||||
$this->assertEquals($b1->version, $data->version);
|
||||
$this->assertEquals($b1->language, $data->language);
|
||||
$this->assertEquals($b1->imageauthorname, $data->imageauthorname);
|
||||
$this->assertEquals($b1->imageauthoremail, $data->imageauthoremail);
|
||||
$this->assertEquals($b1->imageauthorurl, $data->imageauthorurl);
|
||||
$this->assertEquals($b1->imagecaption, $data->imagecaption);
|
||||
$this->assertNotEmpty($data->issued);
|
||||
$this->assertEmpty($data->manual_award);
|
||||
$this->assertEmpty($data->criteria_met);
|
||||
@@ -340,6 +350,33 @@ class core_badges_privacy_testcase extends provider_testcase {
|
||||
$this->assertEquals('yoohoo', $data->issued['unique_hash']);
|
||||
$this->assertNull($data->issued['expires_on']);
|
||||
|
||||
$this->assertNotEmpty($data->endorsement);
|
||||
$this->assertNotEmpty($data->endorsement['issuername']);
|
||||
$this->assertNotEmpty($data->endorsement['issuerurl']);
|
||||
$this->assertNotEmpty($data->endorsement['issueremail']);
|
||||
$this->assertNotEmpty($data->endorsement['claimid']);
|
||||
$this->assertNotEmpty($data->endorsement['claimcomment']);
|
||||
$this->assertNotEmpty($data->endorsement['dateissued']);
|
||||
|
||||
$this->assertNotEmpty($data->related_badge);
|
||||
$this->assertNotEmpty($data->related_badge[0]);
|
||||
$this->assertEquals($data->related_badge[0]['badgeid'], $b2->id);
|
||||
$this->assertEquals($data->related_badge[0]['badgename'], $b2->name);
|
||||
|
||||
$this->assertNotEmpty($data->alignment);
|
||||
$this->assertNotEmpty($data->alignment[0]);
|
||||
$this->assertNotEmpty($data->alignment[0]['targetname']);
|
||||
$this->assertNotEmpty($data->alignment[0]['targeturl']);
|
||||
$this->assertNotEmpty($data->alignment[0]['targetdescription']);
|
||||
$this->assertNotEmpty($data->alignment[0]['targetframework']);
|
||||
$this->assertNotEmpty($data->alignment[0]['targetcode']);
|
||||
$this->assertNotEmpty($data->alignment[1]);
|
||||
$this->assertNotEmpty($data->alignment[1]['targetname']);
|
||||
$this->assertNotEmpty($data->alignment[1]['targeturl']);
|
||||
$this->assertNotEmpty($data->alignment[1]['targetdescription']);
|
||||
$this->assertNotEmpty($data->alignment[1]['targetframework']);
|
||||
$this->assertNotEmpty($data->alignment[1]['targetcode']);
|
||||
|
||||
$path = [get_string('badges', 'core_badges'), "{$b2->name} ({$b2->id})"];
|
||||
$data = writer::with_context($u1ctx)->get_data($path);
|
||||
$this->assertEquals($b2->name, $data->name);
|
||||
@@ -598,12 +635,79 @@ class core_badges_privacy_testcase extends provider_testcase {
|
||||
'attachment' => 1,
|
||||
'notification' => 0,
|
||||
'status' => BADGE_STATUS_ACTIVE,
|
||||
'version' => OPEN_BADGES_V2,
|
||||
'language' => 'en',
|
||||
'imageauthorname' => 'Image author',
|
||||
'imageauthoremail' => 'author@example.com',
|
||||
'imageauthorurl' => 'http://image.example.com/',
|
||||
'imagecaption' => 'Image caption'
|
||||
], $params);
|
||||
$record->id = $DB->insert_record('badge', $record);
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Relate a badge.
|
||||
*
|
||||
* @param int $badgeid The badge ID.
|
||||
* @param int $relatedbadgeid The related badge ID.
|
||||
* @return object
|
||||
*/
|
||||
protected function relate_badge(int $badgeid, int $relatedbadgeid) {
|
||||
global $DB;
|
||||
$record = (object) [
|
||||
'badgeid' => $badgeid,
|
||||
'relatedbadgeid' => $relatedbadgeid
|
||||
];
|
||||
$record->id = $DB->insert_record('badge_related', $record);
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Align a badge.
|
||||
*
|
||||
* @param array $params Parameters.
|
||||
* @param string $suffix Option to append to strings.
|
||||
* @return object
|
||||
*/
|
||||
protected function align_badge(array $params = [], $suffix = '') {
|
||||
global $DB;
|
||||
$record = (object) array_merge([
|
||||
'badgeid' => null,
|
||||
'targetname' => "Alignment name" . $suffix,
|
||||
'targeturl' => "http://issuer-url.domain.co.nz",
|
||||
'targetdescription' => "Description" . $suffix,
|
||||
'targetframework' => "Framework" . $suffix,
|
||||
'targetcode' => "Code" . $suffix
|
||||
], $params);
|
||||
$record->id = $DB->insert_record('badge_competencies', $record);
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Endorse a badge.
|
||||
*
|
||||
* @param array $params Parameters.
|
||||
* @return object
|
||||
*/
|
||||
protected function endorse_badge(array $params = []) {
|
||||
global $DB, $USER;
|
||||
$record = (object) array_merge([
|
||||
'issuername' => "External issuer name",
|
||||
'issuerurl' => "http://issuer-url.domain.co.nz",
|
||||
'issueremail' => "issuer@example.com",
|
||||
'claimid' => "Claim ID",
|
||||
'claimcomment' => "Claim comment",
|
||||
'dateissued' => time()
|
||||
], $params);
|
||||
$record->id = $DB->insert_record('badge_endorsement', $record);
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a backpack.
|
||||
*
|
||||
|
||||
@@ -232,7 +232,7 @@ class block_base {
|
||||
$bc->footer = $this->content->footer;
|
||||
}
|
||||
} else {
|
||||
$bc->add_class('invisible');
|
||||
$bc->add_class('invisibleblock');
|
||||
}
|
||||
|
||||
if (!$this->hide_header()) {
|
||||
@@ -429,13 +429,13 @@ class block_base {
|
||||
function html_attributes() {
|
||||
$attributes = array(
|
||||
'id' => 'inst' . $this->instance->id,
|
||||
'class' => 'block_' . $this->name(). ' block',
|
||||
'class' => 'block_' . $this->name() . ' block',
|
||||
'role' => $this->get_aria_role()
|
||||
);
|
||||
if ($this->hide_header()) {
|
||||
$attributes['class'] .= ' no-header';
|
||||
}
|
||||
if ($this->instance_can_be_docked() && get_user_preferences('docked_block_instance_'.$this->instance->id, 0)) {
|
||||
if ($this->instance_can_be_docked() && get_user_preferences('docked_block_instance_' . $this->instance->id, 0)) {
|
||||
$attributes['class'] .= ' dock_on_load';
|
||||
}
|
||||
return $attributes;
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -499,7 +499,7 @@ function(
|
||||
}
|
||||
|
||||
// Set the last page to either the current or next page
|
||||
if (loadedPages[currentPage].courses.length < pageData.limit) {
|
||||
if (loadedPages[currentPage].courses.length < pageData.limit || !remainingCourses.length) {
|
||||
lastPage = currentPage;
|
||||
actions.allItemsLoaded(currentPage);
|
||||
} else if (loadedPages[currentPage + 1] != undefined
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<span class="sr-only">
|
||||
{{#str}}aria:coursename, core_course{{/str}}
|
||||
</span>
|
||||
<h6 class="d-inline h5">{{{fullname}}}</h4>
|
||||
<h6 class="d-inline h5">{{{fullname}}}</h6>
|
||||
</a>
|
||||
{{> block_myoverview/course-action-menu }}
|
||||
</div>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
$string['everything'] = 'Everything';
|
||||
$string['courses'] = 'Categories and courses';
|
||||
$string['coursestructures'] = 'Categories, courses, and course structures';
|
||||
$string['courseactivities'] = 'Categories, courses, and course Activities';
|
||||
$string['courseactivities'] = 'Categories, courses, and course activities';
|
||||
$string['enabledockdesc'] = 'Allow the user to dock this block';
|
||||
$string['expansionlimit'] = 'Generate navigation for the following';
|
||||
$string['linkcategoriesdesc'] = 'Display categories as links';
|
||||
|
||||
@@ -53,11 +53,13 @@ class helper {
|
||||
return $recentitems;
|
||||
}
|
||||
|
||||
// Determine sort sql clause.
|
||||
$sort = 'timeaccess DESC';
|
||||
|
||||
$paramsql = array('userid' => $userid);
|
||||
$records = $DB->get_records('block_recentlyaccesseditems', $paramsql, $sort);
|
||||
$sql = "SELECT rai.*
|
||||
FROM {block_recentlyaccesseditems} rai
|
||||
JOIN {course} c ON c.id = rai.courseid
|
||||
WHERE userid = :userid
|
||||
ORDER BY rai.timeaccess DESC";
|
||||
$records = $DB->get_records_sql($sql, $paramsql);
|
||||
$order = 0;
|
||||
|
||||
// Get array of items by course. Use $order index to keep sql sorted results.
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This file keeps track of upgrades to the recentlyaccesseditems block
|
||||
*
|
||||
* Sometimes, changes between versions involve alterations to database structures
|
||||
* and other major things that may break installations.
|
||||
*
|
||||
* The upgrade function in this file will attempt to perform all the necessary
|
||||
* actions to upgrade your older installation to the current version.
|
||||
*
|
||||
* If there's something it cannot do itself, it will tell you what you need to do.
|
||||
*
|
||||
* The commands in here will all be database-neutral, using the methods of
|
||||
* database_manager class
|
||||
*
|
||||
* Please do not forget to use upgrade_set_timeout()
|
||||
* before any action that may take longer time to finish.
|
||||
*
|
||||
* @package block_recentlyaccesseditems
|
||||
* @copyright 2019 Peter Dias
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Upgrade the recentlyaccesseditems db table.
|
||||
*
|
||||
* @param $oldversion
|
||||
* @return bool
|
||||
*/
|
||||
function xmldb_block_recentlyaccesseditems_upgrade($oldversion, $block) {
|
||||
global $DB;
|
||||
|
||||
// Automatically generated Moodle v3.6.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
if ($oldversion < 2018120302) {
|
||||
$sql = "courseid NOT IN (SELECT c.id from {course} c) OR cmid NOT IN (SELECT cm.id from {course_modules} cm)";
|
||||
$DB->delete_records_select("block_recentlyaccesseditems", $sql);
|
||||
upgrade_block_savepoint(true, 2018120302, 'recentlyaccesseditems', false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
// This file is part of Moodle - https://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The interface library between the core and the subsystem.
|
||||
*
|
||||
* @package block_recentlyaccesseditems
|
||||
* @copyright 2019 Peter Dias <peter@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Pre-delete course module hook to cleanup any records with references to the deleted module.
|
||||
*
|
||||
* @param stdClass $cm The deleted course module
|
||||
*/
|
||||
function block_recentlyaccesseditems_pre_course_module_delete($cm) {
|
||||
global $DB;
|
||||
|
||||
$DB->delete_records('block_recentlyaccesseditems', ['cmid' => $cm->id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-delete course hook to cleanup any records with references to the deleted course.
|
||||
*
|
||||
* @param stdClass $course The deleted course
|
||||
*/
|
||||
function block_recentlyaccesseditems_pre_course_delete($course) {
|
||||
global $DB;
|
||||
|
||||
$DB->delete_records('block_recentlyaccesseditems', ['courseid' => $course->id]);
|
||||
}
|
||||
@@ -103,5 +103,15 @@ class block_recentlyaccesseditems_externallib_testcase extends externallib_advan
|
||||
}
|
||||
$this->assertTrue($record->timeaccess < $result[$key - 1]->timeaccess);
|
||||
}
|
||||
|
||||
// Delete a course and confirm it's activities don't get returned.
|
||||
delete_course($courses[0], false);
|
||||
$result = \block_recentlyaccesseditems\external::get_recent_items();
|
||||
$this->assertCount((count($forum) + count($chat)) - 2, $result);
|
||||
|
||||
// Delete a single course module should still return.
|
||||
course_delete_module($forum[1]->cmid);
|
||||
$result = \block_recentlyaccesseditems\external::get_recent_items();
|
||||
$this->assertCount((count($forum) + count($chat)) - 3, $result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Block recentlyaccesseditems helper tests.
|
||||
*
|
||||
* @package block_recentlyaccesseditems
|
||||
* @copyright 2019 University of Nottingham
|
||||
* @author Neill Magill <neill.magill@nottingham.ac.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
use block_recentlyaccesseditems\helper;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Block Recently accessed helper class tests.
|
||||
*
|
||||
* @package block_recentlyaccesseditems
|
||||
* @copyright 2019 University of Nottingham
|
||||
* @author Neill Magill <neill.magill@nottingham.ac.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class block_recentlyaccesseditems_helper_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Tests that the get recent items method can handle getting records when courses have been deleted.
|
||||
*/
|
||||
public function test_get_recent_items() {
|
||||
$this->resetAfterTest();
|
||||
$course = self::getDataGenerator()->create_course();
|
||||
$coursetodelete = self::getDataGenerator()->create_course();
|
||||
$user = self::getDataGenerator()->create_and_enrol($course, 'student');
|
||||
self::getDataGenerator()->enrol_user($user->id, $coursetodelete->id, 'student');
|
||||
|
||||
// Add an activity to each course.
|
||||
$forum = self::getDataGenerator()->create_module('forum', ['course' => $course]);
|
||||
$glossary = self::getDataGenerator()->create_module('glossary', ['course' => $coursetodelete]);
|
||||
self::setUser($user);
|
||||
|
||||
// Get the user to visit the activities.
|
||||
$event1params = ['context' => context_module::instance($forum->cmid), 'objectid' => $forum->id];
|
||||
$event1 = \mod_forum\event\course_module_viewed::create($event1params);
|
||||
$event1->trigger();
|
||||
$event2params = ['context' => context_module::instance($glossary->cmid), 'objectid' => $glossary->id];
|
||||
$event2 = \mod_glossary\event\course_module_viewed::create($event2params);
|
||||
$event2->trigger();
|
||||
$recent1 = helper::get_recent_items();
|
||||
self::assertCount(2, $recent1);
|
||||
$recentlimited = helper::get_recent_items(1);
|
||||
self::assertCount(1, $recentlimited);
|
||||
delete_course($coursetodelete, false);
|
||||
|
||||
// There should be no errors if a course has been deleted.
|
||||
$recent2 = helper::get_recent_items();
|
||||
self::assertCount(1, $recent2);
|
||||
}
|
||||
}
|
||||
@@ -207,6 +207,60 @@ class block_recentlyaccesseditems_privacy_testcase extends \core_privacy\tests\p
|
||||
// Confirm student's data is exported.
|
||||
$writer = \core_privacy\local\request\writer::with_context($studentcontext);
|
||||
$this->assertTrue($writer->has_any_data());
|
||||
|
||||
delete_course($course, false);
|
||||
$sc = context_user::instance($student->id);
|
||||
$approvedlist = new approved_contextlist($student, $component, [$sc->id]);
|
||||
provider::export_user_data($approvedlist);
|
||||
$writer = \core_privacy\local\request\writer::with_context($sc);
|
||||
$this->assertTrue($writer->has_any_data());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test exporting data for an approved contextlist with a deleted course
|
||||
*/
|
||||
public function test_export_user_data_with_deleted_course() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
$component = 'block_recentlyaccesseditems';
|
||||
|
||||
$student = $generator->create_user();
|
||||
$studentcontext = context_user::instance($student->id);
|
||||
|
||||
// Enrol user in course and add course items.
|
||||
$course = $generator->create_course();
|
||||
$generator->enrol_user($student->id, $course->id, 'student');
|
||||
$forum = $generator->create_module('forum', ['course' => $course]);
|
||||
$chat = $generator->create_module('chat', ['course' => $course]);
|
||||
|
||||
// Generate some recent activity.
|
||||
$this->setUser($student);
|
||||
$event = \mod_forum\event\course_module_viewed::create(['context' => context_module::instance($forum->cmid),
|
||||
'objectid' => $forum->id]);
|
||||
$event->trigger();
|
||||
$event = \mod_chat\event\course_module_viewed::create(['context' => context_module::instance($chat->cmid),
|
||||
'objectid' => $chat->id]);
|
||||
$event->trigger();
|
||||
|
||||
// Confirm data is present.
|
||||
$params = [
|
||||
'courseid' => $course->id,
|
||||
'userid' => $student->id,
|
||||
];
|
||||
|
||||
$result = $DB->count_records('block_recentlyaccesseditems', $params);
|
||||
$this->assertEquals(2, $result);
|
||||
delete_course($course, false);
|
||||
|
||||
// Export data for student.
|
||||
$approvedlist = new approved_contextlist($student, $component, [$studentcontext->id]);
|
||||
provider::export_user_data($approvedlist);
|
||||
|
||||
// Confirm student's data is exported.
|
||||
$writer = \core_privacy\local\request\writer::with_context($studentcontext);
|
||||
$this->assertFalse($writer->has_any_data());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,6 @@
|
||||
*/
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018120301; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2018120302; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2018112800; // Requires this Moodle version.
|
||||
$plugin->component = 'block_recentlyaccesseditems'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -43,7 +43,7 @@ $string['editfeeds'] = 'Edit, subscribe or unsubscribe from RSS/Atom news feeds'
|
||||
$string['editnewsfeeds'] = 'Edit news feeds';
|
||||
$string['editrssblock'] = 'Edit RSS headline block';
|
||||
$string['enableautodiscovery'] = 'Enable auto-discovery of feeds?';
|
||||
$string['enableautodiscovery_help'] = 'If enabled, feeds on web pages are found automatically. For example, if http://docs.moodle.org is entered, then http://docs.moodle.org/en/index.php?title=Special:RecentChanges&feed=rss would be found.';
|
||||
$string['enableautodiscovery_help'] = 'If enabled, feeds on web pages are found automatically. For example, if https://docs.moodle.org is entered, then https://docs.moodle.org/en/index.php?title=Special:RecentChanges&feed=rss would be found.';
|
||||
$string['failedfeed'] = 'Feed failed to download - will retry after {$a}';
|
||||
$string['failedfeeds'] = 'One or more RSS feeds have failed';
|
||||
$string['feed'] = 'Feed';
|
||||
|
||||
@@ -50,13 +50,16 @@
|
||||
</div>
|
||||
<div class="w-100 event-name-container text-truncate line-height-3">
|
||||
<a href="{{url}}"
|
||||
title="{{name}}"
|
||||
aria-label='{{#str}} ariaeventlistitem, block_timeline, { "name": "{{name}}", "course": "{{course.fullnamedisplay}}", "date": "{{#userdate}} {{timesort}}, {{#str}} strftimedatetime, core_langconfig {{/str}} {{/userdate}}" } {{/str}}'
|
||||
><h6 class="event-name text-truncate mb-0">{{{name}}}</h6></a>
|
||||
<small class="text-muted text-truncate m-b-0">{{{course.fullnamedisplay}}}</small>
|
||||
title={{#quote}}{{{name}}}{{/quote}}
|
||||
aria-label='{{#str}} ariaeventlistitem, block_timeline, { "name": {{#quote}}{{{name}}}{{/quote}}, "course": {{#quote}}{{{course.fullnamedisplay}}}{{/quote}}, "date": "{{#userdate}} {{timesort}}, {{#str}} strftimedatetime, core_langconfig {{/str}} {{/userdate}}" } {{/str}}'
|
||||
><h6 class="event-name text-truncate mb-0">{{#quote}}{{{name}}}{{/quote}}</h6></a>
|
||||
<small class="text-muted text-truncate m-b-0">{{#quote}}{{{course.fullnamedisplay}}}{{/quote}}</small>
|
||||
{{#action.actionable}}
|
||||
<h6 class="m-b-0 pt-2">
|
||||
<a href="{{{action.url}}}" aria-label="{{{action.name}}}" title="{{{action.name}}}" class="list-group-item-action">{{{action.name}}}</a>
|
||||
{{#action.showitemcount}}
|
||||
<span class="badge badge-secondary">{{action.itemcount}}</span>
|
||||
{{/action.showitemcount}}
|
||||
</h6>
|
||||
{{/action.actionable}}
|
||||
</div>
|
||||
|
||||
@@ -23,20 +23,27 @@
|
||||
{}
|
||||
}}
|
||||
<div data-region="day-filter" class="dropdown">
|
||||
<button type="button" class="btn btn-outline-secondary dropdown-toggle icon-no-margin" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<button type="button" class="btn btn-outline-secondary dropdown-toggle icon-no-margin" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
|
||||
aria-label="{{#str}} ariadayfilter, block_timeline {{/str}}" aria-controls="menudayfilter">
|
||||
{{#pix}} i/duration {{/pix}}
|
||||
<span class="sr-only">
|
||||
{{#str}} ariadayfilter, block_timeline {{/str}}
|
||||
<span data-active-item-text>{{#str}} next30days, block_timeline {{/str}}</span>
|
||||
<span class="sr-only" data-active-item-text>
|
||||
{{#all}} {{#str}} all, core {{/str}} {{/all}}
|
||||
{{#overdue}} {{#str}} overdue, block_timeline {{/str}} {{/overdue}}
|
||||
{{#next7days}} {{#str}}next7days, block_timeline {{/str}} {{/next7days}}
|
||||
{{#next30days}} {{#str}}next30days, block_timeline {{/str}} {{/next30days}}
|
||||
{{#next3months}} {{#str}}next3months, block_timeline {{/str}} {{/next3months}}
|
||||
{{#next6months}} {{#str}}next6months, block_timeline {{/str}} {{/next6months}}
|
||||
</span>
|
||||
</button>
|
||||
<div role="menu" class="dropdown-menu" data-show-active-item>
|
||||
<div id="menudayfilter" role="menu" class="dropdown-menu" data-show-active-item>
|
||||
<a
|
||||
class="dropdown-item {{#all}} active {{/all}}"
|
||||
href="#"
|
||||
data-from="-14"
|
||||
data-filtername="all"
|
||||
aria-label="{{#str}} ariadayfilteroption, block_timeline, {{#str}} all, core {{/str}}{{/str}}"
|
||||
role="menuitem"
|
||||
{{#all}}aria-current="true"{{/all}}
|
||||
>
|
||||
{{#str}} all, core {{/str}}
|
||||
</a>
|
||||
@@ -47,10 +54,12 @@
|
||||
data-to="0"
|
||||
data-filtername="overdue"
|
||||
aria-label="{{#str}} ariadayfilteroption, block_timeline, {{#str}} overdue, block_timeline {{/str}}{{/str}}"
|
||||
role="menuitem"
|
||||
{{#overdue}}aria-current="true"{{/overdue}}
|
||||
>
|
||||
{{#str}} overdue, block_timeline {{/str}}
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<div class="dropdown-divider" role="separator"></div>
|
||||
<h6 class="dropdown-header">{{#str}} duedate, block_timeline {{/str}}</h6>
|
||||
<a
|
||||
class="dropdown-item {{#next7days}} active {{/next7days}}"
|
||||
@@ -59,6 +68,8 @@
|
||||
data-to="7"
|
||||
data-filtername="next7days"
|
||||
aria-label="{{#str}} ariadayfilteroption, block_timeline, {{#str}} next7days, block_timeline {{/str}}{{/str}}"
|
||||
role="menuitem"
|
||||
{{#next7days}}aria-current="true"{{/next7days}}
|
||||
>
|
||||
{{#str}} next7days, block_timeline {{/str}}
|
||||
</a>
|
||||
@@ -69,6 +80,8 @@
|
||||
data-to="30"
|
||||
data-filtername="next30days"
|
||||
aria-label="{{#str}} ariadayfilteroption, block_timeline, {{#str}} next30days, block_timeline {{/str}}{{/str}}"
|
||||
role="menuitem"
|
||||
{{#next30days}}aria-current="true"{{/next30days}}
|
||||
>
|
||||
{{#str}} next30days, block_timeline {{/str}}
|
||||
</a>
|
||||
@@ -79,6 +92,8 @@
|
||||
data-to="90"
|
||||
data-filtername="next3months"
|
||||
aria-label="{{#str}} ariadayfilteroption, block_timeline, {{#str}} next3months, block_timeline {{/str}}{{/str}}"
|
||||
role="menuitem"
|
||||
{{#next3months}}aria-current="true"{{/next3months}}
|
||||
>
|
||||
{{#str}} next3months, block_timeline {{/str}}
|
||||
</a>
|
||||
@@ -89,6 +104,8 @@
|
||||
data-to="180"
|
||||
data-filtername="next6months"
|
||||
aria-label="{{#str}} ariadayfilteroption, block_timeline, {{#str}} next6months, block_timeline {{/str}}{{/str}}"
|
||||
role="menuitem"
|
||||
{{#next6months}}aria-current="true"{{/next6months}}
|
||||
>
|
||||
{{#str}} next6months, block_timeline {{/str}}
|
||||
</a>
|
||||
|
||||
@@ -23,20 +23,23 @@
|
||||
{}
|
||||
}}
|
||||
<div data-region="view-selector" class="btn-group">
|
||||
<button type="button" class="btn btn-outline-secondary dropdown-toggle icon-no-margin" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<button type="button" class="btn btn-outline-secondary dropdown-toggle icon-no-margin" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
|
||||
aria-label="{{#str}} ariaviewselector, block_timeline{{/str}}" aria-controls="menusortby">
|
||||
{{#pix}} i/filter {{/pix}}
|
||||
<span class="sr-only">
|
||||
{{#sorttimelinecourses}}<span data-active-item-text>{{/sorttimelinecourses}}{{#str}} ariaviewselector, block_timeline{{/str}}{{#sorttimelinecourses}}</span>{{/sorttimelinecourses}}
|
||||
{{#sorttimelinedates}}<span data-active-item-text>{{/sorttimelinedates}}{{#str}} sortbydates, block_timeline {{/str}}{{#sorttimelinedates}}</span>{{/sorttimelinedates}}
|
||||
<span class="sr-only" data-active-item-text>
|
||||
{{#sorttimelinecourses}}{{#str}} sortbycourses, block_timeline{{/str}}{{/sorttimelinecourses}}
|
||||
{{#sorttimelinedates}}{{#str}} sortbydates, block_timeline {{/str}}{{/sorttimelinedates}}
|
||||
</span>
|
||||
</button>
|
||||
<div role="menu" class="dropdown-menu dropdown-menu-right list-group hidden" data-show-active-item data-skip-active-class="true" >
|
||||
<div id="menusortby" role="menu" class="dropdown-menu dropdown-menu-right list-group hidden" data-show-active-item data-skip-active-class="true">
|
||||
<a
|
||||
class="dropdown-item {{#sorttimelinedates}}active{{/sorttimelinedates}}"
|
||||
href="#view_dates_{{uniqid}}"
|
||||
data-toggle="tab"
|
||||
data-filtername="sortbydates"
|
||||
aria-label="{{#str}} ariaviewselectoroption, block_timeline, {{#str}} sortbydates, block_timeline {{/str}}{{/str}}"
|
||||
role="menuitem"
|
||||
{{#sorttimelinedates}}aria-current="true"{{/sorttimelinedates}}
|
||||
>
|
||||
{{#str}} sortbydates, block_timeline {{/str}}
|
||||
</a>
|
||||
@@ -46,6 +49,8 @@
|
||||
data-toggle="tab"
|
||||
data-filtername="sortbycourses"
|
||||
aria-label="{{#str}} ariaviewselectoroption, block_timeline, {{#str}} sortbycourses, block_timeline {{/str}}{{/str}}"
|
||||
role="menuitem"
|
||||
{{#sorttimelinecourses}}aria-current="true"{{/sorttimelinecourses}}
|
||||
>
|
||||
{{#str}} sortbycourses, block_timeline {{/str}}
|
||||
</a>
|
||||
|
||||
@@ -36,7 +36,7 @@ Feature: The timeline block allows users to see upcoming courses
|
||||
|
||||
Scenario: Next 30 days in course view
|
||||
Given I log in as "student1"
|
||||
And I click on "Sort" "button" in the "Timeline" "block"
|
||||
And I click on "Sort timeline items" "button" in the "Timeline" "block"
|
||||
When I click on "Sort by courses" "link" in the "Timeline" "block"
|
||||
Then I should see "Course 1" in the "Timeline" "block"
|
||||
And I should see "Course 2" in the "Timeline" "block"
|
||||
@@ -52,9 +52,9 @@ Feature: The timeline block allows users to see upcoming courses
|
||||
|
||||
Scenario: All in course view
|
||||
Given I log in as "student1"
|
||||
And I click on "Next 30 days" "button" in the "Timeline" "block"
|
||||
And I click on "Filter timeline items" "button" in the "Timeline" "block"
|
||||
And I click on "All" "link" in the "Timeline" "block"
|
||||
And I click on "Sort" "button" in the "Timeline" "block"
|
||||
And I click on "Sort timeline items" "button" in the "Timeline" "block"
|
||||
And I click on "Sort by courses" "link" in the "Timeline" "block"
|
||||
When I click on "More courses" "button" in the "Timeline" "block"
|
||||
Then I should see "Course 3" in the "Timeline" "block"
|
||||
@@ -73,9 +73,9 @@ Feature: The timeline block allows users to see upcoming courses
|
||||
|
||||
Scenario: Persistent sort filter
|
||||
Given I log in as "student1"
|
||||
And I click on "Sort" "button" in the "Timeline" "block"
|
||||
And I click on "Sort timeline items" "button" in the "Timeline" "block"
|
||||
And I click on "Sort by dates" "link" in the "Timeline" "block"
|
||||
And I click on "Sort" "button" in the "Timeline" "block"
|
||||
And I click on "Sort timeline items" "button" in the "Timeline" "block"
|
||||
And I click on "Sort by courses" "link" in the "Timeline" "block"
|
||||
And I reload the page
|
||||
Then I should see "Course 1" in the "Timeline" "block"
|
||||
|
||||
@@ -33,7 +33,7 @@ Feature: The timeline block allows users to see upcoming activities
|
||||
|
||||
Scenario: Next 7 days in date view
|
||||
Given I log in as "student1"
|
||||
And I click on "Next 30 days" "button" in the "Timeline" "block"
|
||||
And I click on "Filter timeline items" "button" in the "Timeline" "block"
|
||||
When I click on "Next 7 days" "link" in the "Timeline" "block"
|
||||
Then I should see "Test choice 1 closes" in the "Timeline" "block"
|
||||
And I should see "Test feedback 1 closes" in the "Timeline" "block"
|
||||
@@ -44,7 +44,7 @@ Feature: The timeline block allows users to see upcoming activities
|
||||
|
||||
Scenario: Overdue in date view
|
||||
Given I log in as "student1"
|
||||
And I click on "Next 30 days" "button" in the "Timeline" "block"
|
||||
And I click on "Filter timeline items" "button" in the "Timeline" "block"
|
||||
When I click on "Overdue" "link" in the "Timeline" "block"
|
||||
Then I should see "Test assign 1 is due" in the "Timeline" "block"
|
||||
And I should not see "Test choice 2 closes" in the "Timeline" "block"
|
||||
@@ -55,7 +55,7 @@ Feature: The timeline block allows users to see upcoming activities
|
||||
|
||||
Scenario: All in date view
|
||||
Given I log in as "student1"
|
||||
And I click on "Next 30 days" "button" in the "Timeline" "block"
|
||||
And I click on "Filter timeline items" "button" in the "Timeline" "block"
|
||||
When I click on "All" "link" in the "Timeline" "block"
|
||||
Then I should see "Test assign 1 is due" in the "Timeline" "block"
|
||||
And I should see "Test feedback 1 closes" in the "Timeline" "block"
|
||||
@@ -75,7 +75,7 @@ Feature: The timeline block allows users to see upcoming activities
|
||||
|
||||
Scenario: All in date view no next
|
||||
Given I log in as "student1"
|
||||
And I click on "Next 30 days" "button" in the "Timeline" "block"
|
||||
And I click on "Filter timeline items" "button" in the "Timeline" "block"
|
||||
And I click on "All" "link" in the "Timeline" "block"
|
||||
And I click on "5" "button" in the "Timeline" "block"
|
||||
When I click on "25" "link" in the "Timeline" "block"
|
||||
@@ -89,7 +89,7 @@ Feature: The timeline block allows users to see upcoming activities
|
||||
|
||||
Scenario: Persistent All in date view
|
||||
Given I log in as "student1"
|
||||
And I click on "Next 30 days" "button" in the "Timeline" "block"
|
||||
And I click on "Filter timeline items" "button" in the "Timeline" "block"
|
||||
When I click on "All" "link" in the "Timeline" "block"
|
||||
And I reload the page
|
||||
Then I should see "Test assign 1 is due" in the "Timeline" "block"
|
||||
@@ -110,7 +110,7 @@ Feature: The timeline block allows users to see upcoming activities
|
||||
|
||||
Scenario: Persistent Overdue in date view
|
||||
Given I log in as "student1"
|
||||
And I click on "Next 30 days" "button" in the "Timeline" "block"
|
||||
And I click on "Filter timeline items" "button" in the "Timeline" "block"
|
||||
When I click on "Overdue" "link" in the "Timeline" "block"
|
||||
And I reload the page
|
||||
Then I should see "Test assign 1 is due" in the "Timeline" "block"
|
||||
|
||||
@@ -35,9 +35,9 @@ Feature: The timeline block allows user persistence of their page limits
|
||||
|
||||
Scenario: Toggle the page limit 5 - 25
|
||||
Given I log in as "student1"
|
||||
And I click on "Next 30 days" "button" in the "Timeline" "block"
|
||||
And I click on "Filter timeline items" "button" in the "Timeline" "block"
|
||||
And I click on "All" "link" in the "Timeline" "block"
|
||||
And I click on "Sort" "button" in the "Timeline" "block"
|
||||
And I click on "Sort timeline items" "button" in the "Timeline" "block"
|
||||
And I click on "Sort by dates" "link" in the "Timeline" "block"
|
||||
When I click on "5" "button" in the "Timeline" "block"
|
||||
And I click on "25" "link"
|
||||
@@ -48,9 +48,9 @@ Feature: The timeline block allows user persistence of their page limits
|
||||
|
||||
Scenario: Toggle the page limit 25 - 5
|
||||
Given I log in as "student1"
|
||||
And I click on "Next 30 days" "button" in the "Timeline" "block"
|
||||
And I click on "Filter timeline items" "button" in the "Timeline" "block"
|
||||
And I click on "All" "link" in the "Timeline" "block"
|
||||
And I click on "Sort" "button" in the "Timeline" "block"
|
||||
And I click on "Sort timeline items" "button" in the "Timeline" "block"
|
||||
And I click on "Sort by dates" "link" in the "Timeline" "block"
|
||||
When I click on "5" "button" in the "Timeline" "block"
|
||||
And I click on "25" "link"
|
||||
|
||||
Vendored
+7
@@ -1284,6 +1284,13 @@ class cache implements cache_loader {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may support purging cache of all data belonging to the
|
||||
* current user.
|
||||
*/
|
||||
public function purge_current_user() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,6 +49,7 @@ class core_calendar_export_form extends moodleform {
|
||||
|
||||
$export = array();
|
||||
$export[] = $mform->createElement('radio', 'exportevents', '', get_string('eventsall', 'calendar'), 'all');
|
||||
$export[] = $mform->createElement('radio', 'exportevents', '', get_string('eventsrelatedtocategories', 'calendar'), 'categories');
|
||||
$export[] = $mform->createElement('radio', 'exportevents', '', get_string('eventsrelatedtocourses', 'calendar'), 'courses');
|
||||
$export[] = $mform->createElement('radio', 'exportevents', '', get_string('eventsrelatedtogroups', 'calendar'), 'groups');
|
||||
$export[] = $mform->createElement('radio', 'exportevents', '', get_string('eventspersonal', 'calendar'), 'user');
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ class event_exporter_base extends exporter {
|
||||
'null' => NULL_ALLOWED
|
||||
],
|
||||
'location' => [
|
||||
'type' => PARAM_RAW_TRIMMED,
|
||||
'type' => PARAM_RAW,
|
||||
'optional' => true,
|
||||
'default' => null,
|
||||
'null' => NULL_ALLOWED
|
||||
|
||||
@@ -100,8 +100,6 @@ class footer_options_exporter extends exporter {
|
||||
$params['course'] = $this->calendar->course->id;
|
||||
} else if (null !== $this->calendar->categoryid && $this->calendar->categoryid > 0) {
|
||||
$params['category'] = $this->calendar->categoryid;
|
||||
} else {
|
||||
$params['course'] = SITEID;
|
||||
}
|
||||
|
||||
return $params;
|
||||
|
||||
@@ -100,9 +100,6 @@ class event_vault implements event_vault_interface {
|
||||
$ignorehidden = true,
|
||||
callable $filter = null
|
||||
) {
|
||||
if ($limitnum < 1 || $limitnum > 200) {
|
||||
throw new limit_invalid_parameter_exception("Limit must be between 1 and 200 (inclusive)");
|
||||
}
|
||||
|
||||
$fromquery = function($field, $timefrom, $lastseenmethod, $afterevent, $withduration) {
|
||||
if (!$timefrom) {
|
||||
@@ -186,7 +183,11 @@ class event_vault implements event_vault_interface {
|
||||
}
|
||||
}
|
||||
|
||||
$offset += $limitnum;
|
||||
if (!$limitnum) {
|
||||
break;
|
||||
} else {
|
||||
$offset += $limitnum;
|
||||
}
|
||||
}
|
||||
|
||||
return $events;
|
||||
|
||||
@@ -98,7 +98,7 @@ trait eventtype {
|
||||
$mform->hideIf('categoryid', 'eventtype', 'noteq', 'category');
|
||||
}
|
||||
|
||||
$showall = $CFG->calendar_adminseesall && !has_capability('moodle/calendar:manageentries', \context_system::instance());
|
||||
$showall = is_siteadmin() && !empty($CFG->calendar_adminseesall);
|
||||
if (!empty($eventtypes['course'])) {
|
||||
$mform->addElement('course', 'courseid', get_string('course'), ['limittoenrolled' => !$showall]);
|
||||
$mform->hideIf('courseid', 'eventtype', 'noteq', 'course');
|
||||
|
||||
@@ -532,8 +532,7 @@ class provider implements
|
||||
AND ctx.contextlevel = :categorycontext)
|
||||
OR (e.courseid = ctx.instanceid
|
||||
AND (e.eventtype = 'course'
|
||||
OR e.eventtype = 'group'
|
||||
OR e.modulename != '0')
|
||||
OR e.eventtype = 'group')
|
||||
AND ctx.contextlevel = :coursecontext)
|
||||
OR (e.userid = ctx.instanceid
|
||||
AND e.eventtype = 'user'
|
||||
|
||||
@@ -40,8 +40,8 @@ $time = optional_param('preset_time', 'weeknow', PARAM_ALPHA);
|
||||
$now = $calendartype->timestamp_to_date_array(time());
|
||||
|
||||
// Let's see if we have sufficient and correct data
|
||||
$allowed_what = array('all', 'user', 'groups', 'courses');
|
||||
$allowed_time = array('weeknow', 'weeknext', 'monthnow', 'monthnext', 'recentupcoming', 'custom');
|
||||
$allowedwhat = ['all', 'user', 'groups', 'courses', 'categories'];
|
||||
$allowedtime = ['weeknow', 'weeknext', 'monthnow', 'monthnext', 'recentupcoming', 'custom'];
|
||||
|
||||
if (!empty($generateurl)) {
|
||||
$authtoken = sha1($user->id . $user->password . $CFG->calendar_exportsalt);
|
||||
@@ -56,9 +56,9 @@ if (!empty($generateurl)) {
|
||||
redirect($link->out());
|
||||
die;
|
||||
}
|
||||
|
||||
$paramcategory = false;
|
||||
if(!empty($what) && !empty($time)) {
|
||||
if(in_array($what, $allowed_what) && in_array($time, $allowed_time)) {
|
||||
if(in_array($what, $allowedwhat) && in_array($time, $allowedtime)) {
|
||||
$courses = enrol_get_users_courses($user->id, true, 'id, visible, shortname');
|
||||
// Array of courses that we will pass to calendar_get_legacy_events() which
|
||||
// is initially set to the list of the user's courses.
|
||||
@@ -78,6 +78,7 @@ if(!empty($what) && !empty($time)) {
|
||||
$courses[SITEID] = new stdClass;
|
||||
$courses[SITEID]->shortname = get_string('globalevents', 'calendar');
|
||||
$paramcourses[SITEID] = $courses[SITEID];
|
||||
$paramcategory = true;
|
||||
} else if ($what == 'groups') {
|
||||
$users = false;
|
||||
$paramcourses = array();
|
||||
@@ -85,6 +86,11 @@ if(!empty($what) && !empty($time)) {
|
||||
$users = $user->id;
|
||||
$groups = false;
|
||||
$paramcourses = array();
|
||||
} else if ($what == 'categories') {
|
||||
$users = $user->id;
|
||||
$groups = false;
|
||||
$paramcourses = array();
|
||||
$paramcategory = true;
|
||||
} else {
|
||||
$users = false;
|
||||
$groups = false;
|
||||
@@ -180,7 +186,9 @@ if(!empty($what) && !empty($time)) {
|
||||
die();
|
||||
}
|
||||
}
|
||||
$events = calendar_get_legacy_events($timestart, $timeend, $users, $groups, array_keys($paramcourses), false);
|
||||
$limitnum = 0;
|
||||
$events = calendar_get_legacy_events($timestart, $timeend, $users, $groups, array_keys($paramcourses), false, true,
|
||||
$paramcategory, $limitnum);
|
||||
|
||||
$ical = new iCalendar;
|
||||
$ical->add_property('method', 'PUBLISH');
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user