Compare commits
562 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 23360fd95b | |||
| 0ed1c5d259 | |||
| 28f52bc329 | |||
| 8c18772edc | |||
| dd819dbb88 | |||
| 4c48d88c46 | |||
| af16b393ea | |||
| 2dc0dc6293 | |||
| a3aa1600de | |||
| fb629ce56a | |||
| 33f31a12d4 | |||
| 92033f7e9d | |||
| 8e28bc402b | |||
| 1cee1602be | |||
| bea116e2ff | |||
| 106eb4921a | |||
| bc1d916868 | |||
| a053131595 | |||
| f3b9685ad0 | |||
| 93e09909ab | |||
| f373c08314 | |||
| dacac4d10b | |||
| 35d1b3da7a | |||
| a52742dea7 | |||
| ffd642a654 | |||
| dbbbf02dc0 | |||
| e29ea1b2c9 | |||
| 74accbdd85 | |||
| eea5900c13 | |||
| 01005ff69a | |||
| ee3ba0c3f8 | |||
| 5bbc58a7fb | |||
| c6a447f1b7 | |||
| 2584ce25c7 | |||
| 2dd87cb37a | |||
| 53456587f6 | |||
| b9874d0c13 | |||
| bf58adf836 | |||
| afeb42ed9e | |||
| 52cabc8f66 | |||
| 4197baf775 | |||
| ba9f218e17 | |||
| 32dc9a45d6 | |||
| 8c696ccafc | |||
| 0bfbdaef20 | |||
| 55fc1104d5 | |||
| 7032ff37b7 | |||
| 1d0867ae48 | |||
| 4e50a38018 | |||
| a54703d88f | |||
| 4ba0709401 | |||
| f72bc10fe3 | |||
| b15859b879 | |||
| ed248331a4 | |||
| 5b8ca7ac93 | |||
| 0efb93bbe9 | |||
| f3008f556c | |||
| aac5a4f103 | |||
| e5881ad801 | |||
| c244743775 | |||
| deb442a293 | |||
| 93a935049e | |||
| f2fb8f5a7f | |||
| 3a5ae742f1 | |||
| 017d8f1aec | |||
| 95e1ac08fe | |||
| ef43566698 | |||
| d33f7642dd | |||
| a9a72025b6 | |||
| 222189cc75 | |||
| 62c58d22be | |||
| b962ac2439 | |||
| c2f08942d9 | |||
| 9072b59c0e | |||
| f86bb360b5 | |||
| 90e86c7568 | |||
| f730eb2a81 | |||
| 485decb3a2 | |||
| b95a012124 | |||
| f7e3be1bbb | |||
| 5a5efa8bd9 | |||
| 079c73c98d | |||
| 3fb4b3a9ed | |||
| c7d6a0e356 | |||
| 8b57dd0768 | |||
| 97d966df08 | |||
| fe67ec46da | |||
| 285e7e5eb3 | |||
| 53e3825619 | |||
| 90156e427c | |||
| 5074849703 | |||
| c5a8568799 | |||
| ab7056032d | |||
| 0d063c69ac | |||
| ac6ab5b8d2 | |||
| 74fbdebd0d | |||
| e523626603 | |||
| c33e03ae95 | |||
| b3f3c3c395 | |||
| 072c0b4196 | |||
| 638aa54cbb | |||
| 8d9e621f2c | |||
| bb3956eb5c | |||
| a8fc3a33ab | |||
| 2c6f3befeb | |||
| d11850101e | |||
| 531c1ec0be | |||
| 8ae8ed4aca | |||
| 1faba07075 | |||
| 587b99609d | |||
| c9192ffccb | |||
| 4290182491 | |||
| cb1c0ae4dc | |||
| ffc7fda945 | |||
| a9ba142326 | |||
| 59527f3e7a | |||
| 8122557661 | |||
| ae84db35c7 | |||
| ab8b6fae1a | |||
| b3ca553f3a | |||
| 7e31bb6dc2 | |||
| 5e39900691 | |||
| a4f5cdddcf | |||
| 4d26fcd737 | |||
| 11127354db | |||
| f751b97393 | |||
| 9265a5db56 | |||
| f5942f3e6f | |||
| 3ad8f33235 | |||
| abeb033dfb | |||
| 98b955d665 | |||
| 7624f86fb2 | |||
| 7f4835a7da | |||
| ba76815aaf | |||
| 70dcc1e39a | |||
| 25ffa9a67f | |||
| 99864cdf74 | |||
| d356128f05 | |||
| c33c78a33c | |||
| 3ec562a322 | |||
| 07e6a3c6c1 | |||
| 5ec875b281 | |||
| a202546fd3 | |||
| 589b4c0360 | |||
| 5f6cf92f97 | |||
| 43391de3e2 | |||
| fbf621bd29 | |||
| 5a10b55442 | |||
| 554167b43a | |||
| c90e9381d2 | |||
| 8ac3e5ea59 | |||
| 74554368a0 | |||
| 6f4d9a89d5 | |||
| 7fd70c34a0 | |||
| a31c6bc4b9 | |||
| 6160631e28 | |||
| c82ad7c6e0 | |||
| 087ab1e345 | |||
| f7c9dd2158 | |||
| 984bcd770c | |||
| e84d284e65 | |||
| 352b9b00d6 | |||
| 2a7bb7a0f8 | |||
| 269e1f027e | |||
| 48903d67ea | |||
| 3bfdb64f43 | |||
| a42233487e | |||
| 07da0155a7 | |||
| 1a7cf17ed8 | |||
| 818bc4c9a7 | |||
| 8708ea0968 | |||
| cdffe52b28 | |||
| 67970b8675 | |||
| d979b4614d | |||
| 85eacc3a31 | |||
| bfc16e66a5 | |||
| 3a8f07f241 | |||
| e621a47c48 | |||
| 4bb9787760 | |||
| 2469a838f1 | |||
| c28e5eaee2 | |||
| f85f0c80da | |||
| 4643358a5c | |||
| 2774649389 | |||
| 18d41005e3 | |||
| a281621189 | |||
| ab8fa2ff7a | |||
| fb0e1b10fe | |||
| 78f7595fd9 | |||
| 338097adce | |||
| f98e259ddb | |||
| 0e106b8332 | |||
| 25e3a8a14c | |||
| 46280f7478 | |||
| 80bf9ac40a | |||
| e2b4084bfd | |||
| 2a667a0820 | |||
| a4ad655bf1 | |||
| 653e23bcc1 | |||
| 80d970cd0e | |||
| f8962b4270 | |||
| e2f921ea2a | |||
| 3586517ff8 | |||
| 4e8f93abba | |||
| 164d1e290e | |||
| 373bd248b9 | |||
| c6bbd48e23 | |||
| 4db85a5943 | |||
| 726fce53e7 | |||
| 5607f04f15 | |||
| 5d37f70d68 | |||
| 163e56dfbb | |||
| 07f1e2b49c | |||
| 328159027c | |||
| c6e47712f5 | |||
| 4a1d28e6c6 | |||
| cbbfaad531 | |||
| bb78b2fd22 | |||
| 021b544c7a | |||
| 71e94edfd8 | |||
| 37b143aeaf | |||
| d1e6ca40a0 | |||
| d926390fba | |||
| 1dcc39e9a1 | |||
| e48bab0b24 | |||
| bc71da7f50 | |||
| 3739127caf | |||
| 4321953314 | |||
| d69fcc7bc1 | |||
| 723ed522a5 | |||
| 0d011d9ad3 | |||
| 15e5a39266 | |||
| 1a63aaddeb | |||
| ec66fbbeec | |||
| 11580ef62a | |||
| 8afd92382e | |||
| cf4753e354 | |||
| ed8c3327fb | |||
| 3bd5edccf8 | |||
| 01932fa6fa | |||
| db8ff4bc78 | |||
| 2dced48edf | |||
| 835bc90d7c | |||
| 4d160525d3 | |||
| b3d4341009 | |||
| bfa2ed7f6c | |||
| 00fe8e5e3f | |||
| 5aeb8b003c | |||
| d90b7b734c | |||
| 26aaa71667 | |||
| 2265e74a31 | |||
| 04f82f5669 | |||
| e4d2487dd2 | |||
| b603370a23 | |||
| 6ed25aee09 | |||
| 9fff50ae00 | |||
| 787d1a8f53 | |||
| 896ce9b4e6 | |||
| c41605e282 | |||
| 70ae23829f | |||
| f29a819640 | |||
| c89d29f8e0 | |||
| dc5cbf1532 | |||
| 08c1b6eff4 | |||
| 02660c64ff | |||
| 787573a007 | |||
| 6585f80f29 | |||
| 7bcc28ac2f | |||
| cc22e91f8f | |||
| 3b2ea17ec4 | |||
| ce37064429 | |||
| 6aa0203deb | |||
| d6d9c4a9c3 | |||
| 993a1ac38f | |||
| e76a83ebf9 | |||
| e6d2eb3792 | |||
| 0f11ec17ca | |||
| c09577b5f3 | |||
| 68124fe0af | |||
| 5611053587 | |||
| c493c6ba2a | |||
| ce589fb4fe | |||
| d8564f56f0 | |||
| e17ee1e9c5 | |||
| 9ddd0bd72a | |||
| fba8a2e290 | |||
| 6f4f3946f6 | |||
| b6098b92b0 | |||
| a42ebf3385 | |||
| 6acdd1f3ab | |||
| 602b3815a0 | |||
| b06b50cf1a | |||
| 9c2f96a13d | |||
| 9214bc50a2 | |||
| 45e0bc6372 | |||
| b1e73801d1 | |||
| 445308fc3b | |||
| 3179ca58b7 | |||
| 86cbb2c450 | |||
| b84949f618 | |||
| 0c4220fba5 | |||
| 743ad4f126 | |||
| 4a5e209d8c | |||
| 6b50178e4e | |||
| 22227b7701 | |||
| db4031fa52 | |||
| 9a68e396e8 | |||
| b5cc09d20b | |||
| 75528e9305 | |||
| 1082da020e | |||
| 7d085745be | |||
| d46c89d4e7 | |||
| 8817bcf7ba | |||
| 00b7c8a2fd | |||
| c1d089b1be | |||
| 3bbd2e7892 | |||
| c35f42e679 | |||
| 070262c8f9 | |||
| ac3022fb0e | |||
| eed84bfd4b | |||
| 133c06b2e1 | |||
| 6c18a3c802 | |||
| e7c4f87a5d | |||
| 2e95bfa5f2 | |||
| f8ca828c02 | |||
| 22e22a39af | |||
| 6823ac39cc | |||
| 1e5a1eb77b | |||
| 910247b15e | |||
| b94131a90e | |||
| cfe4df185c | |||
| 41c817ef38 | |||
| 32549792c2 | |||
| b6c6b99470 | |||
| 72f7cc6a07 | |||
| a6757673a2 | |||
| e47c42f4a8 | |||
| c51e98722b | |||
| d513f79a24 | |||
| e7048861a6 | |||
| a188724a19 | |||
| d165d2cf24 | |||
| 6db0ef42d4 | |||
| 0e526f9ea6 | |||
| f2520ef454 | |||
| 9fd041ad16 | |||
| 608923c445 | |||
| ec3ade054f | |||
| 0edc92e6bf | |||
| ebdcc591a2 | |||
| 029a29a3cf | |||
| 856dbae2cb | |||
| 729c7f143b | |||
| 5f42866391 | |||
| e0b69258fb | |||
| e684411988 | |||
| 1e15527fa5 | |||
| 0373f42728 | |||
| 8a33f2a530 | |||
| 864cf11c2f | |||
| ba6e9f22b4 | |||
| 554afbe2cd | |||
| a0f9277b51 | |||
| ebda22d612 | |||
| 0ad03ab13c | |||
| 4dcdab69f6 | |||
| a8321fc3ac | |||
| 167a73cffc | |||
| b19bf809dc | |||
| d2f333357d | |||
| a1d78eac23 | |||
| 937831c736 | |||
| de7951b817 | |||
| 0e1c96a44c | |||
| cdabef710d | |||
| 2a7b253e82 | |||
| b87f4d1ea5 | |||
| c3606bf9ba | |||
| 6806700b01 | |||
| 071633c194 | |||
| ef01fb49a6 | |||
| b9fb0ede39 | |||
| 3567a37302 | |||
| 34770e55b2 | |||
| f7e9dbcbe2 | |||
| e353a80475 | |||
| 8226afc20e | |||
| 535abb009d | |||
| 3125397713 | |||
| 9ce8250f77 | |||
| 1eefc40296 | |||
| a3d444a3af | |||
| 3509e3771a | |||
| d7dd9aa732 | |||
| 2335a0018d | |||
| 6cb68d8d3a | |||
| a3b99f1c03 | |||
| 139c549e3c | |||
| 8336d2be91 | |||
| 46629407c2 | |||
| f1996f596b | |||
| db2718a65d | |||
| 2e26127821 | |||
| ccbc8fadf8 | |||
| 0905763270 | |||
| 558d773213 | |||
| 4288a75a48 | |||
| cdcb157b7e | |||
| 12d454cb4c | |||
| c1d1c098f4 | |||
| 402ce4d5f5 | |||
| f8db8073a2 | |||
| 2eb6dd8cdc | |||
| 32cf700c31 | |||
| c2542c35f6 | |||
| a563739951 | |||
| 04d312fbcc | |||
| e82c98f26d | |||
| f40585c94d | |||
| b91250e430 | |||
| ea3f8fa0cb | |||
| e1f11ce7b1 | |||
| 7c920b5706 | |||
| 3abc7d1c4e | |||
| d2decc7fb0 | |||
| 784e8588ee | |||
| d00a536a0c | |||
| 9a9a841497 | |||
| e319e5a2d0 | |||
| 90574fd1c8 | |||
| 67e8ddc65f | |||
| a41f004fb7 | |||
| 22c756c644 | |||
| b42298d3fa | |||
| e13c169ded | |||
| 766a5891b6 | |||
| 5108b3e7c1 | |||
| 87f417f510 | |||
| 7a75832faa | |||
| 8b2d2a5fc6 | |||
| 5b9dead542 | |||
| 50b024746e | |||
| c0ab266dd0 | |||
| a90270455e | |||
| f0be825426 | |||
| dce24b2a5a | |||
| 985b62f3a9 | |||
| d92499f4c4 | |||
| 6c61222fbb | |||
| 6e19e0c175 | |||
| 92ae0fda60 | |||
| 9702e72bd7 | |||
| 1401dd8f47 | |||
| aae0a865bb | |||
| fed7bdfa96 | |||
| 57a38cf813 | |||
| ed6478fb7e | |||
| 26ce8a1f15 | |||
| f2ceab9582 | |||
| 02151fbe85 | |||
| 6db9bc560d | |||
| 440b00b7d3 | |||
| ba0bd5b9ab | |||
| cf2cda4b31 | |||
| 3cc6435b18 | |||
| e0b383aae0 | |||
| 2121f50755 | |||
| 121c9d3099 | |||
| 32e709a9dc | |||
| 397a8936cb | |||
| 4e3551004e | |||
| e48dd18997 | |||
| 6d36432f2f | |||
| aaf67d012b | |||
| 316706511d | |||
| b407285890 | |||
| 042b90d078 | |||
| c2465fa0e3 | |||
| 4b4f760bfa | |||
| 9ec4ffe9be | |||
| 08e1b1e49e | |||
| 8bf0e602c6 | |||
| 4583242dd6 | |||
| af97a17395 | |||
| 89574179da | |||
| 6ee2dd680e | |||
| 72fce6fc46 | |||
| 3c57ce6e72 | |||
| 79d55f5dee | |||
| d9864df62c | |||
| a04b4d89b2 | |||
| 21735b6075 | |||
| 156371af9c | |||
| a3a8a4a970 | |||
| 3848d57eef | |||
| 4afdece091 | |||
| 649bad89a2 | |||
| 886ce4cfcb | |||
| 8da74658b5 | |||
| 59f2ef03ae | |||
| d1b41e0a34 | |||
| c2e2d38d99 | |||
| 7bde88208c | |||
| 2e2686acf2 | |||
| ed532e8f3b | |||
| 94535cbc8a | |||
| 12d3875811 | |||
| be52d0dace | |||
| e078aa1351 | |||
| 1b13df8f65 | |||
| 3111a4f120 | |||
| aacfdda594 | |||
| 349027033c | |||
| 4ca5db7c0b | |||
| 4c72716d43 | |||
| 733e84fc26 | |||
| 594253c322 | |||
| 1cf3129e86 | |||
| 6178a8c820 | |||
| 4c22adddef | |||
| 91b779aa02 | |||
| 27946d0c65 | |||
| 9ac0280f80 | |||
| b2931bc620 | |||
| 7acd833a3c | |||
| 8f48fbf6f6 | |||
| 81636e4694 | |||
| 91df0eb289 | |||
| 27e0254e8f | |||
| 1929107b8c | |||
| e587b9be09 | |||
| 80ff09db28 | |||
| 61495ffbcb | |||
| 3bc489a0ec | |||
| 6d3a686c2c | |||
| e9fe4cc37c | |||
| 4457c6605b | |||
| d82b0d8ed9 | |||
| f16b97eb8c | |||
| dda3512514 | |||
| f4fe47705e | |||
| 3ec84d746a | |||
| 330f45b6e2 | |||
| cddbbc1752 | |||
| 9407afa0ba | |||
| a62863c658 | |||
| 1164882eff | |||
| 7b0a42b20c | |||
| ff14679150 | |||
| e79a53e370 | |||
| e78f6b391a | |||
| 5a04868aec | |||
| b3a6face59 | |||
| e7714135a4 | |||
| e2b2fad613 | |||
| b85bdc91d4 | |||
| 39f02182de | |||
| fc73f127eb | |||
| e35a24916f | |||
| 36916193d2 | |||
| c85f8d3f93 | |||
| 4bdc05f059 |
+29
-1
@@ -2,7 +2,7 @@
|
||||
# process (which uses our internal CI system) this file is here for the benefit
|
||||
# of community developers git clones - see MDL-51458.
|
||||
|
||||
sudo: false
|
||||
sudo: required
|
||||
|
||||
# We currently disable Travis notifications entirely until https://github.com/travis-ci/travis-ci/issues/4976
|
||||
# is fixed.
|
||||
@@ -16,6 +16,12 @@ php:
|
||||
- 7.1
|
||||
- 5.6
|
||||
|
||||
addons:
|
||||
packages:
|
||||
- mysql-server-5.6
|
||||
- mysql-client-core-5.6
|
||||
- mysql-client-5.6
|
||||
|
||||
services:
|
||||
- redis-server
|
||||
|
||||
@@ -70,6 +76,28 @@ cache:
|
||||
- $HOME/.npm
|
||||
|
||||
install:
|
||||
- sudo apt-get -y install haveged
|
||||
- sudo service haveged start
|
||||
- >
|
||||
if [ "$DB" = 'mysqli' ];
|
||||
then
|
||||
sudo mkdir /mnt/ramdisk
|
||||
sudo mount -t tmpfs -o size=1024m tmpfs /mnt/ramdisk
|
||||
sudo stop mysql
|
||||
sudo mv /var/lib/mysql /mnt/ramdisk
|
||||
sudo ln -s /mnt/ramdisk/mysql /var/lib/mysql
|
||||
sudo start mysql
|
||||
fi
|
||||
- >
|
||||
if [ "$DB" = 'pgsql' ];
|
||||
then
|
||||
sudo mkdir /mnt/ramdisk
|
||||
sudo mount -t tmpfs -o size=1024m tmpfs /mnt/ramdisk
|
||||
sudo service postgresql stop
|
||||
sudo mv /var/lib/postgresql /mnt/ramdisk
|
||||
sudo ln -s /mnt/ramdisk/postgresql /var/lib/postgresql
|
||||
sudo service postgresql start 9.1
|
||||
fi
|
||||
- >
|
||||
if [ "$TASK" = 'PHPUNIT' ];
|
||||
then
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ if ($PAGE->user_allowed_editing()) {
|
||||
$savebutton = false;
|
||||
$outputhtml = '';
|
||||
foreach ($settingspage->children as $childpage) {
|
||||
if ($childpage->is_hidden()) {
|
||||
if ($childpage->is_hidden() || !$childpage->check_access()) {
|
||||
continue;
|
||||
}
|
||||
if ($childpage instanceof admin_externalpage) {
|
||||
|
||||
@@ -53,7 +53,7 @@ Options:
|
||||
-h, --help Print out this help
|
||||
|
||||
Example:
|
||||
\$ sudo -u www-data /usr/bin/php admin/cli/mysql_collation.php --collation=utf8_general_ci
|
||||
\$ sudo -u www-data /usr/bin/php admin/cli/mysql_collation.php --collation=utf8mb4_unicode_ci
|
||||
";
|
||||
|
||||
if (!empty($options['collation'])) {
|
||||
@@ -145,9 +145,22 @@ if (!empty($options['collation'])) {
|
||||
$skipped++;
|
||||
|
||||
} else {
|
||||
$DB->change_database_structure("ALTER TABLE $table->name DEFAULT CHARACTER SET $charset DEFAULT COLLATE = $collation");
|
||||
echo "CONVERTED\n";
|
||||
$converted++;
|
||||
try {
|
||||
$DB->change_database_structure("ALTER TABLE $table->name CONVERT TO CHARACTER SET $charset COLLATE $collation");
|
||||
echo "CONVERTED\n";
|
||||
$converted++;
|
||||
} catch (ddl_exception $e) {
|
||||
$result = mysql_set_row_format($table->name, $charset, $collation, $engine);
|
||||
if ($result) {
|
||||
echo "CONVERTED\n";
|
||||
$converted++;
|
||||
} else {
|
||||
// We don't know what the problem is. Stop the conversion.
|
||||
cli_error("Error: Tried to convert $table->name, but there was a problem. Please check the details of this
|
||||
table and try again.");
|
||||
die();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "SHOW FULL COLUMNS FROM $table->name WHERE collation IS NOT NULL";
|
||||
@@ -290,3 +303,26 @@ function mysql_get_column_collations($tablename) {
|
||||
$rs->close();
|
||||
return $collations;
|
||||
}
|
||||
|
||||
function mysql_set_row_format($tablename, $charset, $collation, $engine) {
|
||||
global $DB;
|
||||
|
||||
$sql = "SELECT row_format
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE table_schema = DATABASE() AND table_name = ?";
|
||||
$rs = $DB->get_record_sql($sql, array($tablename));
|
||||
if ($rs) {
|
||||
if ($rs->row_format == 'Compact' || $rs->row_format == 'Redundant') {
|
||||
$rowformat = $DB->get_row_format_sql($engine, $collation);
|
||||
// Try to convert to compressed format and then try updating the collation again.
|
||||
$DB->change_database_structure("ALTER TABLE $tablename $rowformat");
|
||||
$DB->change_database_structure("ALTER TABLE $tablename CONVERT TO CHARACTER SET $charset COLLATE $collation");
|
||||
} else {
|
||||
// Row format may not be the problem. Can not diagnose problem. Send fail reply.
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ switch ($action) {
|
||||
// remove from enabled list
|
||||
$key = array_search($editor, $active_editors);
|
||||
unset($active_editors[$key]);
|
||||
add_to_config_log('editor_visibility', '1', '0', $editor);
|
||||
break;
|
||||
|
||||
case 'enable':
|
||||
@@ -54,6 +55,7 @@ switch ($action) {
|
||||
if (!in_array($editor, $active_editors)) {
|
||||
$active_editors[] = $editor;
|
||||
$active_editors = array_unique($active_editors);
|
||||
add_to_config_log('editor_visibility', '0', '1', $editor);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -66,6 +68,7 @@ switch ($action) {
|
||||
$fsave = $active_editors[$key];
|
||||
$active_editors[$key] = $active_editors[$key + 1];
|
||||
$active_editors[$key + 1] = $fsave;
|
||||
add_to_config_log('editor_position', $key, $key + 1, $editor);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -79,6 +82,7 @@ switch ($action) {
|
||||
$fsave = $active_editors[$key];
|
||||
$active_editors[$key] = $active_editors[$key - 1];
|
||||
$active_editors[$key - 1] = $fsave;
|
||||
add_to_config_log('editor_position', $key, $key - 1, $editor);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1905,4 +1905,180 @@
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
<MOODLE version="3.3" requires="2.7">
|
||||
<UNICODE level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unicoderequired" />
|
||||
</FEEDBACK>
|
||||
</UNICODE>
|
||||
<DATABASE level="required">
|
||||
<VENDOR name="mariadb" version="5.5.31" />
|
||||
<VENDOR name="mysql" version="5.5.31" />
|
||||
<VENDOR name="postgres" version="9.3" />
|
||||
<VENDOR name="mssql" version="10.0" />
|
||||
<VENDOR name="oracle" version="10.2" />
|
||||
</DATABASE>
|
||||
<PHP version="5.6.5" 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="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="intlrecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="json" level="required">
|
||||
</PHP_EXTENSION>
|
||||
<PHP_EXTENSION name="hash" level="required"/>
|
||||
<PHP_EXTENSION name="fileinfo" level="required"/>
|
||||
</PHP_EXTENSIONS>
|
||||
<PHP_SETTINGS>
|
||||
<PHP_SETTING name="memory_limit" value="96M" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="settingmemorylimit" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="file_uploads" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="settingfileuploads" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
<PHP_SETTING name="opcache.enable" value="1" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="opcacherecommended" />
|
||||
</FEEDBACK>
|
||||
</PHP_SETTING>
|
||||
</PHP_SETTINGS>
|
||||
<CUSTOM_CHECKS>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbstorageengine" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="quizattemptsupgradedmessage" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="slashargumentswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unsupporteddbtablerowformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="unoconvwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="libcurlwarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfileformat" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddbfilepertable" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
|
||||
<FEEDBACK>
|
||||
<ON_ERROR message="unsupporteddblargeprefix" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="ishttpswarning" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
<CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
|
||||
<FEEDBACK>
|
||||
<ON_CHECK message="incompleteunicodesupport" />
|
||||
</FEEDBACK>
|
||||
</CUSTOM_CHECK>
|
||||
</CUSTOM_CHECKS>
|
||||
</MOODLE>
|
||||
</COMPATIBILITY_MATRIX>
|
||||
|
||||
@@ -232,7 +232,7 @@ class site_registration_form extends moodleform {
|
||||
}
|
||||
$language = get_config('hub', 'site_language_' . $cleanhuburl);
|
||||
if ($language === false) {
|
||||
$language = current_language();
|
||||
$language = explode('_', current_language())[0];
|
||||
}
|
||||
$geolocation = get_config('hub', 'site_geolocation_' . $cleanhuburl);
|
||||
$contactable = get_config('hub', 'site_contactable_' . $cleanhuburl);
|
||||
@@ -422,12 +422,12 @@ class site_registration_form extends moodleform {
|
||||
$mform->addElement('checkbox', 'badges', '',
|
||||
" " . get_string('badgesnumber', 'hub', $badges));
|
||||
$mform->setDefault('badges', $badgesnumber != -1);
|
||||
$mform->setType('resources', PARAM_INT);
|
||||
$mform->setType('badges', PARAM_INT);
|
||||
|
||||
$mform->addElement('checkbox', 'issuedbadges', '',
|
||||
" " . get_string('issuedbadgesnumber', 'hub', $issuedbadges));
|
||||
$mform->setDefault('issuedbadges', $issuedbadgesnumber != -1);
|
||||
$mform->setType('resources', PARAM_INT);
|
||||
$mform->setType('issuedbadges', PARAM_INT);
|
||||
|
||||
$mform->addElement('checkbox', 'participantnumberaverage', '',
|
||||
" " . get_string('participantnumberaverage', 'hub', $participantnumberaverage));
|
||||
|
||||
+1
-1
@@ -1612,7 +1612,7 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
$row = new html_table_row();
|
||||
$row->attributes['class'] = 'type-' . $plugin->type . ' name-' . $plugin->type . '_' . $plugin->name;
|
||||
|
||||
if ($this->page->theme->resolve_image_location('icon', $plugin->type . '_' . $plugin->name)) {
|
||||
if ($this->page->theme->resolve_image_location('icon', $plugin->type . '_' . $plugin->name, null)) {
|
||||
$icon = $this->output->pix_icon('icon', '', $plugin->type . '_' . $plugin->name, array('class' => 'icon pluginicon'));
|
||||
} else {
|
||||
$icon = $this->output->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
|
||||
|
||||
@@ -179,7 +179,8 @@ preferences,moodle|/user/preferences.php|preferences',
|
||||
'idnumber' => new lang_string('sort_idnumber', 'admin'),
|
||||
);
|
||||
$temp->add(new admin_setting_configselect('navsortmycoursessort', new lang_string('navsortmycoursessort', 'admin'), new lang_string('navsortmycoursessort_help', 'admin'), 'sortorder', $sortoptions));
|
||||
$temp->add(new admin_setting_configtext('navcourselimit',new lang_string('navcourselimit','admin'),new lang_string('confignavcourselimit', 'admin'),20,PARAM_INT));
|
||||
$temp->add(new admin_setting_configtext('navcourselimit', new lang_string('navcourselimit', 'admin'),
|
||||
new lang_string('confignavcourselimit', 'admin'), 10, PARAM_INT));
|
||||
$temp->add(new admin_setting_configcheckbox('usesitenameforsitepages', new lang_string('usesitenameforsitepages', 'admin'), new lang_string('configusesitenameforsitepages', 'admin'), 0));
|
||||
$temp->add(new admin_setting_configcheckbox('linkadmincategories', new lang_string('linkadmincategories', 'admin'), new lang_string('linkadmincategories_help', 'admin'), 1));
|
||||
$temp->add(new admin_setting_configcheckbox('linkcoursesections', new lang_string('linkcoursesections', 'admin'), new lang_string('linkcoursesections_help', 'admin'), 0));
|
||||
|
||||
@@ -325,6 +325,7 @@ if ($verbose || $status) {
|
||||
}
|
||||
|
||||
// Update status after auto-rerun finished.
|
||||
$status = 0;
|
||||
foreach ($exitcodes as $name => $exitcode) {
|
||||
if ($exitcode) {
|
||||
if (!empty($parallelrun)) {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
@tool @tool_behat
|
||||
Feature: Backported behat step definitions
|
||||
In order to provide feature file compatibility between multiple Moodle versions
|
||||
As a developer
|
||||
I need to be able to use backported steps from newer Moodle versiosn
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | format |
|
||||
| Course 1 | C101 | topics |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C101 | student |
|
||||
| teacher1 | C101 | editingteacher |
|
||||
|
||||
Scenario: I am on the course homepage
|
||||
When I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
Then I should see "Topic 1"
|
||||
|
||||
@javascript
|
||||
Scenario: I am on the course homepage with JS
|
||||
When I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage
|
||||
Then I should see "Topic 1"
|
||||
|
||||
Scenario: I am on the course homepage with editing
|
||||
When I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
Then I should see "Topic 1"
|
||||
And I should see "Add an activity or resource"
|
||||
|
||||
@javascript
|
||||
Scenario: I am on the course homepage with editing and JS
|
||||
When I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
Then I should see "Topic 1"
|
||||
And I should see "Add an activity or resource"
|
||||
@@ -31,7 +31,7 @@ $string['delete_confirmation'] = 'Are you absolutely sure you want to remove <st
|
||||
$string['deletea'] = 'Delete {$a}';
|
||||
$string['deletefiletypes'] = 'Delete a file type';
|
||||
$string['description'] = 'Custom description';
|
||||
$string['description_help'] = 'Simple file type description, e.g. ‘Kindle ebook’. If your site supports multiple languages and uses the multi-language filter, you can enter multi-language tags in this field to supply a description in different languages.';
|
||||
$string['description_help'] = 'Simple file type description, e.g. \'Kindle ebook\'. If your site supports multiple languages and uses the multi-language filter, you can enter multi-language tags in this field to supply a description in different languages.';
|
||||
$string['descriptiontype'] = 'Description type';
|
||||
$string['descriptiontype_help'] = 'There are three possible ways to specify a description.
|
||||
|
||||
@@ -49,15 +49,15 @@ $string['error_defaulticon'] = 'Another file extension with the same MIME type i
|
||||
$string['error_extension'] = 'The file type extension <strong>{$a}</strong> already exists or is invalid. File extensions must be unique and must not contain special characters.';
|
||||
$string['error_notfound'] = 'The file type with extension {$a} cannot be found.';
|
||||
$string['extension'] = 'Extension';
|
||||
$string['extension_help'] = 'File name extension without the dot, e.g. ‘mobi’';
|
||||
$string['extension_help'] = 'File name extension without the dot, e.g. \'mobi\'';
|
||||
$string['groups'] = 'Type groups';
|
||||
$string['groups_help'] = 'Optional list of file type groups that this type belongs to. These are generic categories such as ‘document’ and ‘image’.';
|
||||
$string['groups_help'] = 'Optional list of file type groups that this type belongs to. These are generic categories such as \'document\' and \'image\'.';
|
||||
$string['icon'] = 'File icon';
|
||||
$string['icon_help'] = 'Icon filename.
|
||||
|
||||
The list of icons is taken from the /pix/f directory inside your Moodle installation. You can add custom icons to this folder if required.';
|
||||
$string['mimetype'] = 'MIME type';
|
||||
$string['mimetype_help'] = 'MIME type associated with this file type, e.g. ‘application/x-mobipocket-ebook’';
|
||||
$string['mimetype_help'] = 'MIME type associated with this file type, e.g. \'application/x-mobipocket-ebook\'';
|
||||
$string['pluginname'] = 'File types';
|
||||
$string['revert'] = 'Restore {$a} to Moodle defaults';
|
||||
$string['revert_confirmation'] = 'Are you sure you want to restore <strong>.{$a}</strong> to Moodle defaults, discarding your changes?';
|
||||
|
||||
@@ -40,7 +40,7 @@ if (empty($CFG->langotherroot)) {
|
||||
$mode = optional_param('mode', 0, PARAM_INT); // action
|
||||
$pack = optional_param_array('pack', array(), PARAM_SAFEDIR); // pack to install
|
||||
$uninstalllang = optional_param_array('uninstalllang', array(), PARAM_LANG);// installed pack to uninstall
|
||||
$confirmtounistall = optional_param('confirmtouninstall', '', PARAM_ALPHAEXT); // uninstallation confirmation
|
||||
$confirmtounistall = optional_param('confirmtouninstall', '', PARAM_SAFEPATH); // uninstallation confirmation
|
||||
$purgecaches = optional_param('purgecaches', false, PARAM_BOOL); // explicit caches reset
|
||||
|
||||
if ($purgecaches) {
|
||||
@@ -74,7 +74,7 @@ if ($mode == DELETION_OF_SELECTED_LANG and (!empty($uninstalllang) or !empty($co
|
||||
// Actually deleting languages, languages to delete are passed as GET parameter as string
|
||||
// ...need to populate them to array.
|
||||
if (empty($uninstalllang)) {
|
||||
$uninstalllang = explode('-', $confirmtounistall);
|
||||
$uninstalllang = explode('/', $confirmtounistall);
|
||||
}
|
||||
|
||||
if (in_array('en', $uninstalllang)) {
|
||||
@@ -84,8 +84,10 @@ if ($mode == DELETION_OF_SELECTED_LANG and (!empty($uninstalllang) or !empty($co
|
||||
} else if (empty($confirmtounistall) and confirm_sesskey()) { // User chose langs to be deleted, show confirmation.
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->confirm(get_string('uninstallconfirm', 'tool_langimport', implode(', ', $uninstalllang)),
|
||||
'index.php?mode='.DELETION_OF_SELECTED_LANG.'&confirmtouninstall='.implode('-', $uninstalllang),
|
||||
'index.php');
|
||||
new moodle_url($PAGE->url, array(
|
||||
'mode' => DELETION_OF_SELECTED_LANG,
|
||||
'confirmtouninstall' => implode('/', $uninstalllang),
|
||||
)), $PAGE->url);
|
||||
echo $OUTPUT->footer();
|
||||
die;
|
||||
|
||||
|
||||
@@ -84,8 +84,7 @@ abstract class restore_tool_log_logstore_subplugin extends restore_subplugin {
|
||||
}
|
||||
}
|
||||
|
||||
// Roll dates.
|
||||
$data->timecreated = $this->apply_date_offset($data->timecreated);
|
||||
// There is no need to roll dates. Logs are supposed to be immutable. See MDL-44961.
|
||||
|
||||
// Revert other to its original php way.
|
||||
$data->other = unserialize(base64_decode($data->other));
|
||||
|
||||
@@ -88,6 +88,7 @@ class store implements \tool_log\log\writer, \core\log\sql_reader {
|
||||
$dboptions['dbport'] = $this->get_config('dbport', '');
|
||||
$dboptions['dbschema'] = $this->get_config('dbschema', '');
|
||||
$dboptions['dbcollation'] = $this->get_config('dbcollation', '');
|
||||
$dboptions['dbhandlesoptions'] = $this->get_config('dbhandlesoptions', false);
|
||||
try {
|
||||
$db->connect($this->get_config('dbhost'), $this->get_config('dbuser'), $this->get_config('dbpass'),
|
||||
$this->get_config('dbname'), false, $dboptions);
|
||||
|
||||
@@ -31,6 +31,8 @@ $string['databasesettings_help'] = 'Connection details for the external log data
|
||||
$string['databasepersist'] = 'Persistent database connections';
|
||||
$string['databaseschema'] = 'Database schema';
|
||||
$string['databasecollation'] = 'Database collation';
|
||||
$string['databasehandlesoptions'] = 'Database handles options';
|
||||
$string['databasehandlesoptions_help'] = 'Does the remote database handle its own options.';
|
||||
$string['databasetable'] = 'Database table';
|
||||
$string['databasetable_help'] = 'Name of the table where logs will be stored. This table should have a structure identical to the one used by logstore_standard (mdl_logstore_standard_log).';
|
||||
$string['includeactions'] = 'Include actions of these types';
|
||||
|
||||
@@ -54,6 +54,8 @@ if ($hassiteconfig) {
|
||||
'logstore_database'), '', ''));
|
||||
$settings->add(new admin_setting_configtext('logstore_database/dbcollation', get_string('databasecollation',
|
||||
'logstore_database'), '', ''));
|
||||
$settings->add(new admin_setting_configcheckbox('logstore_database/dbhandlesoptions', get_string('databasehandlesoptions',
|
||||
'logstore_database'), get_string('databasehandlesoptions_help', 'logstore_database'), '0'));
|
||||
$settings->add(new admin_setting_configtext('logstore_database/buffersize', get_string('buffersize',
|
||||
'logstore_database'), get_string('buffersize_help', 'logstore_database'), 50));
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ $dboptions['dbsocket'] = get_config('logstore_database', 'dbsocket');
|
||||
$dboptions['dbport'] = get_config('logstore_database', 'dbport');
|
||||
$dboptions['dbschema'] = get_config('logstore_database', 'dbschema');
|
||||
$dboptions['dbcollation'] = get_config('logstore_database', 'dbcollation');
|
||||
$dboptions['dbhandlesoptions'] = get_config('logstore_database', 'dbhandlesoptions');
|
||||
|
||||
try {
|
||||
$db->connect(get_config('logstore_database', 'dbhost'), get_config('logstore_database', 'dbuser'),
|
||||
|
||||
@@ -83,6 +83,11 @@ class logstore_database_store_testcase extends advanced_testcase {
|
||||
} else {
|
||||
set_config('dbcollation', '', 'logstore_database');
|
||||
}
|
||||
if (!empty($CFG->dboptions['dbhandlesoptions'])) {
|
||||
set_config('dbhandlesoptions', $CFG->dboptions['dbhandlesoptions'], 'logstore_database');
|
||||
} else {
|
||||
set_config('dbhandlesoptions', false, 'logstore_database');
|
||||
}
|
||||
|
||||
// Enable logging plugin.
|
||||
set_config('enabled_stores', 'logstore_database', 'tool_log');
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
This files describes API changes in the logstore_database code.
|
||||
|
||||
=== 3.2.4 ===
|
||||
* PostgreSQL connections now use advanced options to reduce connection overhead. These options are not compatible
|
||||
with some connection poolers. The dbhandlesoptions parameter has been added to allow the database to configure the
|
||||
required defaults. The parameters that are required in the database are;
|
||||
ALTER DATABASE moodle SET client_encoding = UTF8;
|
||||
ALTER DATABASE moodle SET standard_conforming_strings = on;
|
||||
ALTER DATABASE moodle SET search_path = 'moodle,public'; -- Optional, if you wish to use a custom schema.
|
||||
You can set these options against the database or the moodle user who connects.
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2016120500; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2016120501; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2016112900; // Requires this Moodle version.
|
||||
$plugin->component = 'logstore_database'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div data-region="footer" class="pull-xs-right">
|
||||
<input type="button" data-action="rate" value="{{#str}}rate, tool_lp{{/str}}" class="btn btn-primary">
|
||||
<button data-action="rate" class="btn btn-primary">{{#str}}rate, tool_lp{{/str}}</button>
|
||||
<button data-action="cancel" class="btn btn-secondary">{{#str}}cancel{{/str}}</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<div data-region="rule-base" class="form">
|
||||
<div data-region="rule-outcome" class="form-group">
|
||||
<label>{{#str}}outcome, tool_lp{{/str}}</label>
|
||||
<select name="outcome" class="custom-select" ng-label="{{#str}}outcome, tool_lp{{/str}}">
|
||||
<select name="outcome" class="custom-select">
|
||||
{{#outcomes}}
|
||||
<option value="{{code}}" {{#selected}}selected{{/selected}}>{{name}}</option>
|
||||
{{/outcomes}}
|
||||
@@ -68,7 +68,7 @@
|
||||
</div>
|
||||
<div data-region="rule-type" class="form-group">
|
||||
<label>{{#str}}when, tool_lp{{/str}}</label>
|
||||
<select name="rule" class="custom-select" ng-label="{{#str}}when, tool_lp{{/str}}">
|
||||
<select name="rule" class="custom-select">
|
||||
<option value="-1">{{#str}}choosedots{{/str}}</option>
|
||||
{{#rules}}
|
||||
<option value="{{type}}" {{#selected}}selected{{/selected}}>{{name}}</option>
|
||||
@@ -81,10 +81,11 @@
|
||||
{{/config}}
|
||||
</div>
|
||||
|
||||
<div data-region="footer" class="pull-xs-right">
|
||||
<div data-region="footer" class="pull-xs-right m-t-1">
|
||||
{{#config}}
|
||||
<input type="button" class="btn btn-primary" data-action="save" value="{{#str}}savechanges{{/str}}"/>
|
||||
{{/config}}
|
||||
<input type="button" class="btn btn-secondary" data-action="cancel" value="{{#str}}cancel{{/str}}"/>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
@@ -1012,7 +1012,7 @@ class manager {
|
||||
$messageparams = new \stdClass();
|
||||
$messageparams->html = $message->html;
|
||||
$messageparams->plain = $message->plain;
|
||||
$messagepreferencesurl = new \moodle_url("/message/edit.php", array('id' => $USER->id));
|
||||
$messagepreferencesurl = new \moodle_url("/message/notificationpreferences.php", array('id' => $USER->id));
|
||||
$messageparams->messagepreferencesurl = $messagepreferencesurl->out();
|
||||
$htmlmessage = get_string('messageprocessingsuccesshtml', 'tool_messageinbound', $messageparams);
|
||||
$plainmessage = get_string('messageprocessingsuccess', 'tool_messageinbound', $messageparams);
|
||||
|
||||
@@ -57,6 +57,13 @@ class api {
|
||||
global $CFG;
|
||||
require_once($CFG->libdir . '/adminlib.php');
|
||||
|
||||
// Check if we can return this from cache.
|
||||
$cache = \cache::make('tool_mobile', 'plugininfo');
|
||||
$pluginsinfo = $cache->get('mobileplugins');
|
||||
if ($pluginsinfo !== false) {
|
||||
return (array)$pluginsinfo;
|
||||
}
|
||||
|
||||
$pluginsinfo = [];
|
||||
$plugintypes = core_component::get_plugin_types();
|
||||
|
||||
@@ -68,7 +75,7 @@ class api {
|
||||
$component = $plugintype . '_' . $plugin;
|
||||
$version = get_component_version($component);
|
||||
|
||||
require_once("$path/db/mobile.php");
|
||||
require("$path/db/mobile.php");
|
||||
foreach ($addons as $addonname => $addoninfo) {
|
||||
$plugininfo = array(
|
||||
'component' => $component,
|
||||
@@ -81,7 +88,7 @@ class api {
|
||||
);
|
||||
|
||||
// All the mobile packages must be under the plugin mobile directory.
|
||||
$package = $path . DIRECTORY_SEPARATOR . 'mobile' . DIRECTORY_SEPARATOR . $addonname . '.zip';
|
||||
$package = $path . '/mobile/' . $addonname . '.zip';
|
||||
if (file_exists($package)) {
|
||||
$plugininfo['fileurl'] = $CFG->wwwroot . '' . str_replace($CFG->dirroot, '', $package);
|
||||
$plugininfo['filehash'] = sha1_file($package);
|
||||
@@ -91,6 +98,9 @@ class api {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cache->set('mobileplugins', $pluginsinfo);
|
||||
|
||||
return $pluginsinfo;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Mobile cache definitions.
|
||||
*
|
||||
* @package tool_mobile
|
||||
* @copyright 2017 Skylar Kelty <S.Kelty@kent.ac.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$definitions = array(
|
||||
'plugininfo' => array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'simplekeys' => true,
|
||||
'staticacceleration' => true,
|
||||
'staticaccelerationsize' => 1
|
||||
)
|
||||
);
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
$string['autologinkeygenerationlockout'] = 'Auto-login key generation is blocked because of too many requests within an hour.';
|
||||
$string['autologinnotallowedtoadmins'] = 'Auto-login is not allowed for site admins.';
|
||||
$string['cachedef_plugininfo'] = 'This stores the list of plugins with mobile addons';
|
||||
$string['clickheretolaunchtheapp'] = 'Click here if the app does not open automatically.';
|
||||
$string['configmobilecssurl'] = 'A CSS file to customise your mobile app interface.';
|
||||
$string['enablesmartappbanners'] = 'Enable Smart App Banners';
|
||||
|
||||
@@ -70,6 +70,9 @@ class api {
|
||||
// Look at all the templates dirs for subsystems.
|
||||
$subsystems = core_component::get_core_subsystems();
|
||||
foreach ($subsystems as $subsystem => $dir) {
|
||||
if (empty($dir)) {
|
||||
continue;
|
||||
}
|
||||
$dir .= '/templates';
|
||||
if (is_dir($dir)) {
|
||||
$dirs = mustache_template_finder::get_template_directories_for_component('core_' . $subsystem, $themename);
|
||||
|
||||
@@ -49,7 +49,7 @@ It\'s highly recommended to be running the latest (+ version) available of your
|
||||
This functionality doesn\'t perform any action against the DB (just reads from it), so can be safely executed at any moment.';
|
||||
$string['confirmcheckforeignkeys'] = 'This functionality will search for potential violations of the foreign keys defined in the install.xml definitions. (Moodle does not currently generate actual foreign key constraints in the database, which is why invalid data may be present.)
|
||||
|
||||
It\'s highly recommended to be running the latest (+ version) available of your Moodle release before executing the search of missing indexes.
|
||||
It\'s highly recommended to be running the latest (+ version) available of your Moodle release before executing the search for potential violations of the foreign keys.
|
||||
|
||||
This functionality doesn\'t perform any action against the DB (just reads from it), so can be safely executed at any moment.';
|
||||
$string['confirmcheckindexes'] = 'This functionality will search for potential missing indexes in your Moodle server, generating (but not executing!) automatically the needed SQL statements to keep everything updated.
|
||||
|
||||
+12
-12
@@ -3187,6 +3187,18 @@ class CAS_Client
|
||||
false/*$no_response*/, true/*$bad_response*/, $text_response
|
||||
);
|
||||
$result = false;
|
||||
} else if ( $tree_response->getElementsByTagName("authenticationFailure")->length != 0) {
|
||||
// authentication failed, extract the error code and message and throw exception
|
||||
$auth_fail_list = $tree_response
|
||||
->getElementsByTagName("authenticationFailure");
|
||||
throw new CAS_AuthenticationException(
|
||||
$this, 'Ticket not validated', $validate_url,
|
||||
false/*$no_response*/, false/*$bad_response*/,
|
||||
$text_response,
|
||||
$auth_fail_list->item(0)->getAttribute('code')/*$err_code*/,
|
||||
trim($auth_fail_list->item(0)->nodeValue)/*$err_msg*/
|
||||
);
|
||||
$result = false;
|
||||
} else if ($tree_response->getElementsByTagName("authenticationSuccess")->length != 0) {
|
||||
// authentication succeded, extract the user name
|
||||
$success_elements = $tree_response
|
||||
@@ -3227,18 +3239,6 @@ class CAS_Client
|
||||
$result = true;
|
||||
}
|
||||
}
|
||||
} else if ( $tree_response->getElementsByTagName("authenticationFailure")->length != 0) {
|
||||
// authentication succeded, extract the error code and message
|
||||
$auth_fail_list = $tree_response
|
||||
->getElementsByTagName("authenticationFailure");
|
||||
throw new CAS_AuthenticationException(
|
||||
$this, 'Ticket not validated', $validate_url,
|
||||
false/*$no_response*/, false/*$bad_response*/,
|
||||
$text_response,
|
||||
$auth_fail_list->item(0)->getAttribute('code')/*$err_code*/,
|
||||
trim($auth_fail_list->item(0)->nodeValue)/*$err_msg*/
|
||||
);
|
||||
$result = false;
|
||||
} else {
|
||||
throw new CAS_AuthenticationException(
|
||||
$this, 'Ticket not validated', $validate_url,
|
||||
|
||||
@@ -2,3 +2,4 @@ Description of phpCAS 1.3.4 library import
|
||||
|
||||
* downloaded from http://downloads.jasig.org/cas-clients/php/current/
|
||||
|
||||
* MDL-59456 phpCAS library has been patched because of an authentication bypass security vulnerability.
|
||||
@@ -143,7 +143,7 @@ class login implements renderable, templatable {
|
||||
$data->error = $this->error;
|
||||
$data->forgotpasswordurl = $this->forgotpasswordurl->out(false);
|
||||
$data->hasidentityproviders = !empty($this->identityproviders);
|
||||
$data->hasinstructions = !empty($this->instructions);
|
||||
$data->hasinstructions = !empty($this->instructions) || $this->cansignup;
|
||||
$data->identityproviders = $identityproviders;
|
||||
list($data->instructions, $data->instructionsformat) = external_format_text($this->instructions, FORMAT_MOODLE,
|
||||
context_system::instance()->id);
|
||||
|
||||
+9
-9
@@ -276,7 +276,7 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
}
|
||||
$ldapval = NULL;
|
||||
foreach ($values as $value) {
|
||||
$entry = array_change_key_case($user_entry[0], CASE_LOWER);
|
||||
$entry = $user_entry[0];
|
||||
if (($value == 'dn') || ($value == 'distinguishedname')) {
|
||||
$result[$key] = $user_dn;
|
||||
continue;
|
||||
@@ -634,7 +634,7 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
if ($sr) {
|
||||
$info = ldap_get_entries_moodle($ldapconnection, $sr);
|
||||
if (!empty ($info)) {
|
||||
$info = array_change_key_case($info[0], CASE_LOWER);
|
||||
$info = $info[0];
|
||||
if (isset($info[$this->config->expireattr][0])) {
|
||||
$expiretime = $this->ldap_expirationtime2unix($info[$this->config->expireattr][0], $ldapconnection, $user_dn);
|
||||
if ($expiretime != 0) {
|
||||
@@ -1201,7 +1201,7 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
return false;
|
||||
}
|
||||
|
||||
$user_entry = array_change_key_case($user_entry[0], CASE_LOWER);
|
||||
$user_entry = $user_entry[0];
|
||||
|
||||
foreach ($attrmap as $key => $ldapkeys) {
|
||||
$profilefield = '';
|
||||
@@ -1369,7 +1369,7 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
$sr = ldap_read($ldapconnection, $user_dn, '(objectClass=*)', $search_attribs);
|
||||
if ($sr) {
|
||||
$entry = ldap_get_entries_moodle($ldapconnection, $sr);
|
||||
$info = array_change_key_case($entry[0], CASE_LOWER);
|
||||
$info = $entry[0];
|
||||
$newattrs = array();
|
||||
if (!empty($info[$this->config->expireattr][0])) {
|
||||
// Set expiration time only if passwordExpirationInterval is defined
|
||||
@@ -2024,7 +2024,7 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
}
|
||||
|
||||
$entry = ldap_get_entries_moodle($ldapconn, $sr);
|
||||
$info = array_change_key_case($entry[0], CASE_LOWER);
|
||||
$info = $entry[0];
|
||||
$useraccountcontrol = $info['useraccountcontrol'][0];
|
||||
if ($useraccountcontrol & UF_DONT_EXPIRE_PASSWD) {
|
||||
// Password doesn't expire.
|
||||
@@ -2069,17 +2069,17 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
}
|
||||
|
||||
$entry = ldap_get_entries_moodle($ldapconn, $sr);
|
||||
$info = array_change_key_case($entry[0], CASE_LOWER);
|
||||
$info = $entry[0];
|
||||
$domaindn = $info['defaultnamingcontext'][0];
|
||||
|
||||
$sr = ldap_read ($ldapconn, $domaindn, '(objectClass=*)',
|
||||
array('maxPwdAge'));
|
||||
$entry = ldap_get_entries_moodle($ldapconn, $sr);
|
||||
$info = array_change_key_case($entry[0], CASE_LOWER);
|
||||
$info = $entry[0];
|
||||
$maxpwdage = $info['maxpwdage'][0];
|
||||
if ($sr = ldap_read($ldapconn, $user_dn, '(objectClass=*)', array('msDS-ResultantPSO'))) {
|
||||
if ($entry = ldap_get_entries_moodle($ldapconn, $sr)) {
|
||||
$info = array_change_key_case($entry[0], CASE_LOWER);
|
||||
$info = $entry[0];
|
||||
$userpso = $info['msds-resultantpso'][0];
|
||||
|
||||
// If a PSO exists, FGPP is being utilized.
|
||||
@@ -2087,7 +2087,7 @@ class auth_plugin_ldap extends auth_plugin_base {
|
||||
if (!empty($userpso)) {
|
||||
$sr = ldap_read($ldapconn, $userpso, '(objectClass=*)', array('msDS-MaximumPasswordAge'));
|
||||
if ($entry = ldap_get_entries_moodle($ldapconn, $sr)) {
|
||||
$info = array_change_key_case($entry[0], CASE_LOWER);
|
||||
$info = $entry[0];
|
||||
// Default value of msds-maximumpasswordage is 42 and is always set.
|
||||
$maxpwdage = $info['msds-maximumpasswordage'][0];
|
||||
}
|
||||
|
||||
@@ -26,13 +26,14 @@ if (!is_enabled_auth('shibboleth')) {
|
||||
$inputstream = file_get_contents("php://input");
|
||||
if ($action == 'logout' && !empty($redirect)) {
|
||||
|
||||
if ($USER->auth == 'shibboleth') {
|
||||
// Logout out user from application.
|
||||
if (isloggedin($USER) && $USER->auth == 'shibboleth') {
|
||||
// Logout user from application.
|
||||
require_logout();
|
||||
// Finally, send user to the return URL.
|
||||
redirect($redirect);
|
||||
}
|
||||
|
||||
// Finally, send user to the return URL.
|
||||
redirect($redirect);
|
||||
|
||||
} else if (!empty($inputstream)) {
|
||||
|
||||
// Back channel logout.
|
||||
|
||||
@@ -15,6 +15,17 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This script is used to configure and execute the backup proccess.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage backup
|
||||
* @copyright Moodle
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
require_once('../config.php');
|
||||
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_plan_builder.class.php');
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This script is used to configure and execute the import proccess.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage backup
|
||||
* @copyright Moodle
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
// Require both the backup and restore libs
|
||||
require_once('../config.php');
|
||||
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
|
||||
|
||||
@@ -869,6 +869,14 @@ class backup_calendarevents_structure_step extends backup_structure_step {
|
||||
AND (eventtype = 'course' OR eventtype = 'group')";
|
||||
$calendar_items_params = array('courseid'=>backup::VAR_COURSEID);
|
||||
$event->set_source_sql($calendar_items_sql, $calendar_items_params);
|
||||
} else if ($this->name == 'activity_calendar') {
|
||||
$params = array('instance' => backup::VAR_ACTIVITYID, 'modulename' => backup::VAR_MODNAME);
|
||||
// If we don't want to include the userinfo in the backup then setting the courseid
|
||||
// will filter out all of the user override events (which have a course id of zero).
|
||||
if (!$this->get_setting_value('userinfo')) {
|
||||
$params['courseid'] = backup::VAR_COURSEID;
|
||||
}
|
||||
$event->set_source_table('event', $params);
|
||||
} else {
|
||||
$event->set_source_table('event', array('courseid' => backup::VAR_COURSEID, 'instance' => backup::VAR_ACTIVITYID, 'modulename' => backup::VAR_MODNAME));
|
||||
}
|
||||
|
||||
@@ -2632,6 +2632,17 @@ class restore_calendarevents_structure_step extends restore_structure_step {
|
||||
$data = (object)$data;
|
||||
$oldid = $data->id;
|
||||
$restorefiles = true; // We'll restore the files
|
||||
// User overrides for activities are identified by having a courseid of zero with
|
||||
// both a modulename and instance value set.
|
||||
$isuseroverride = !$data->courseid && $data->modulename && $data->instance;
|
||||
|
||||
// If we don't want to include user data and this record is a user override event
|
||||
// for an activity then we should not create it. (Only activity events can be user override events - which must have this
|
||||
// setting).
|
||||
if ($isuseroverride && $this->task->setting_exists('userinfo') && !$this->task->get_setting_value('userinfo')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the userid and the groupid associated with the event.
|
||||
$data->userid = $this->get_mappingid('user', $data->userid);
|
||||
if ($data->userid === false) {
|
||||
@@ -2667,7 +2678,9 @@ class restore_calendarevents_structure_step extends restore_structure_step {
|
||||
'name' => $data->name,
|
||||
'description' => $data->description,
|
||||
'format' => $data->format,
|
||||
'courseid' => $this->get_courseid(),
|
||||
// User overrides in activities use a course id of zero. All other event types
|
||||
// must use the mapped course id.
|
||||
'courseid' => $data->courseid ? $this->get_courseid() : 0,
|
||||
'groupid' => $data->groupid,
|
||||
'userid' => $data->userid,
|
||||
'repeatid' => $this->get_mappingid('event', $data->repeatid),
|
||||
@@ -2689,10 +2702,11 @@ class restore_calendarevents_structure_step extends restore_structure_step {
|
||||
WHERE " . $DB->sql_compare_text('name', 255) . " = " . $DB->sql_compare_text('?', 255) . "
|
||||
AND courseid = ?
|
||||
AND modulename = ?
|
||||
AND instance = ?
|
||||
AND timestart = ?
|
||||
AND timeduration = ?
|
||||
AND " . $DB->sql_compare_text('description', 255) . " = " . $DB->sql_compare_text('?', 255);
|
||||
$arg = array ($params['name'], $params['courseid'], $params['modulename'], $params['timestart'], $params['timeduration'], $params['description']);
|
||||
$arg = array ($params['name'], $params['courseid'], $params['modulename'], $params['instance'], $params['timestart'], $params['timeduration'], $params['description']);
|
||||
$result = $DB->record_exists_sql($sql, $arg);
|
||||
if (empty($result)) {
|
||||
$newitemid = $DB->insert_record('event', $params);
|
||||
@@ -3045,7 +3059,8 @@ class restore_course_logs_structure_step extends restore_structure_step {
|
||||
|
||||
$data = (object)($data);
|
||||
|
||||
$data->time = $this->apply_date_offset($data->time);
|
||||
// There is no need to roll dates. Logs are supposed to be immutable. See MDL-44961.
|
||||
|
||||
$data->userid = $this->get_mappingid('user', $data->userid);
|
||||
$data->course = $this->get_courseid();
|
||||
$data->cmid = 0;
|
||||
@@ -3092,7 +3107,8 @@ class restore_activity_logs_structure_step extends restore_course_logs_structure
|
||||
|
||||
$data = (object)($data);
|
||||
|
||||
$data->time = $this->apply_date_offset($data->time);
|
||||
// There is no need to roll dates. Logs are supposed to be immutable. See MDL-44961.
|
||||
|
||||
$data->userid = $this->get_mappingid('user', $data->userid);
|
||||
$data->course = $this->get_courseid();
|
||||
$data->cmid = $this->task->get_moduleid();
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Tests for backup_xml_transformer class.
|
||||
*
|
||||
* @package core_backup
|
||||
* @subpackage moodle2
|
||||
* @category backup
|
||||
* @copyright 2017 Dmitrii Metelkin (dmitriim@catalyst-au.net)
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_plan_builder.class.php');
|
||||
|
||||
/**
|
||||
* Tests for backup_xml_transformer.
|
||||
*
|
||||
* @package core_backup
|
||||
* @copyright 2017 Dmitrii Metelkin (dmitriim@catalyst-au.net)
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class backup_xml_transformer_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Initial set up.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for ::test_filephp_links_replace.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filephp_links_replace_data_provider() {
|
||||
return array(
|
||||
array('http://test.test/', 'http://test.test/'),
|
||||
array('http://test.test/file.php/1', 'http://test.test/file.php/1'),
|
||||
array('http://test.test/file.php/2/1.jpg', 'http://test.test/file.php/2/1.jpg'),
|
||||
array('http://test.test/file.php/2', 'http://test.test/file.php/2'),
|
||||
array('http://test.test/file.php/1/1.jpg', '$@FILEPHP@$$@SLASH@$1.jpg'),
|
||||
array('http://test.test/file.php/1//1.jpg', '$@FILEPHP@$$@SLASH@$$@SLASH@$1.jpg'),
|
||||
array('http://test.test/file.php?file=/1', '$@FILEPHP@$'),
|
||||
array('http://test.test/file.php?file=/2/1.jpg', 'http://test.test/file.php?file=/2/1.jpg'),
|
||||
array('http://test.test/file.php?file=/2', 'http://test.test/file.php?file=/2'),
|
||||
array('http://test.test/file.php?file=/1/1.jpg', '$@FILEPHP@$$@SLASH@$1.jpg'),
|
||||
array('http://test.test/file.php?file=/1//1.jpg', '$@FILEPHP@$$@SLASH@$$@SLASH@$1.jpg'),
|
||||
array('http://test.test/file.php?file=%2f1', '$@FILEPHP@$'),
|
||||
array('http://test.test/file.php?file=%2f2%2f1.jpg', 'http://test.test/file.php?file=%2f2%2f1.jpg'),
|
||||
array('http://test.test/file.php?file=%2f2', 'http://test.test/file.php?file=%2f2'),
|
||||
array('http://test.test/file.php?file=%2f1%2f1.jpg', '$@FILEPHP@$$@SLASH@$1.jpg'),
|
||||
array('http://test.test/file.php?file=%2f1%2f%2f1.jpg', '$@FILEPHP@$$@SLASH@$$@SLASH@$1.jpg'),
|
||||
array('http://test.test/file.php?file=%2F1', '$@FILEPHP@$'),
|
||||
array('http://test.test/file.php?file=%2F2%2F1.jpg', 'http://test.test/file.php?file=%2F2%2F1.jpg'),
|
||||
array('http://test.test/file.php?file=%2F2', 'http://test.test/file.php?file=%2F2'),
|
||||
array('http://test.test/file.php?file=%2F1%2F1.jpg', '$@FILEPHP@$$@SLASH@$1.jpg'),
|
||||
array('http://test.test/file.php?file=%2F1%2F%2F1.jpg', '$@FILEPHP@$$@SLASH@$$@SLASH@$1.jpg'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that backup_xml_transformer replaces file php links to $@FILEPHP@$.
|
||||
*
|
||||
* @dataProvider filephp_links_replace_data_provider
|
||||
* @param string $content Testing content.
|
||||
* @param string $expected Expected result.
|
||||
*/
|
||||
public function test_filephp_links_replace($content, $expected) {
|
||||
global $CFG;
|
||||
|
||||
$CFG->wwwroot = 'http://test.test';
|
||||
|
||||
$transformer = new backup_xml_transformer(1);
|
||||
|
||||
$this->assertEquals($expected, $transformer->process($content));
|
||||
}
|
||||
|
||||
}
|
||||
+25
-1
@@ -1,5 +1,29 @@
|
||||
<?php
|
||||
//This script is used to configure and execute the restore proccess.
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This script is used to configure and execute the restore proccess.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage backup
|
||||
* @copyright Moodle
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
require_once('../config.php');
|
||||
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
|
||||
|
||||
@@ -60,17 +60,26 @@ class restore_structure_parser_processor extends grouped_parser_processor {
|
||||
} else if (strpos($cdata, '$@FILEPHP@$') === false) { // No $@FILEPHP@$, nothing to convert
|
||||
return $cdata;
|
||||
}
|
||||
|
||||
if ($CFG->slasharguments) {
|
||||
$slash = '/';
|
||||
$forcedownload = '?forcedownload=1';
|
||||
} else {
|
||||
$slash = '%2F';
|
||||
$forcedownload = '&forcedownload=1';
|
||||
}
|
||||
|
||||
// We have to remove trailing slashes, otherwise file URLs will be restored with an extra slash.
|
||||
$basefileurl = rtrim(moodle_url::make_legacyfile_url($this->courseid, null)->out(true), $slash);
|
||||
// Decode file.php calls
|
||||
$search = array ("$@FILEPHP@$");
|
||||
$replace = array(moodle_url::make_legacyfile_url($this->courseid, null));
|
||||
$replace = array($basefileurl);
|
||||
$result = str_replace($search, $replace, $cdata);
|
||||
|
||||
// Now $@SLASH@$ and $@FORCEDOWNLOAD@$ MDL-18799
|
||||
$search = array('$@SLASH@$', '$@FORCEDOWNLOAD@$');
|
||||
if ($CFG->slasharguments) {
|
||||
$replace = array('/', '?forcedownload=1');
|
||||
} else {
|
||||
$replace = array('%2F', '&forcedownload=1');
|
||||
}
|
||||
$replace = array($slash, $forcedownload);
|
||||
|
||||
return str_replace($search, $replace, $result);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Tests for restore_structure_parser_processor class.
|
||||
*
|
||||
* @package core_backup
|
||||
* @category test
|
||||
* @copyright 2017 Dmitrii Metelkin (dmitriim@catalyst-au.net)
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
|
||||
require_once($CFG->dirroot . '/backup/util/helper/restore_structure_parser_processor.class.php');
|
||||
|
||||
/**
|
||||
* Tests for restore_structure_parser_processor class.
|
||||
*
|
||||
* @package core_backup
|
||||
* @copyright 2017 Dmitrii Metelkin (dmitriim@catalyst-au.net)
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class restore_structure_parser_processor_test extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Initial set up.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for ::test_process_cdata.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function process_cdata_data_provider() {
|
||||
return array(
|
||||
array(null, null, true),
|
||||
array("$@NULL@$", null, true),
|
||||
array("$@NULL@$ ", "$@NULL@$ ", true),
|
||||
array(1, 1, true),
|
||||
array(" ", " ", true),
|
||||
array("1", "1", true),
|
||||
array("$@FILEPHP@$1.jpg", "$@FILEPHP@$1.jpg", true),
|
||||
array(
|
||||
"http://test.test/$@SLASH@$",
|
||||
"http://test.test/$@SLASH@$",
|
||||
true
|
||||
),
|
||||
array(
|
||||
"<a href='$@FILEPHP@$1.jpg'>Image</a>",
|
||||
"<a href='http://test.test/file.php/11.jpg'>Image</a>",
|
||||
true
|
||||
),
|
||||
array(
|
||||
"<a href='$@FILEPHP@$$@SLASH@$1.jpg'>Image</a>",
|
||||
"<a href='http://test.test/file.php/1/1.jpg'>Image</a>",
|
||||
true
|
||||
),
|
||||
array(
|
||||
"<a href='$@FILEPHP@$$@SLASH@$$@SLASH@$1.jpg'>Image</a>",
|
||||
"<a href='http://test.test/file.php/1//1.jpg'>Image</a>",
|
||||
true
|
||||
),
|
||||
array(
|
||||
"<a href='$@FILEPHP@$1.jpg'>Image</a>",
|
||||
"<a href='http://test.test/file.php?file=%2F11.jpg'>Image</a>",
|
||||
false
|
||||
),
|
||||
array(
|
||||
"<a href='$@FILEPHP@$$@SLASH@$1.jpg'>Image</a>",
|
||||
"<a href='http://test.test/file.php?file=%2F1%2F1.jpg'>Image</a>",
|
||||
false
|
||||
),
|
||||
array(
|
||||
"<a href='$@FILEPHP@$$@SLASH@$$@SLASH@$1.jpg'>Image</a>",
|
||||
"<a href='http://test.test/file.php?file=%2F1%2F%2F1.jpg'>Image</a>",
|
||||
false
|
||||
),
|
||||
array(
|
||||
"<a href='$@FILEPHP@$$@SLASH@$1.jpg$@FORCEDOWNLOAD@$'>Image</a>",
|
||||
"<a href='http://test.test/file.php/1/1.jpg?forcedownload=1'>Image</a>",
|
||||
true
|
||||
),
|
||||
array(
|
||||
"<a href='$@FILEPHP@$$@SLASH@$1.jpg$@FORCEDOWNLOAD@$'>Image</a>",
|
||||
"<a href='http://test.test/file.php?file=%2F1%2F1.jpg&forcedownload=1'>Image</a>",
|
||||
false
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that restore_structure_parser_processor replaces $@FILEPHP@$ to correct file php links.
|
||||
*
|
||||
* @dataProvider process_cdata_data_provider
|
||||
* @param string $content Testing content.
|
||||
* @param string $expected Expected result.
|
||||
* @param bool $slasharguments A value for $CFG->slasharguments setting.
|
||||
*/
|
||||
public function test_process_cdata($content, $expected, $slasharguments) {
|
||||
global $CFG;
|
||||
|
||||
$CFG->slasharguments = $slasharguments;
|
||||
$CFG->wwwroot = 'http://test.test';
|
||||
|
||||
$processor = new restore_structure_parser_processor(1, 1);
|
||||
|
||||
$this->assertEquals($expected, $processor->process_cdata($content));
|
||||
}
|
||||
|
||||
}
|
||||
+42
-25
@@ -46,23 +46,6 @@ class edit_backpack_form extends moodleform {
|
||||
$mform->addElement('header', 'backpackheader', get_string('backpackconnection', 'badges'));
|
||||
$mform->addHelpButton('backpackheader', 'backpackconnection', 'badges');
|
||||
$mform->addElement('static', 'url', get_string('url'), BADGE_BACKPACKURL);
|
||||
$status = html_writer::tag('span', get_string('notconnected', 'badges'),
|
||||
array('class' => 'notconnected', 'id' => 'connection-status'));
|
||||
$mform->addElement('static', 'status', get_string('status'), $status);
|
||||
|
||||
$nojs = html_writer::tag('noscript', get_string('error:personaneedsjs', 'badges'),
|
||||
array('class' => 'notconnected'));
|
||||
$personadiv = $OUTPUT->container($nojs, null, 'persona-container');
|
||||
|
||||
$mform->addElement('static', 'persona', '', $personadiv);
|
||||
$mform->addHelpButton('persona', 'personaconnection', 'badges');
|
||||
|
||||
$PAGE->requires->js(new moodle_url('https://login.persona.org/include.js'));
|
||||
$PAGE->requires->js('/badges/backpack.js');
|
||||
$PAGE->requires->js_init_call('badges_init_persona_login_button', null, false);
|
||||
$PAGE->requires->strings_for_js(array('error:backpackloginfailed', 'signinwithyouremail',
|
||||
'error:noassertion', 'error:connectionunknownreason', 'error:badjson', 'connecting',
|
||||
'notconnected'), 'badges');
|
||||
|
||||
$mform->addElement('hidden', 'userid', $USER->id);
|
||||
$mform->setType('userid', PARAM_INT);
|
||||
@@ -70,23 +53,57 @@ class edit_backpack_form extends moodleform {
|
||||
$mform->addElement('hidden', 'backpackurl', BADGE_BACKPACKURL);
|
||||
$mform->setType('backpackurl', PARAM_URL);
|
||||
|
||||
if (isset($this->_customdata['email'])) {
|
||||
// Email will be passed in when we're in the process of verifying the user's email address,
|
||||
// so set the connection status, lock the email field, and provide options to resend the verification
|
||||
// email or cancel the verification process entirely and start over.
|
||||
$status = html_writer::tag('span', get_string('backpackemailverificationpending', 'badges'),
|
||||
array('class' => 'notconnected', 'id' => 'connection-status'));
|
||||
$mform->addElement('static', 'status', get_string('status'), $status);
|
||||
$mform->addElement('hidden', 'email', $this->_customdata['email']);
|
||||
$mform->setType('email', PARAM_RAW_TRIMMED);
|
||||
$mform->hardFreeze(['email']);
|
||||
$status = html_writer::tag('span', $this->_customdata['email'], []);
|
||||
$mform->addElement('static', 'emailverify', get_string('email'), $status);
|
||||
$buttonarray = [];
|
||||
$buttonarray[] = &$mform->createElement('submit', 'submitbutton',
|
||||
get_string('backpackconnectionresendemail', 'badges'));
|
||||
$buttonarray[] = &$mform->createElement('submit', 'revertbutton',
|
||||
get_string('backpackconnectioncancelattempt', 'badges'));
|
||||
$mform->addGroup($buttonarray, 'buttonar', '', [''], false);
|
||||
$mform->closeHeaderBefore('buttonar');
|
||||
} else {
|
||||
// Email isn't present, so provide an input element to get it and a button to start the verification process.
|
||||
$status = html_writer::tag('span', get_string('notconnected', 'badges'),
|
||||
array('class' => 'notconnected', 'id' => 'connection-status'));
|
||||
$mform->addElement('static', 'status', get_string('status'), $status);
|
||||
$mform->addElement('text', 'email', get_string('email'), 'maxlength="100" size="30"');
|
||||
$mform->addHelpButton('email', 'backpackemail', 'badges');
|
||||
$mform->addRule('email', get_string('required'), 'required', null, 'client');
|
||||
$mform->setType('email', PARAM_RAW_TRIMMED);
|
||||
$this->add_action_buttons(false, get_string('backpackconnectionconnect', 'badges'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates form data
|
||||
*/
|
||||
public function validation($data, $files) {
|
||||
global $DB;
|
||||
$errors = parent::validation($data, $files);
|
||||
|
||||
$check = new stdClass();
|
||||
$check->backpackurl = $data['backpackurl'];
|
||||
$check->email = $data['email'];
|
||||
// We don't need to verify the email address if we're clearing a pending email verification attempt.
|
||||
if (!isset($data['revertbutton'])) {
|
||||
$check = new stdClass();
|
||||
$check->backpackurl = $data['backpackurl'];
|
||||
$check->email = $data['email'];
|
||||
|
||||
$bp = new OpenBadgesBackpackHandler($check);
|
||||
$request = $bp->curl_request('user');
|
||||
if (isset($request->status) && $request->status == 'missing') {
|
||||
$errors['email'] = get_string('error:nosuchuser', 'badges');
|
||||
$bp = new OpenBadgesBackpackHandler($check);
|
||||
$request = $bp->curl_request('user');
|
||||
if (isset($request->status) && $request->status == 'missing') {
|
||||
$errors['email'] = get_string('error:nosuchuser', 'badges');
|
||||
} else if (!isset($request->status) || $request->status !== 'okay') {
|
||||
$errors['email'] = get_string('backpackconnectionunexpectedresult', 'badges');
|
||||
}
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Endpoint for the verification email link.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage badges
|
||||
* @copyright 2016 Jake Dallimore <jrhdallimore@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
require_once(__DIR__ . '/../config.php');
|
||||
require_once($CFG->libdir . '/badgeslib.php');
|
||||
require_once(__DIR__ . '/lib/backpacklib.php');
|
||||
|
||||
$data = optional_param('data', '', PARAM_RAW);
|
||||
require_login();
|
||||
$PAGE->set_url('/badges/openbackpackemailverify.php');
|
||||
$PAGE->set_context(context_user::instance($USER->id));
|
||||
$redirect = '/badges/mybackpack.php';
|
||||
|
||||
// Confirm the secret and create the backpack connection.
|
||||
$storedsecret = get_user_preferences('badges_email_verify_secret');
|
||||
if (!is_null($storedsecret)) {
|
||||
if ($data === $storedsecret) {
|
||||
$storedemail = get_user_preferences('badges_email_verify_address');
|
||||
|
||||
$data = new stdClass();
|
||||
$data->backpackurl = BADGE_BACKPACKURL;
|
||||
$data->email = $storedemail;
|
||||
$bp = new OpenBadgesBackpackHandler($data);
|
||||
|
||||
// Make sure we have all the required information before trying to save the connection.
|
||||
$backpackuser = $bp->curl_request('user');
|
||||
if (isset($backpackuser->status) && $backpackuser->status === 'okay' && isset($backpackuser->userId)) {
|
||||
$backpackuid = $backpackuser->userId;
|
||||
} else {
|
||||
redirect(new moodle_url($redirect), get_string('backpackconnectionunexpectedresult', 'badges'),
|
||||
null, \core\output\notification::NOTIFY_ERROR);
|
||||
}
|
||||
|
||||
$obj = new stdClass();
|
||||
$obj->userid = $USER->id;
|
||||
$obj->email = $data->email;
|
||||
$obj->backpackurl = $data->backpackurl;
|
||||
$obj->backpackuid = $backpackuid;
|
||||
$obj->autosync = 0;
|
||||
$obj->password = '';
|
||||
$DB->insert_record('badge_backpack', $obj);
|
||||
|
||||
// Remove the verification vars and redirect to the mypackpack page.
|
||||
unset_user_preference('badges_email_verify_secret');
|
||||
unset_user_preference('badges_email_verify_address');
|
||||
redirect(new moodle_url($redirect), get_string('backpackemailverifysuccess', 'badges'),
|
||||
null, \core\output\notification::NOTIFY_SUCCESS);
|
||||
} else {
|
||||
// Stored secret doesn't match the supplied secret. Take user back to the mybackpack page and present a warning message.
|
||||
redirect(new moodle_url($redirect), get_string('backpackemailverifytokenmismatch', 'badges'),
|
||||
null, \core\output\notification::NOTIFY_ERROR);
|
||||
}
|
||||
} else {
|
||||
// Stored secret is null. Either the email address has already been verified, or there is no record of a verification attempt
|
||||
// for the current user. Either way, just redirect to the mybackpack page.
|
||||
redirect(new moodle_url($redirect));
|
||||
}
|
||||
@@ -117,3 +117,45 @@ class OpenBadgesBackpackHandler {
|
||||
return $this->backpack;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and send a verification email to the email address supplied.
|
||||
*
|
||||
* Since we're not sending this email to a user, email_to_user can't be used
|
||||
* but this function borrows largely the code from that process.
|
||||
*
|
||||
* @param string $email the email address to send the verification email to.
|
||||
* @return true if the email was sent successfully, false otherwise.
|
||||
*/
|
||||
function send_verification_email($email) {
|
||||
global $DB, $USER;
|
||||
|
||||
// Store a user secret (badges_email_verify_secret) and the address (badges_email_verify_address) as users prefs.
|
||||
// The address will be used by edit_backpack_form for display during verification and to facilitate the resending
|
||||
// of verification emails to said address.
|
||||
$secret = random_string(15);
|
||||
set_user_preference('badges_email_verify_secret', $secret);
|
||||
set_user_preference('badges_email_verify_address', $email);
|
||||
|
||||
// To, from.
|
||||
$tempuser = $DB->get_record('user', array('id' => $USER->id), '*', MUST_EXIST);
|
||||
$tempuser->email = $email;
|
||||
$noreplyuser = core_user::get_noreply_user();
|
||||
|
||||
// Generate the verification email body.
|
||||
$verificationurl = '/badges/backpackemailverify.php';
|
||||
$verificationurl = new moodle_url($verificationurl);
|
||||
$verificationpath = $verificationurl->out(false);
|
||||
|
||||
$site = get_site();
|
||||
$args = new stdClass();
|
||||
$args->link = $verificationpath . '?data='. $secret;
|
||||
$args->sitename = $site->fullname;
|
||||
$args->admin = generate_email_signoff();
|
||||
|
||||
$messagesubject = get_string('backpackemailverifyemailsubject', 'badges', $site->fullname);
|
||||
$messagetext = get_string('backpackemailverifyemailbody', 'badges', $args);
|
||||
$messagehtml = text_to_html($messagetext, false, false, true);
|
||||
|
||||
return email_to_user($tempuser, $noreplyuser, $messagesubject, $messagetext, $messagehtml);
|
||||
}
|
||||
|
||||
+29
-13
@@ -110,23 +110,39 @@ if ($backpack) {
|
||||
}
|
||||
} else {
|
||||
// If backpack is not connected, need to connect first.
|
||||
$form = new edit_backpack_form();
|
||||
// To create a new connection to the backpack, first we need to verify the user's email address:
|
||||
// 1. User enters email and clicks 'Connect to backpack'.
|
||||
// 2. After cross-checking the email address against the backpack provider, an email is sent to the specified address,
|
||||
// and the email and secret are stored in user preferences. These will be cleared upon successful verification.
|
||||
// 3. User clicks verification link in the email to confirm the backpack connection.
|
||||
// 4. User redirected to the mybackpack page.
|
||||
// While the verification process is pending, the edit_backpack_form form will present the user with options to resend the
|
||||
// verification email, and to cancel the current verification attempt and start over.
|
||||
|
||||
// To pass through the current state of the verification attempt to the form.
|
||||
$params['email'] = get_user_preferences('badges_email_verify_address');
|
||||
|
||||
$form = new edit_backpack_form(new moodle_url('/badges/mybackpack.php'), $params);
|
||||
if ($form->is_cancelled()) {
|
||||
redirect(new moodle_url('/badges/mybadges.php'));
|
||||
} else if ($data = $form->get_data()) {
|
||||
$bp = new OpenBadgesBackpackHandler($data);
|
||||
|
||||
$obj = new stdClass();
|
||||
$obj->userid = $data->userid;
|
||||
$obj->email = $data->email;
|
||||
$obj->backpackurl = $data->backpackurl;
|
||||
$obj->backpackuid = $bp->curl_request('user')->userId;
|
||||
$obj->autosync = 0;
|
||||
$obj->password = '';
|
||||
$DB->insert_record('badge_backpack', $obj);
|
||||
|
||||
redirect(new moodle_url('/badges/mybackpack.php'));
|
||||
// The form may have been submitted under one of the following circumstances:
|
||||
// 1. After clicking 'Connect to backpack'. We'll have $data->email.
|
||||
// 2. After clicking 'Resend verification email'. We'll have $data->email.
|
||||
// 3. After clicking 'Connect using a different email' to cancel the verification process. We'll have $data->revertbutton.
|
||||
if (isset($data->revertbutton)) {
|
||||
unset_user_preference('badges_email_verify_secret');
|
||||
unset_user_preference('badges_email_verify_address');
|
||||
redirect(new moodle_url('/badges/mybackpack.php'));
|
||||
} else if (isset($data->email)) {
|
||||
if (send_verification_email($data->email)) {
|
||||
redirect(new moodle_url('/badges/mybackpack.php'),
|
||||
get_string('backpackemailverifypending', 'badges', $data->email),
|
||||
null, \core\output\notification::NOTIFY_INFO);
|
||||
} else {
|
||||
print_error ('backpackcannotsendverification', 'badges');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@core @core_badges @_file_upload
|
||||
@core @core_badges
|
||||
Feature: Add badges to the system
|
||||
In order to give badges to users for their achievements
|
||||
As an admin
|
||||
@@ -27,7 +27,7 @@ Feature: Add badges to the system
|
||||
Given I navigate to "Site badges" node in "Site pages"
|
||||
Then I should see "There are no badges available."
|
||||
|
||||
@javascript
|
||||
@javascript @_file_upload
|
||||
Scenario: Add a badge
|
||||
Given I navigate to "Add a new badge" node in "Site administration > Badges"
|
||||
And I set the following fields to these values:
|
||||
|
||||
@@ -682,7 +682,7 @@ class block_activity_results extends block_base {
|
||||
|
||||
$o = html_writer::start_tag('h3');
|
||||
$o .= html_writer::link(new moodle_url('/mod/'.$activity->itemmodule.'/view.php',
|
||||
array('id' => $cm->id)), $activity->itemname);
|
||||
array('id' => $cm->id)), format_string(($activity->itemname), true, ['context' => context_module::instance($cm->id)]));
|
||||
$o .= html_writer::end_tag('h3');
|
||||
return $o;
|
||||
}
|
||||
@@ -699,4 +699,4 @@ class block_activity_results extends block_base {
|
||||
return $scale;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,8 @@ class block_community_renderer extends plugin_renderer_base {
|
||||
array('class' => 'hubcourseuserinfo'));
|
||||
|
||||
//create course content related information html
|
||||
$course->subject = get_string($course->subject, 'edufields');
|
||||
$course->subject = (get_string_manager()->string_exists($course->subject, 'edufields')) ?
|
||||
get_string($course->subject, 'edufields') : get_string('none');
|
||||
$course->audience = get_string('audience' . $course->audience, 'hub');
|
||||
$course->educationallevel = get_string('edulevel' . $course->educationallevel, 'hub');
|
||||
$coursecontentinfo = '';
|
||||
|
||||
@@ -90,7 +90,7 @@ echo html_writer::start_tag('tbody');
|
||||
if ($USER->id != $user->id) {
|
||||
echo html_writer::start_tag('tr');
|
||||
echo html_writer::start_tag('td', array('colspan' => '2'));
|
||||
echo html_writer::tag('b', get_string('showinguser', 'completion'));
|
||||
echo html_writer::tag('b', get_string('showinguser', 'completion') . ' ');
|
||||
$url = new moodle_url('/user/view.php', array('id' => $user->id, 'course' => $course->id));
|
||||
echo html_writer::link($url, fullname($user));
|
||||
echo html_writer::end_tag('td');
|
||||
@@ -99,7 +99,7 @@ if ($USER->id != $user->id) {
|
||||
|
||||
echo html_writer::start_tag('tr');
|
||||
echo html_writer::start_tag('td', array('colspan' => '2'));
|
||||
echo html_writer::tag('b', get_string('status'));
|
||||
echo html_writer::tag('b', get_string('status') . ' ');
|
||||
|
||||
// Is course complete?
|
||||
$coursecomplete = $info->is_course_complete($user->id);
|
||||
@@ -141,7 +141,7 @@ if (empty($completions)) {
|
||||
} else {
|
||||
echo html_writer::start_tag('tr');
|
||||
echo html_writer::start_tag('td', array('colspan' => '2'));
|
||||
echo html_writer::tag('b', get_string('required'));
|
||||
echo html_writer::tag('b', get_string('required') . ' ');
|
||||
|
||||
// Get overall aggregation method.
|
||||
$overall = $info->get_aggregation_method();
|
||||
@@ -214,7 +214,7 @@ if (empty($completions)) {
|
||||
echo core_text::strtolower(get_string('any', 'completion'));
|
||||
}
|
||||
|
||||
echo html_writer::end_tag('i') .core_text::strtolower(get_string('required')).')';
|
||||
echo ' ' . html_writer::end_tag('i') .core_text::strtolower(get_string('required')).')';
|
||||
$agg_type = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ class block_course_overview extends block_base {
|
||||
$content = array();
|
||||
|
||||
$updatemynumber = optional_param('mynumber', -1, PARAM_INT);
|
||||
if ($updatemynumber >= 0) {
|
||||
if ($updatemynumber >= 0 && optional_param('sesskey', '', PARAM_RAW) && confirm_sesskey()) {
|
||||
block_course_overview_update_mynumber($updatemynumber);
|
||||
}
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ class block_course_overview_renderer extends plugin_renderer_base {
|
||||
for ($i = 1; $i <= $max; $i++) {
|
||||
$options[$i] = $i;
|
||||
}
|
||||
$url = new moodle_url('/my/index.php');
|
||||
$url = new moodle_url('/my/index.php', ['sesskey' => sesskey()]);
|
||||
$select = new single_select($url, 'mynumber', $options, block_course_overview_get_max_user_courses(), array());
|
||||
$select->set_label(get_string('numtodisplay', 'block_course_overview'));
|
||||
$output .= $this->output->render($select);
|
||||
|
||||
@@ -119,7 +119,9 @@ class block_login extends block_base {
|
||||
$this->content->text .= '<h6>' . get_string('potentialidps', 'auth') . '</h6>';
|
||||
$this->content->text .= '<div class="potentialidplist">';
|
||||
foreach ($potentialidps as $idp) {
|
||||
$this->content->text .= '<div class="potentialidp"><a href="' . $idp['url']->out() . '" title="' . s($idp['name']) . '">';
|
||||
$this->content->text .= '<div class="potentialidp">';
|
||||
$this->content->text .= '<a class="btn btn-default btn-block" ';
|
||||
$this->content->text .= 'href="' . $idp['url']->out() . '" title="' . s($idp['name']) . '">';
|
||||
$this->content->text .= $OUTPUT->render($idp['icon'], $idp['name']) . s($idp['name']) . '</a></div>';
|
||||
}
|
||||
$this->content->text .= '</div>';
|
||||
|
||||
@@ -52,11 +52,11 @@ $action = (empty($id)) ? 'add' : 'edit';
|
||||
|
||||
$external = new stdClass();
|
||||
|
||||
// Check that this id exists.
|
||||
if (!empty($id) && !$DB->record_exists('blog_external', array('id' => $id))) {
|
||||
print_error('wrongexternalid', 'blog');
|
||||
} else if (!empty($id)) {
|
||||
$external = $DB->get_record('blog_external', array('id' => $id));
|
||||
// Retrieve the external blog record.
|
||||
if (!empty($id)) {
|
||||
if (!$external = $DB->get_record('blog_external', array('id' => $id, 'userid' => $USER->id))) {
|
||||
print_error('wrongexternalid', 'blog');
|
||||
}
|
||||
$external->autotags = core_tag_tag::get_item_tags_array('core', 'blog_external', $id);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -872,7 +872,7 @@ function blog_get_headers($courseid=null, $groupid=null, $userid=null, $tagid=nu
|
||||
}
|
||||
|
||||
// Append Search info.
|
||||
if (!empty($search)) {
|
||||
if (!empty($search) && has_capability('moodle/blog:search', $sitecontext)) {
|
||||
$headers['filters']['search'] = $search;
|
||||
$blogurl->param('search', $search);
|
||||
$PAGE->navbar->add(get_string('searchterm', 'blog', $search), $blogurl->out());
|
||||
|
||||
Vendored
+2
-2
@@ -1018,7 +1018,7 @@ class cache implements cache_loader {
|
||||
$result = $data;
|
||||
}
|
||||
}
|
||||
if ($result) {
|
||||
if ($result !== false) {
|
||||
if ($this->perfdebug) {
|
||||
cache_helper::record_cache_hit('** static acceleration **', $this->definition);
|
||||
}
|
||||
@@ -2197,4 +2197,4 @@ class cache_session extends cache {
|
||||
*/
|
||||
class cache_request extends cache {
|
||||
// This comment appeases code pre-checker ;) !
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+54
-2
@@ -229,8 +229,60 @@ class cachestore_memcached extends cache_store implements cache_is_configurable
|
||||
$version = phpversion('memcached');
|
||||
$this->candeletemulti = ($version && version_compare($version, self::REQUIRED_VERSION, '>='));
|
||||
|
||||
// Test the connection to the main connection.
|
||||
$this->isready = @$this->connection->set("ping", 'ping', 1);
|
||||
$this->isready = $this->is_connection_ready();
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm whether the connection is ready and usable.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_connection_ready() {
|
||||
if (!@$this->connection->set("ping", 'ping', 1)) {
|
||||
// Test the connection to the server.
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isshared) {
|
||||
// There is a bug in libmemcached which means that it is not possible to purge the cache in a shared cache
|
||||
// configuration.
|
||||
// This issue currently affects:
|
||||
// - memcached 1.4.23+ with php-memcached <= 2.2.0
|
||||
// The following combinations are not affected:
|
||||
// - memcached <= 1.4.22 with any version of php-memcached
|
||||
// - any version of memcached with php-memcached >= 3.0.1
|
||||
|
||||
|
||||
// This check is cheapest as it does not involve connecting to the server at all.
|
||||
$safecombination = false;
|
||||
$extension = new ReflectionExtension('memcached');
|
||||
if ((version_compare($extension->getVersion(), '3.0.1') >= 0)) {
|
||||
// This is php-memcached version >= 3.0.1 which is a safe combination.
|
||||
$safecombination = true;
|
||||
}
|
||||
|
||||
if (!$safecombination) {
|
||||
$allsafe = true;
|
||||
foreach ($this->connection->getVersion() as $version) {
|
||||
$allsafe = $allsafe && (version_compare($version, '1.4.22') <= 0);
|
||||
}
|
||||
// All memcached servers connected are version <= 1.4.22 which is a safe combination.
|
||||
$safecombination = $allsafe;
|
||||
}
|
||||
|
||||
if (!$safecombination) {
|
||||
// This is memcached 1.4.23+ and php-memcached < 3.0.1.
|
||||
// The issue may have been resolved in a subsequent update to any of the three libraries.
|
||||
// The only way to safely determine if the combination is safe is to call getAllKeys.
|
||||
// A safe combination will return an array, whilst an affected combination will return false.
|
||||
// This is the most expensive check.
|
||||
if (!is_array($this->connection->getAllKeys())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -288,6 +288,10 @@ class cachestore_memcached_test extends cachestore_tests {
|
||||
|
||||
$definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_memcached', 'phpunit_test');
|
||||
$cachestore = $this->create_test_cache_with_config($definition, array('isshared' => true));
|
||||
if (!$cachestore->is_connection_ready()) {
|
||||
$this->markTestSkipped('Could not test cachestore_memcached. Connection is not ready.');
|
||||
}
|
||||
|
||||
$connection = new Memcached(crc32(__METHOD__));
|
||||
$connection->addServers($this->get_servers(TEST_CACHESTORE_MEMCACHED_TESTSERVERS));
|
||||
$connection->setOptions(array(
|
||||
|
||||
Vendored
+33
@@ -2173,6 +2173,39 @@ class core_cache_testcase extends advanced_testcase {
|
||||
$startstats[$requestid]['stores']['cachestore_static']['sets']);
|
||||
}
|
||||
|
||||
public function test_static_cache() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest(true);
|
||||
$CFG->perfdebug = 15;
|
||||
|
||||
// Create cache store with static acceleration.
|
||||
$instance = cache_config_testing::instance();
|
||||
$applicationid = 'phpunit/applicationperf';
|
||||
$instance->phpunit_add_definition($applicationid, array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'applicationperf',
|
||||
'simplekeys' => true,
|
||||
'staticacceleration' => true,
|
||||
'staticaccelerationsize' => 3
|
||||
));
|
||||
|
||||
$application = cache::make('phpunit', 'applicationperf');
|
||||
|
||||
// Check that stores register sets.
|
||||
$this->assertTrue($application->set('setMe1', 1));
|
||||
$this->assertTrue($application->set('setMe2', 0));
|
||||
$this->assertTrue($application->set('setMe3', array()));
|
||||
$this->assertTrue($application->get('setMe1') !== false);
|
||||
$this->assertTrue($application->get('setMe2') !== false);
|
||||
$this->assertTrue($application->get('setMe3') !== false);
|
||||
|
||||
// Check that the static acceleration worked, even on empty arrays and the number 0.
|
||||
$endstats = cache_helper::get_stats();
|
||||
$this->assertEquals(0, $endstats[$applicationid]['stores']['** static acceleration **']['misses']);
|
||||
$this->assertEquals(3, $endstats[$applicationid]['stores']['** static acceleration **']['hits']);
|
||||
}
|
||||
|
||||
public function test_performance_debug_off() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
Vendored
+14
@@ -288,6 +288,20 @@ class cache_config_testing extends cache_config_writer {
|
||||
global $CFG;
|
||||
return $CFG->wwwroot.'phpunit';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the configuration file exists.
|
||||
*
|
||||
* @return bool True if it exists
|
||||
*/
|
||||
public static function config_file_exists() {
|
||||
// Allow for late static binding by using static.
|
||||
$configfilepath = static::get_config_file_path();
|
||||
|
||||
// Invalidate opcode php cache, so we get correct status of file.
|
||||
core_component::invalidate_opcode_php_cache($configfilepath);
|
||||
return file_exists($configfilepath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1029
-338
File diff suppressed because it is too large
Load Diff
@@ -72,7 +72,8 @@ class event_form extends moodleform {
|
||||
if (!empty($eventtypes->groups) && is_array($eventtypes->groups)) {
|
||||
$groupoptions = array();
|
||||
foreach ($eventtypes->groups as $group) {
|
||||
$groupoptions[$group->id] = $group->name;
|
||||
$groupoptions[$group->id] = format_string($group->name, true,
|
||||
array('context' => context_course::instance($group->courseid)));
|
||||
}
|
||||
$mform->addElement('select', 'groupid', get_string('typegroup', 'calendar'), $groupoptions);
|
||||
$mform->disabledIf('groupid', 'eventtype', 'noteq', 'group');
|
||||
|
||||
+3
-2
@@ -77,7 +77,8 @@ if (!empty($day) && !empty($mon) && !empty($year)) {
|
||||
}
|
||||
|
||||
if ($courseid != SITEID && !empty($courseid)) {
|
||||
$course = $DB->get_record('course', array('id' => $courseid));
|
||||
// Course ID must be valid and existing.
|
||||
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
|
||||
$courses = array($course->id => $course);
|
||||
$issite = false;
|
||||
} else {
|
||||
@@ -85,7 +86,7 @@ if ($courseid != SITEID && !empty($courseid)) {
|
||||
$courses = calendar_get_default_courses();
|
||||
$issite = true;
|
||||
}
|
||||
require_course_login($course);
|
||||
require_login($course, false);
|
||||
|
||||
$url = new moodle_url('/calendar/export.php', array('time' => $time));
|
||||
|
||||
|
||||
@@ -193,10 +193,20 @@ foreach($events as $event) {
|
||||
$hostaddress = str_replace('http://', '', $CFG->wwwroot);
|
||||
$hostaddress = str_replace('https://', '', $hostaddress);
|
||||
|
||||
$ev = new iCalendar_event;
|
||||
$me = new calendar_event($event); // To use moodle calendar event services.
|
||||
$ev = new iCalendar_event; // To export in ical format.
|
||||
$ev->add_property('uid', $event->id.'@'.$hostaddress);
|
||||
$ev->add_property('summary', $event->name);
|
||||
$ev->add_property('description', clean_param($event->description, PARAM_NOTAGS));
|
||||
|
||||
// Set iCal event summary from event name.
|
||||
$ev->add_property('summary', format_string($event->name, true, ['context' => $me->context]));
|
||||
|
||||
// Format the description text.
|
||||
$description = format_text($me->description, $me->format, ['context' => $me->context]);
|
||||
// Then convert it to plain text, since it's the only format allowed for the event description property.
|
||||
// We use html_to_text in order to convert <br> and <p> tags to new line characters for descriptions in HTML format.
|
||||
$description = html_to_text($description, 0);
|
||||
$ev->add_property('description', $description);
|
||||
|
||||
$ev->add_property('class', 'PUBLIC'); // PUBLIC / PRIVATE / CONFIDENTIAL
|
||||
$ev->add_property('last-modified', Bennu::timestamp_to_datetime($event->timemodified));
|
||||
$ev->add_property('dtstamp', Bennu::timestamp_to_datetime()); // now
|
||||
|
||||
@@ -45,13 +45,15 @@ $PAGE->set_pagelayout('admin');
|
||||
$PAGE->navbar->add(get_string('managesubscriptions', 'calendar'));
|
||||
|
||||
if ($courseid != SITEID && !empty($courseid)) {
|
||||
$course = $DB->get_record('course', array('id' => $courseid));
|
||||
// Course ID must be valid and existing.
|
||||
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
|
||||
$courses = array($course->id => $course);
|
||||
} else {
|
||||
$course = get_site();
|
||||
$courses = calendar_get_default_courses();
|
||||
}
|
||||
require_course_login($course);
|
||||
require_login($course, false);
|
||||
|
||||
if (!calendar_user_can_add_event($course)) {
|
||||
print_error('errorcannotimport', 'calendar');
|
||||
}
|
||||
|
||||
@@ -171,4 +171,55 @@ class core_calendar_lib_testcase extends advanced_testcase {
|
||||
$event = reset($events);
|
||||
$this->assertEquals('assign', $event->modulename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create calendar events using the old code.
|
||||
*
|
||||
* @param array $properties A list of calendar event properties to set
|
||||
* @return calendar_event|bool
|
||||
*/
|
||||
protected function create_event($properties = []) {
|
||||
$record = new \stdClass();
|
||||
$record->name = 'event name';
|
||||
$record->eventtype = 'global';
|
||||
$record->timestart = time();
|
||||
$record->timeduration = 0;
|
||||
$record->timesort = 0;
|
||||
$record->type = 1;
|
||||
$record->courseid = 0;
|
||||
|
||||
foreach ($properties as $name => $value) {
|
||||
$record->$name = $value;
|
||||
}
|
||||
|
||||
$event = new calendar_event($record);
|
||||
return $event->create($record, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that when course module is deleted all events are also deleted.
|
||||
*/
|
||||
public function test_delete_module_delete_events() {
|
||||
global $DB;
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
// Create the course we will be using.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
|
||||
|
||||
foreach (core_component::get_plugin_list('mod') as $modname => $unused) {
|
||||
$module = $this->getDataGenerator()->create_module($modname, ['course' => $course->id]);
|
||||
|
||||
// Create bunch of events of different type (user override, group override, module event).
|
||||
$this->create_event(['userid' => $user->id, 'modulename' => $modname, 'instance' => $module->id]);
|
||||
$this->create_event(['groupid' => $group->id, 'modulename' => $modname, 'instance' => $module->id]);
|
||||
$this->create_event(['modulename' => $modname, 'instance' => $module->id]);
|
||||
$this->create_event(['modulename' => $modname, 'instance' => $module->id, 'courseid' => $course->id]);
|
||||
|
||||
// Delete module and make sure all events are deleted.
|
||||
course_delete_module($module->cmid);
|
||||
$this->assertEmpty($DB->get_record('event', ['modulename' => $modname, 'instance' => $module->id]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,537 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Defines test class to test manage rrule during ical imports.
|
||||
*
|
||||
* @package core_calendar
|
||||
* @category test
|
||||
* @copyright 2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
|
||||
/** @var stdClass a dummy event */
|
||||
protected $event;
|
||||
|
||||
/**
|
||||
* Set up method.
|
||||
*/
|
||||
protected function setUp() {
|
||||
global $DB, $CFG;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$this->setTimezone('Australia/Perth');
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$sub = new stdClass();
|
||||
$sub->url = '';
|
||||
$sub->courseid = 0;
|
||||
$sub->groupid = 0;
|
||||
$sub->userid = $user->id;
|
||||
$sub->pollinterval = 0;
|
||||
$subid = $DB->insert_record('event_subscriptions', $sub, true);
|
||||
|
||||
$event = new stdClass();
|
||||
$event->name = 'Event name';
|
||||
$event->description = '';
|
||||
$event->timestart = 1385913700; // A 2013-12-2 Monday event.
|
||||
$event->timeduration = 3600;
|
||||
$event->uuid = 'uuid';
|
||||
$event->subscriptionid = $subid;
|
||||
$event->userid = $user->id;
|
||||
$event->groupid = 0;
|
||||
$event->courseid = 0;
|
||||
$event->eventtype = 'user';
|
||||
$eventobj = calendar_event::create($event, false);
|
||||
$DB->set_field('event', 'repeatid', $eventobj->id, array('id' => $eventobj->id));
|
||||
$eventobj->repeatid = $eventobj->id;
|
||||
$this->event = $eventobj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test parse_rrule() method.
|
||||
*/
|
||||
public function test_parse_rrule() {
|
||||
|
||||
$rrule = "FREQ=DAILY;COUNT=3;INTERVAL=4;BYSECOND=20,40;BYMINUTE=2,30;BYHOUR=3,4;BYDAY=MO,TH;BYMONTHDAY=20,
|
||||
30;BYYEARDAY=300,-20;BYWEEKNO=22,33;BYMONTH=3,4";
|
||||
$mang = new core_tests_calendar_rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$this->assertEquals(\core_calendar\rrule_manager::FREQ_DAILY, $mang->freq);
|
||||
$this->assertEquals(3, $mang->count);
|
||||
$this->assertEquals(4, $mang->interval);
|
||||
$this->assertEquals(array(20, 40), $mang->bysecond);
|
||||
$this->assertEquals(array(2, 30), $mang->byminute);
|
||||
$this->assertEquals(array(3, 4), $mang->byhour);
|
||||
$this->assertEquals(array('MO', 'TH'), $mang->byday);
|
||||
$this->assertEquals(array(20, 30), $mang->bymonthday);
|
||||
$this->assertEquals(array(300, -20), $mang->byyearday);
|
||||
$this->assertEquals(array(22, 33), $mang->byweekno);
|
||||
$this->assertEquals(array(3, 4), $mang->bymonth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test exception is thrown for invalid property.
|
||||
*
|
||||
* @expectedException moodle_exception
|
||||
*/
|
||||
public function test_parse_rrule_validation() {
|
||||
|
||||
$rrule = "RANDOM=PROPERTY;";
|
||||
$mang = new core_tests_calendar_rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test exception is thrown for invalid frequency.
|
||||
*
|
||||
* @expectedException moodle_exception
|
||||
*/
|
||||
public function test_freq_validation() {
|
||||
|
||||
$rrule = "FREQ=RANDOMLY;";
|
||||
$mang = new core_tests_calendar_rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test recurrence rules for daily frequency.
|
||||
*/
|
||||
public function test_daily_events() {
|
||||
global $DB;
|
||||
|
||||
$rrule = 'FREQ=DAILY;COUNT=3'; // This should generate 2 child events + 1 parent.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(3, $count);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + DAYSECS)));
|
||||
$this->assertTrue($result);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + 2 * DAYSECS)));
|
||||
$this->assertTrue($result);
|
||||
|
||||
$until = $this->event->timestart + DAYSECS * 2;
|
||||
$until = date('Y-m-d', $until);
|
||||
$rrule = "FREQ=DAILY;UNTIL=$until"; // This should generate 1 child event + 1 parent,since by then until bound would be hit.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(2, $count);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + DAYSECS)));
|
||||
$this->assertTrue($result);
|
||||
|
||||
$rrule = 'FREQ=DAILY;COUNT=3;INTERVAL=3'; // This should generate 2 child events + 1 parent, every 3rd day.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(3, $count);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + 3 * DAYSECS)));
|
||||
$this->assertTrue($result);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + 6 * DAYSECS)));
|
||||
$this->assertTrue($result);
|
||||
|
||||
// Forever event. This should generate events for time() + 10 year period, every 300th day.
|
||||
$rrule = 'FREQ=DAILY;INTERVAL=300';
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $time = $this->event->timestart + 300 * DAYSECS * $i) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test recurrence rules for weekly frequency.
|
||||
*/
|
||||
public function test_weekly_events() {
|
||||
global $DB;
|
||||
|
||||
$rrule = 'FREQ=WEEKLY;COUNT=1'; // This should generate 7 events in total, one for each day.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(7, $count);
|
||||
for ($i = 0; $i < 7; $i++) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + $i * DAYSECS)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This should generate 4 child event + 1 parent, since by then until bound would be hit.
|
||||
$until = $this->event->timestart + WEEKSECS * 4;
|
||||
$until = date('YmdThis', $until);
|
||||
$rrule = "FREQ=WEEKLY;BYDAY=MO;UNTIL=$until";
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(5, $count);
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + $i * WEEKSECS)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This should generate 4 events in total every monday and Wednesday of every 3rd week.
|
||||
$rrule = 'FREQ=WEEKLY;INTERVAL=3;BYDAY=MO,WE;COUNT=2';
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(4, $count);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + 3 * WEEKSECS))); // Monday event.
|
||||
$this->assertTrue($result);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + 2 * DAYSECS))); // Wednesday event.
|
||||
$this->assertTrue($result);
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($this->event->timestart + 3 * WEEKSECS + 2 * DAYSECS))); // Wednesday event.
|
||||
$this->assertTrue($result);
|
||||
|
||||
// Forever event. This should generate events over time() + 10 year period, every 50th monday.
|
||||
$rrule = 'FREQ=WEEKLY;BYDAY=MO;INTERVAL=50';
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $time = $this->event->timestart + 50 * WEEKSECS * $i) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test recurrence rules for monthly frequency.
|
||||
*/
|
||||
public function test_monthly_events() {
|
||||
global $DB;
|
||||
$rrule = 'FREQ=MONTHLY;COUNT=3;BYMONTHDAY=2'; // This should generate 3 events in total.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(3, $count);
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => (strtotime("+$i month", $this->event->timestart))));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This much seconds after the start of the day.
|
||||
$offset = $this->event->timestart - mktime(0, 0, 0, date("n", $this->event->timestart), date("j", $this->event->timestart),
|
||||
date("Y", $this->event->timestart));
|
||||
$monthstart = mktime(0, 0, 0, date("n", $this->event->timestart), 1, date("Y", $this->event->timestart));
|
||||
|
||||
$rrule = 'FREQ=MONTHLY;COUNT=3;BYDAY=1MO'; // This should generate 3 events in total, first monday of the month.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(3, $count);
|
||||
$time = strtotime('1 Monday', strtotime("+1 months", $monthstart)) + $offset;
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time));
|
||||
$this->assertTrue($result);
|
||||
$time = strtotime('1 Monday', strtotime("+2 months", $monthstart)) + $offset;
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time));
|
||||
$this->assertTrue($result);
|
||||
|
||||
// This should generate 10 child event + 1 parent, since by then until bound would be hit.
|
||||
$until = strtotime('+1 day +10 months', $this->event->timestart);
|
||||
$until = date('YmdThis', $until);
|
||||
$rrule = "FREQ=MONTHLY;BYMONTHDAY=2;UNTIL=$until";
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(11, $count);
|
||||
for ($i = 0; $i < 11; $i++) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => (strtotime("+$i month", $this->event->timestart))));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This should generate 10 child event + 1 parent, since by then until bound would be hit.
|
||||
$until = strtotime('+1 day +10 months', $this->event->timestart);
|
||||
$until = date('YmdThis', $until);
|
||||
$rrule = "FREQ=MONTHLY;BYDAY=1MO;UNTIL=$until";
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(10, $count);
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$time = strtotime('1 Monday', strtotime("+$i months", $monthstart)) + $offset;
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This should generate 11 child event + 1 parent, since by then until bound would be hit.
|
||||
$until = strtotime('+10 day +10 months', $this->event->timestart); // 12 oct 2014.
|
||||
$until = date('YmdThis', $until);
|
||||
$rrule = "FREQ=MONTHLY;INTERVAL=2;BYMONTHDAY=2,5;UNTIL=$until";
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(12, $count);
|
||||
for ($i = 0; $i < 6; $i++) {
|
||||
$moffset = $i * 2;
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => (strtotime("+$moffset month", $this->event->timestart))));
|
||||
$this->assertTrue($result);
|
||||
// Event on the 5th of a month.
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => (strtotime("+3 days +$moffset month", $this->event->timestart))));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This should generate 11 child event + 1 parent, since by then until bound would be hit.
|
||||
$until = strtotime('+20 day +10 months', $this->event->timestart); // 22 oct 2014.
|
||||
$until = date('YmdTHis', $until);
|
||||
$rrule = "FREQ=MONTHLY;INTERVAL=2;BYDAY=1MO,3WE;UNTIL=$until";
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(12, $count);
|
||||
for ($i = 0; $i < 6; $i++) {
|
||||
$moffset = $i * 2;
|
||||
$time = strtotime("+$moffset month", $monthstart);
|
||||
$time2 = strtotime("+1 Monday", $time) + $offset;
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time2));
|
||||
$this->assertTrue($result);
|
||||
$time2 = strtotime("+3 Wednesday", $time) + $offset;
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => $time2)); // Event on the 5th of a month.
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// Forever event. This should generate events over 10 year period, on 2nd of every 12th month.
|
||||
$rrule = 'FREQ=MONTHLY;INTERVAL=12;BYMONTHDAY=2';
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $moffset = $i * 12,
|
||||
$time = strtotime("+$moffset month", $this->event->timestart)) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// Forever event. This should generate 10 child events + 1 parent over 10 year period, every 50th Monday.
|
||||
$rrule = 'FREQ=MONTHLY;BYDAY=1MO;INTERVAL=12';
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(11, $count);
|
||||
for ($i = 0, $moffset = 0, $time = $this->event->timestart; $time < $until; $i++, $moffset = $i * 12) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
$time = strtotime("+$moffset month", $monthstart);
|
||||
$time = strtotime("+1 Monday", $time) + $offset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test recurrence rules for yearly frequency.
|
||||
*/
|
||||
public function test_yearly_events() {
|
||||
global $DB;
|
||||
|
||||
$rrule = 'FREQ=YEARLY;COUNT=3;BYMONTH=12'; // This should generate 3 events in total.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(3, $count);
|
||||
for ($i = 0, $time = $this->event->timestart; $i < 3; $i++, $time = strtotime("+$i years", $this->event->timestart)) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// Create an event every december, until the time limit is hit.
|
||||
$until = strtotime('+20 day +10 years', $this->event->timestart);
|
||||
$until = date('YmdTHis', $until);
|
||||
$rrule = "FREQ=YEARLY;BYMONTH=12;UNTIL=$until"; // Forever event.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(11, $count);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $yoffset = $i * 2,
|
||||
$time = strtotime("+$yoffset years", $this->event->timestart)) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This should generate 5 events in total, every second year in the month of december.
|
||||
$rrule = 'FREQ=YEARLY;BYMONTH=12;INTERVAL=2;COUNT=5';
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(5, $count);
|
||||
for ($i = 0, $time = $this->event->timestart; $i < 5; $i++, $yoffset = $i * 2,
|
||||
$time = strtotime("+$yoffset years", $this->event->timestart)) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
$rrule = 'FREQ=YEARLY;BYMONTH=12;INTERVAL=2'; // Forever event.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(6, $count);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $yoffset = $i * 2,
|
||||
$time = strtotime("+$yoffset years", $this->event->timestart)) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// This much seconds after the start of the day.
|
||||
$offset = $this->event->timestart - mktime(0, 0, 0, date("n", $this->event->timestart), date("j", $this->event->timestart),
|
||||
date("Y", $this->event->timestart));
|
||||
$yearstart = mktime(0, 0, 0, 1, 1, date("Y", $this->event->timestart));
|
||||
|
||||
$rrule = 'FREQ=YEARLY;COUNT=3;BYMONTH=12;BYDAY=1MO'; // This should generate 3 events in total.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(3, $count);
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$time = strtotime("+11 months +$i years", $yearstart);
|
||||
$time = strtotime("+1 Monday", $time) + $offset;
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time));
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
// Create an event every december, until the time limit is hit.
|
||||
$until = strtotime('+20 day +10 years', $this->event->timestart);
|
||||
$until = date('YmdTHis', $until);
|
||||
$rrule = "FREQ=YEARLY;BYMONTH=12;UNTIL=$until;BYDAY=1MO";
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(11, $count);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
$time = strtotime("+11 months +$i years", $yearstart);
|
||||
$time = strtotime("+1 Monday", $time) + $offset;
|
||||
}
|
||||
|
||||
// This should generate 5 events in total, every second year in the month of december.
|
||||
$rrule = 'FREQ=YEARLY;BYMONTH=12;INTERVAL=2;COUNT=5;BYDAY=1MO';
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(5, $count);
|
||||
for ($i = $yoffset = 0, $time = $this->event->timestart; $i < 5; $i++, $yoffset = $i * 2) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
$time = strtotime("+11 months +$yoffset years", $yearstart);
|
||||
$time = strtotime("+1 Monday", $time) + $offset;
|
||||
}
|
||||
|
||||
$rrule = 'FREQ=YEARLY;BYMONTH=12;INTERVAL=2;BYDAY=1MO'; // Forever event.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(6, $count);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $yoffset = $i * 2) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
$time = strtotime("+11 months +$yoffset years", $yearstart);
|
||||
$time = strtotime("+1 Monday", $time) + $offset;
|
||||
}
|
||||
|
||||
$rrule = 'FREQ=YEARLY;INTERVAL=2'; // Forever event.
|
||||
$mang = new \core_calendar\rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$count = $DB->count_records('event', array('repeatid' => $this->event->id));
|
||||
$this->assertEquals(6, $count);
|
||||
for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $yoffset = $i * 2) {
|
||||
$result = $DB->record_exists('event', array('repeatid' => $this->event->id,
|
||||
'timestart' => ($time)));
|
||||
$this->assertTrue($result);
|
||||
$time = strtotime("+11 months +$yoffset years", $yearstart);
|
||||
$time = strtotime("+1 Monday", $time) + $offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class core_calendar_test_rrule_manager
|
||||
*
|
||||
* Wrapper to access protected vars for testing.
|
||||
*
|
||||
* @package core_calendar
|
||||
* @category test
|
||||
* @copyright 2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_tests_calendar_rrule_manager extends \core_calendar\rrule_manager{
|
||||
|
||||
/**
|
||||
* Magic method to get properties.
|
||||
*
|
||||
* @param $prop string property
|
||||
*
|
||||
* @return mixed
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function __get($prop) {
|
||||
if (property_exists($this, $prop)) {
|
||||
return $this->$prop;
|
||||
}
|
||||
throw new coding_exception('invalidproperty');
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -85,7 +85,8 @@ $url->param('time', $time);
|
||||
$PAGE->set_url($url);
|
||||
|
||||
if ($courseid != SITEID && !empty($courseid)) {
|
||||
$course = $DB->get_record('course', array('id' => $courseid));
|
||||
// Course ID must be valid and existing.
|
||||
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
|
||||
$courses = array($course->id => $course);
|
||||
$issite = false;
|
||||
navigation_node::override_active_url(new moodle_url('/course/view.php', array('id' => $course->id)));
|
||||
@@ -95,7 +96,7 @@ if ($courseid != SITEID && !empty($courseid)) {
|
||||
$issite = true;
|
||||
}
|
||||
|
||||
require_course_login($course);
|
||||
require_login($course, false);
|
||||
|
||||
$calendar = new calendar_information(0, 0, 0, $time);
|
||||
$calendar->prepare_for_view($course, $courses);
|
||||
|
||||
@@ -676,7 +676,8 @@ class competency extends persistent {
|
||||
public static function share_same_framework(array $ids) {
|
||||
global $DB;
|
||||
list($insql, $params) = $DB->get_in_or_equal($ids);
|
||||
return $DB->count_records_select(self::TABLE, "id $insql", $params, "COUNT(DISTINCT(competencyframeworkid))") == 1;
|
||||
$sql = "SELECT COUNT('x') FROM (SELECT DISTINCT(competencyframeworkid) FROM {" . self::TABLE . "} WHERE id {$insql}) f";
|
||||
return $DB->count_records_sql($sql, $params) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -243,7 +243,9 @@ class course_competency extends persistent {
|
||||
public static function list_courses($competencyid) {
|
||||
global $DB;
|
||||
|
||||
$results = $DB->get_records_sql('SELECT course.id, course.visible, course.shortname, course.idnumber, course.fullname
|
||||
$results = $DB->get_records_sql('SELECT course.id, course.visible, course.shortname, course.idnumber,
|
||||
course.fullname, course.summary, course.summaryformat, course.startdate,
|
||||
course.enddate
|
||||
FROM {course} course
|
||||
JOIN {' . self::TABLE . '} coursecomp
|
||||
ON coursecomp.courseid = course.id
|
||||
|
||||
@@ -243,6 +243,8 @@ class user_competency_course extends persistent {
|
||||
|
||||
$sql = 'SELECT COUNT(comp.id)
|
||||
FROM {' . self::TABLE . '} usercoursecomp
|
||||
JOIN {' . course_competency::TABLE . '} cc
|
||||
ON usercoursecomp.competencyid = cc.competencyid AND cc.courseid = usercoursecomp.courseid
|
||||
JOIN {' . competency::TABLE . '} comp
|
||||
ON usercoursecomp.competencyid = comp.id
|
||||
WHERE usercoursecomp.courseid = ? AND usercoursecomp.userid = ? AND usercoursecomp.proficiency = ?';
|
||||
|
||||
@@ -74,7 +74,16 @@ class completion_completion extends data_object {
|
||||
* @return data_object instance of data_object or false if none found.
|
||||
*/
|
||||
public static function fetch($params) {
|
||||
return self::fetch_helper('course_completions', __CLASS__, $params);
|
||||
$cache = cache::make('core', 'coursecompletion');
|
||||
|
||||
$key = $params['userid'] . '_' . $params['course'];
|
||||
if ($hit = $cache->get($key)) {
|
||||
return $hit['value'];
|
||||
}
|
||||
|
||||
$tocache = self::fetch_helper('course_completions', __CLASS__, $params);
|
||||
$cache->set($key, ['value' => $tocache]);
|
||||
return $tocache;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,9 +188,10 @@ class completion_completion extends data_object {
|
||||
$this->timeenrolled = 0;
|
||||
}
|
||||
|
||||
$result = false;
|
||||
// Save record
|
||||
if ($this->id) {
|
||||
return $this->update();
|
||||
$result = $this->update();
|
||||
} else {
|
||||
// Make sure reaggregate field is not null
|
||||
if (!$this->reaggregate) {
|
||||
@@ -193,7 +203,17 @@ class completion_completion extends data_object {
|
||||
$this->timestarted = 0;
|
||||
}
|
||||
|
||||
return $this->insert();
|
||||
$result = $this->insert();
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
// Update the cached record.
|
||||
$cache = cache::make('core', 'coursecompletion');
|
||||
$data = $this->get_record_data();
|
||||
$key = $data->userid . '_' . $data->course;
|
||||
$cache->set($key, ['value' => $data]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,13 +84,17 @@ class completion_criteria_activity extends completion_criteria {
|
||||
|
||||
$this->course = $data->id;
|
||||
|
||||
foreach (array_keys($data->criteria_activity) as $activity) {
|
||||
|
||||
$module = $DB->get_record('course_modules', array('id' => $activity));
|
||||
$this->module = self::get_mod_name($module->module);
|
||||
$this->moduleinstance = $activity;
|
||||
$this->id = NULL;
|
||||
$this->insert();
|
||||
// Data comes from advcheckbox, so contains keys for all activities.
|
||||
// A value of 0 is 'not checked' whereas 1 is 'checked'.
|
||||
foreach ($data->criteria_activity as $activity => $val) {
|
||||
// Only update those which are checked.
|
||||
if (!empty($val)) {
|
||||
$module = $DB->get_record('course_modules', array('id' => $activity));
|
||||
$this->module = self::get_mod_name($module->module);
|
||||
$this->moduleinstance = $activity;
|
||||
$this->id = null;
|
||||
$this->insert();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-13
@@ -217,6 +217,12 @@ class course_edit_form extends moodleform {
|
||||
$mform->addElement('select', 'calendartype', get_string('forcecalendartype', 'calendar'), $calendars);
|
||||
}
|
||||
|
||||
$options = range(0, 10);
|
||||
$mform->addElement('select', 'newsitems', get_string('newsitemsnumber'), $options);
|
||||
$courseconfig = get_config('moodlecourse');
|
||||
$mform->setDefault('newsitems', $courseconfig->newsitems);
|
||||
$mform->addHelpButton('newsitems', 'newsitemsnumber');
|
||||
|
||||
$mform->addElement('selectyesno', 'showgrades', get_string('showgrades'));
|
||||
$mform->addHelpButton('showgrades', 'showgrades');
|
||||
$mform->setDefault('showgrades', $courseconfig->showgrades);
|
||||
@@ -368,19 +374,9 @@ class course_edit_form extends moodleform {
|
||||
'addcourseformatoptionshere');
|
||||
}
|
||||
|
||||
if ($courseformat->supports_news()) {
|
||||
// Show the news items select field if the course format supports news.
|
||||
$options = range(0, 10);
|
||||
$newsitems = $mform->createElement('select', 'newsitems', get_string('newsitemsnumber'), $options);
|
||||
$mform->insertElementBefore($newsitems, 'showgrades');
|
||||
$courseconfig = get_config('moodlecourse');
|
||||
$mform->setDefault('newsitems', $courseconfig->newsitems);
|
||||
$mform->addHelpButton('newsitems', 'newsitemsnumber');
|
||||
} else {
|
||||
// Otherwise, create a hidden newsitems element and set its value to 0.
|
||||
$newsitems = $mform->addElement('hidden', 'newsitems', 0);
|
||||
$mform->setType('newsitems', PARAM_INT);
|
||||
$newsitems->setValue(0);
|
||||
// Remove newsitems element if format does not support news.
|
||||
if (!$courseformat->supports_news()) {
|
||||
$mform->removeElement('newsitems');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,7 +433,9 @@ class core_course_external extends external_api {
|
||||
$exceptionparam->courseid = $course->id;
|
||||
throw new moodle_exception('errorcoursecontextnotvalid', 'webservice', '', $exceptionparam);
|
||||
}
|
||||
require_capability('moodle/course:view', $context);
|
||||
if ($course->id != SITEID) {
|
||||
require_capability('moodle/course:view', $context);
|
||||
}
|
||||
|
||||
$courseinfo = array();
|
||||
$courseinfo['id'] = $course->id;
|
||||
@@ -722,6 +724,11 @@ class core_course_external extends external_api {
|
||||
//Note: create_course() core function check shortname, idnumber, category
|
||||
$course['id'] = create_course((object) $course)->id;
|
||||
|
||||
// Create sections that aren't created by core create_course().
|
||||
if (!empty($course['numsections']) && $course['numsections'] > 0) {
|
||||
course_create_sections_if_missing((object)$course, range(0, $course['numsections']));
|
||||
}
|
||||
|
||||
$resultcourses[] = array('id' => $course['id'], 'shortname' => $course['shortname']);
|
||||
}
|
||||
|
||||
|
||||
@@ -678,7 +678,9 @@ abstract class format_base {
|
||||
if (isset($option['type'])) {
|
||||
$mform->setType($optionname, $option['type']);
|
||||
}
|
||||
if (is_null($mform->getElementValue($optionname)) && isset($option['default'])) {
|
||||
if (isset($option['default']) && !array_key_exists($optionname, $mform->_defaultValues)) {
|
||||
// Set defaults for the elements in the form.
|
||||
// Since we call this method after set_data() make sure that we don't override what was already set.
|
||||
$mform->setDefault($optionname, $option['default']);
|
||||
}
|
||||
}
|
||||
|
||||
+55
-1
@@ -55,6 +55,10 @@ define('FIRSTUSEDEXCELROW', 3);
|
||||
define('MOD_CLASS_ACTIVITY', 0);
|
||||
define('MOD_CLASS_RESOURCE', 1);
|
||||
|
||||
define('COURSE_TIMELINE_PAST', 'past');
|
||||
define('COURSE_TIMELINE_INPROGRESS', 'inprogress');
|
||||
define('COURSE_TIMELINE_FUTURE', 'future');
|
||||
|
||||
function make_log_url($module, $url) {
|
||||
switch ($module) {
|
||||
case 'course':
|
||||
@@ -1153,8 +1157,10 @@ function course_delete_module($cmid, $async = false) {
|
||||
|
||||
// Delete events from calendar.
|
||||
if ($events = $DB->get_records('event', array('instance' => $cm->instance, 'modulename' => $modulename))) {
|
||||
$coursecontext = context_course::instance($cm->course);
|
||||
foreach($events as $event) {
|
||||
$calendarevent = calendar_event::load($event->id);
|
||||
$event->context = $coursecontext;
|
||||
$calendarevent = calendar_event::load($event);
|
||||
$calendarevent->delete();
|
||||
}
|
||||
}
|
||||
@@ -2433,6 +2439,14 @@ function update_course($data, $editoroptions = NULL) {
|
||||
}
|
||||
}
|
||||
|
||||
// Set newsitems to 0 if format does not support announcements.
|
||||
if (isset($data->format)) {
|
||||
$newcourseformat = course_get_format((object)['format' => $data->format]);
|
||||
if (!$newcourseformat->supports_news()) {
|
||||
$data->newsitems = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Update with the new data
|
||||
$DB->update_record('course', $data);
|
||||
// make sure the modinfo cache is reset
|
||||
@@ -3912,6 +3926,46 @@ function course_check_updates($course, $tocheck, $filter = array()) {
|
||||
return array($instances, $warnings);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function classifies a course as past, in progress or future.
|
||||
*
|
||||
* This function may incur a DB hit to calculate course completion.
|
||||
* @param stdClass $course Course record
|
||||
* @param stdClass $user User record (optional - defaults to $USER).
|
||||
* @param completion_info $completioninfo Completion record for the user (optional - will be fetched if required).
|
||||
* @return string (one of COURSE_TIMELINE_FUTURE, COURSE_TIMELINE_INPROGRESS or COURSE_TIMELINE_PAST)
|
||||
*/
|
||||
function course_classify_for_timeline($course, $user = null, $completioninfo = null) {
|
||||
global $USER;
|
||||
|
||||
if ($user == null) {
|
||||
$user = $USER;
|
||||
}
|
||||
|
||||
$today = time();
|
||||
// End date past.
|
||||
if (!empty($course->enddate) && $course->enddate < $today) {
|
||||
return COURSE_TIMELINE_PAST;
|
||||
}
|
||||
|
||||
if ($completioninfo == null) {
|
||||
$completioninfo = new completion_info($course);
|
||||
}
|
||||
|
||||
// Course was completed.
|
||||
if ($completioninfo->is_enabled() && $completioninfo->is_course_complete($user->id)) {
|
||||
return COURSE_TIMELINE_PAST;
|
||||
}
|
||||
|
||||
// Start date not reached.
|
||||
if (!empty($course->startdate) && $course->startdate > $today) {
|
||||
return COURSE_TIMELINE_FUTURE;
|
||||
}
|
||||
|
||||
// Everything else is in progress.
|
||||
return COURSE_TIMELINE_INPROGRESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check module updates since a given time.
|
||||
* This function checks for updates in the module config, file areas, completion, grades, comments and ratings.
|
||||
|
||||
@@ -20,16 +20,18 @@
|
||||
// //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
/**
|
||||
* This page display the publication backup form
|
||||
*
|
||||
* @package course
|
||||
* @subpackage publish
|
||||
* @author Jerome Mouneyrac <jerome@mouneyrac.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL
|
||||
* @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This page display the publication backup form
|
||||
*/
|
||||
|
||||
define('NO_OUTPUT_BUFFERING', true);
|
||||
|
||||
require_once('../../config.php');
|
||||
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_plan_builder.class.php');
|
||||
|
||||
@@ -101,7 +101,8 @@ if ($switchrole > 0 && has_capability('moodle/role:switchroles', $context)) {
|
||||
|
||||
foreach ($roles as $key => $role) {
|
||||
$url = new moodle_url('/course/switchrole.php', array('id' => $id, 'switchrole' => $key, 'returnurl' => $returnurl));
|
||||
echo $OUTPUT->container($OUTPUT->single_button($url, $role), 'm-x-3 m-b-1');
|
||||
// Button encodes special characters, apply htmlspecialchars_decode() to avoid double escaping.
|
||||
echo $OUTPUT->container($OUTPUT->single_button($url, htmlspecialchars_decode($role)), 'm-x-3 m-b-1');
|
||||
}
|
||||
|
||||
$url = new moodle_url($returnurl);
|
||||
|
||||
@@ -3689,4 +3689,52 @@ class core_course_courselib_testcase extends advanced_testcase {
|
||||
}
|
||||
$this->assertEquals(2, $count);
|
||||
}
|
||||
|
||||
public function test_classify_course_for_timeline() {
|
||||
global $DB, $CFG;
|
||||
|
||||
require_once($CFG->dirroot.'/completion/criteria/completion_criteria_self.php');
|
||||
|
||||
set_config('enablecompletion', COMPLETION_ENABLED);
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create courses for testing.
|
||||
$generator = $this->getDataGenerator();
|
||||
$future = time() + 3600;
|
||||
$past = time() - 3600;
|
||||
$futurecourse = $generator->create_course(['startdate' => $future]);
|
||||
$pastcourse = $generator->create_course(['startdate' => $past - 60, 'enddate' => $past]);
|
||||
$completedcourse = $generator->create_course(['enablecompletion' => COMPLETION_ENABLED]);
|
||||
$inprogresscourse = $generator->create_course();
|
||||
|
||||
// Set completion rules.
|
||||
$criteriadata = new stdClass();
|
||||
$criteriadata->id = $completedcourse->id;
|
||||
|
||||
// Self completion.
|
||||
$criteriadata->criteria_self = COMPLETION_CRITERIA_TYPE_SELF;
|
||||
$class = 'completion_criteria_self';
|
||||
$criterion = new $class();
|
||||
$criterion->update_config($criteriadata);
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
|
||||
$this->getDataGenerator()->enrol_user($user->id, $futurecourse->id, $studentrole->id);
|
||||
$this->getDataGenerator()->enrol_user($user->id, $pastcourse->id, $studentrole->id);
|
||||
$this->getDataGenerator()->enrol_user($user->id, $completedcourse->id, $studentrole->id);
|
||||
$this->getDataGenerator()->enrol_user($user->id, $inprogresscourse->id, $studentrole->id);
|
||||
|
||||
$this->setUser($user);
|
||||
core_completion_external::mark_course_self_completed($completedcourse->id);
|
||||
$ccompletion = new completion_completion(array('course' => $completedcourse->id, 'userid' => $user->id));
|
||||
$ccompletion->mark_complete();
|
||||
|
||||
// Aggregate the completions.
|
||||
$this->assertEquals(COURSE_TIMELINE_PAST, course_classify_for_timeline($pastcourse));
|
||||
$this->assertEquals(COURSE_TIMELINE_FUTURE, course_classify_for_timeline($futurecourse));
|
||||
$this->assertEquals(COURSE_TIMELINE_PAST, course_classify_for_timeline($completedcourse));
|
||||
$this->assertEquals(COURSE_TIMELINE_INPROGRESS, course_classify_for_timeline($inprogresscourse));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,6 +361,40 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
core_course_external::update_categories($categories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test create_courses numsections
|
||||
*/
|
||||
public function test_create_course_numsections() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Set the required capabilities by the external function.
|
||||
$contextid = context_system::instance()->id;
|
||||
$roleid = $this->assignUserCapability('moodle/course:create', $contextid);
|
||||
$this->assignUserCapability('moodle/course:visibility', $contextid, $roleid);
|
||||
|
||||
$numsections = 10;
|
||||
$category = self::getDataGenerator()->create_category();
|
||||
|
||||
// Create base categories.
|
||||
$course1['fullname'] = 'Test course 1';
|
||||
$course1['shortname'] = 'Testcourse1';
|
||||
$course1['categoryid'] = $category->id;
|
||||
$course1['courseformatoptions'][] = array('name' => 'numsections', 'value' => $numsections);
|
||||
|
||||
$courses = array($course1);
|
||||
|
||||
$createdcourses = core_course_external::create_courses($courses);
|
||||
foreach ($createdcourses as $createdcourse) {
|
||||
$existingsections = $DB->get_records('course_sections', array('course' => $createdcourse['id']));
|
||||
$modinfo = get_fast_modinfo($createdcourse['id']);
|
||||
$sections = $modinfo->get_section_info_all();
|
||||
$this->assertEquals(count($sections), $numsections + 1); // Includes generic section.
|
||||
$this->assertEquals(count($existingsections), $numsections + 1); // Includes generic section.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test create_courses
|
||||
*/
|
||||
@@ -483,7 +517,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
$this->assertEquals($courseinfo->numsections, $course3options['numsections']);
|
||||
$this->assertEquals($courseinfo->coursedisplay, $course3options['coursedisplay']);
|
||||
} else {
|
||||
throw moodle_exception('Unexpected shortname');
|
||||
throw new moodle_exception('Unexpected shortname');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,6 +676,32 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
$this->assertEquals($DB->count_records('course'), count($courses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_courses without capability
|
||||
*/
|
||||
public function test_get_courses_without_capability() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
$this->setUser($this->getDataGenerator()->create_user());
|
||||
|
||||
// No permissions are required to get the site course.
|
||||
$courses = core_course_external::get_courses(array('ids' => [SITEID]));
|
||||
$courses = external_api::clean_returnvalue(core_course_external::get_courses_returns(), $courses);
|
||||
|
||||
$this->assertEquals(1, count($courses));
|
||||
$this->assertEquals('PHPUnit test site', $courses[0]['fullname']);
|
||||
$this->assertEquals('site', $courses[0]['format']);
|
||||
|
||||
// Requesting course without being enrolled or capability to view it will throw an exception.
|
||||
try {
|
||||
core_course_external::get_courses(array('ids' => [$course1->id]));
|
||||
$this->fail('Exception expected');
|
||||
} catch (moodle_exception $e) {
|
||||
$this->assertEquals(1, preg_match('/Course or activity not accessible. \(Not enrolled\)/', $e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test search_courses
|
||||
*/
|
||||
@@ -1137,7 +1197,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
|
||||
$this->assertEquals(0, $courseinfo->newsitems);
|
||||
$this->assertEquals(FORMAT_MOODLE, $courseinfo->summaryformat);
|
||||
} else {
|
||||
throw moodle_exception('Unexpected shortname');
|
||||
throw new moodle_exception('Unexpected shortname');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -661,6 +661,10 @@ DragDrop.prototype = {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (contstraint.get('scrollHeight') === 0 && !contstraint.compareTo(window.document.body)) {
|
||||
contstraint = contstraint.get('parentNode');
|
||||
}
|
||||
|
||||
courseul.all('> li').each(function(li) {
|
||||
this.initCourseListing(li, contstraint);
|
||||
}, this);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -654,6 +654,10 @@ DragDrop.prototype = {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (contstraint.get('scrollHeight') === 0 && !contstraint.compareTo(window.document.body)) {
|
||||
contstraint = contstraint.get('parentNode');
|
||||
}
|
||||
|
||||
courseul.all('> li').each(function(li) {
|
||||
this.initCourseListing(li, contstraint);
|
||||
}, this);
|
||||
|
||||
Vendored
+4
@@ -69,6 +69,10 @@ DragDrop.prototype = {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (contstraint.get('scrollHeight') === 0 && !contstraint.compareTo(window.document.body)) {
|
||||
contstraint = contstraint.get('parentNode');
|
||||
}
|
||||
|
||||
courseul.all('> li').each(function(li) {
|
||||
this.initCourseListing(li, contstraint);
|
||||
}, this);
|
||||
|
||||
@@ -42,11 +42,9 @@ class writer extends \core\dataformat\base {
|
||||
public $extension = ".html";
|
||||
|
||||
/**
|
||||
* Write the start of the format
|
||||
*
|
||||
* @param array $columns
|
||||
* Write the start of the output
|
||||
*/
|
||||
public function write_header($columns) {
|
||||
public function start_output() {
|
||||
echo "<!DOCTYPE html><html>";
|
||||
echo \html_writer::tag('title', $this->filename);
|
||||
echo "<style>
|
||||
@@ -75,9 +73,16 @@ table {
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<table border=1 cellspacing=0 cellpadding=3>
|
||||
";
|
||||
<body>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the start of the sheet we will be adding data to.
|
||||
*
|
||||
* @param array $columns
|
||||
*/
|
||||
public function start_sheet($columns) {
|
||||
echo "<table border=1 cellspacing=0 cellpadding=3>";
|
||||
echo \html_writer::start_tag('tr');
|
||||
foreach ($columns as $k => $v) {
|
||||
echo \html_writer::tag('th', $v);
|
||||
@@ -100,12 +105,18 @@ table {
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the end of the format
|
||||
* Write the end of the sheet containing the data.
|
||||
*
|
||||
* @param array $columns
|
||||
*/
|
||||
public function write_footer($columns) {
|
||||
echo "</table></body></html>";
|
||||
public function close_sheet($columns) {
|
||||
echo "</table>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the end of the sheet containing the data.
|
||||
*/
|
||||
public function close_output() {
|
||||
echo "</body></html>";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,12 +41,31 @@ class writer extends \core\dataformat\base {
|
||||
/** @var $extension */
|
||||
public $extension = ".json";
|
||||
|
||||
/** @var $sheetstarted */
|
||||
public $sheetstarted = false;
|
||||
|
||||
/** @var $sheetdatadded */
|
||||
public $sheetdatadded = false;
|
||||
|
||||
/**
|
||||
* Write the start of the format
|
||||
* Write the start of the file.
|
||||
*/
|
||||
public function start_output() {
|
||||
echo "[";
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the start of the sheet we will be adding data to.
|
||||
*
|
||||
* @param array $columns
|
||||
*/
|
||||
public function write_header($columns) {
|
||||
public function start_sheet($columns) {
|
||||
if ($this->sheetstarted) {
|
||||
echo ",";
|
||||
} else {
|
||||
$this->sheetstarted = true;
|
||||
}
|
||||
$this->sheetdatadded = false;
|
||||
echo "[";
|
||||
}
|
||||
|
||||
@@ -57,19 +76,28 @@ class writer extends \core\dataformat\base {
|
||||
* @param int $rownum
|
||||
*/
|
||||
public function write_record($record, $rownum) {
|
||||
if ($rownum) {
|
||||
if ($this->sheetdatadded) {
|
||||
echo ",";
|
||||
}
|
||||
|
||||
echo json_encode($record);
|
||||
|
||||
$this->sheetdatadded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the end of the format
|
||||
* Write the end of the sheet containing the data.
|
||||
*
|
||||
* @param array $columns
|
||||
*/
|
||||
public function write_footer($columns) {
|
||||
public function close_sheet($columns) {
|
||||
echo "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the end of the file.
|
||||
*/
|
||||
public function close_output() {
|
||||
echo "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
This files describes API changes in /dataformat/ download system,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 3.2.4 ===
|
||||
|
||||
* In order to allow multiple sheets in an exported file the functions write_header() and write_footer() have
|
||||
been removed from core dataformat plugins and have been replaced.
|
||||
- write_header() has been replaced with the two functions start_output() and start_sheet().
|
||||
- write_footer() has been replaced with the two functions close_output() and close_sheet().
|
||||
For backwards compatibility write_header() and write_footer() will continue to work.
|
||||
|
||||
=== 3.1 ===
|
||||
* Added new plugin system with low memory support for csv, ods, xls and json
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ class enrol_guest_enrol_form extends moodleform {
|
||||
$heading = $plugin->get_instance_name($instance);
|
||||
$mform->addElement('header', 'guestheader', $heading);
|
||||
|
||||
$mform->addElement('passwordunmask', 'guestpassword', get_string('password', 'enrol_guest'));
|
||||
$mform->addElement('password', 'guestpassword', get_string('password', 'enrol_guest'));
|
||||
|
||||
$this->add_action_buttons(false, get_string('submit'));
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ $string['course_summary'] = 'Optional: LDAP attribute to get the summary from';
|
||||
$string['course_summary_key'] = 'Summary';
|
||||
$string['course_summary_updateonsync'] = 'Update summary during synchronisation script';
|
||||
$string['course_summary_updateonsync_key'] = 'Update summary';
|
||||
$string['createcourseextid'] = 'CREATE User enrolled to a nonexistant course \'{$a->courseextid}\'';
|
||||
$string['createnotcourseextid'] = 'User enrolled to a nonexistant course \'{$a->courseextid}\'';
|
||||
$string['createcourseextid'] = 'CREATE User enrolled to a non-existing course \'{$a->courseextid}\'';
|
||||
$string['createnotcourseextid'] = 'User enrolled to a non-existing course \'{$a->courseextid}\'';
|
||||
$string['creatingcourse'] = 'Creating course \'{$a}\'...';
|
||||
$string['duplicateshortname'] = "Course creation failed. Duplicate short name. Skipping course with idnumber '{\$a->idnumber}'...";
|
||||
$string['editlock'] = 'Lock value';
|
||||
|
||||
+3
-3
@@ -132,7 +132,7 @@ class enrol_ldap_plugin extends enrol_plugin {
|
||||
*/
|
||||
public function can_hide_show_instance($instance) {
|
||||
$context = context_course::instance($instance->courseid);
|
||||
return has_capability('enrol/ldap:config', $context);
|
||||
return has_capability('enrol/ldap:manage', $context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -328,7 +328,7 @@ class enrol_ldap_plugin extends enrol_plugin {
|
||||
return;
|
||||
}
|
||||
|
||||
$ldap_pagedresults = ldap_paged_results_supported($this->get_config('ldap_version'));
|
||||
$ldap_pagedresults = ldap_paged_results_supported($this->get_config('ldap_version'), $this->ldapconnection);
|
||||
|
||||
// we may need a lot of memory here
|
||||
core_php_time_limit::raise();
|
||||
@@ -761,7 +761,7 @@ class enrol_ldap_plugin extends enrol_plugin {
|
||||
|
||||
// Get all contexts and look for first matching user
|
||||
$ldap_contexts = explode(';', $ldap_contexts);
|
||||
$ldap_pagedresults = ldap_paged_results_supported($this->get_config('ldap_version'));
|
||||
$ldap_pagedresults = ldap_paged_results_supported($this->get_config('ldap_version'), $this->ldapconnection);
|
||||
foreach ($ldap_contexts as $context) {
|
||||
$context = trim($context);
|
||||
if (empty($context)) {
|
||||
|
||||
@@ -78,7 +78,7 @@ class enrol_self_enrol_form extends moodleform {
|
||||
|
||||
if ($instance->password) {
|
||||
// Change the id of self enrolment key input as there can be multiple self enrolment methods.
|
||||
$mform->addElement('passwordunmask', 'enrolpassword', get_string('password', 'enrol_self'),
|
||||
$mform->addElement('password', 'enrolpassword', get_string('password', 'enrol_self'),
|
||||
array('id' => 'enrolpassword_'.$instance->id));
|
||||
$context = context_course::instance($this->instance->courseid);
|
||||
$keyholders = get_users_by_capability($context, 'enrol/self:holdkey', user_picture::fields('u'));
|
||||
|
||||
@@ -280,6 +280,62 @@ class core_enrollib_testcase extends advanced_testcase {
|
||||
$this->assertEquals($sharedcourse->id, $course1->id);
|
||||
}
|
||||
|
||||
public function test_enrol_get_shared_courses_different_methods() {
|
||||
global $DB, $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/enrol/self/externallib.php');
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$user3 = $this->getDataGenerator()->create_user();
|
||||
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
|
||||
// Enrol user1 and user2 in course1 with a different enrolment methode.
|
||||
// Add self enrolment method for course1.
|
||||
$selfplugin = enrol_get_plugin('self');
|
||||
$this->assertNotEmpty($selfplugin);
|
||||
|
||||
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
|
||||
$this->assertNotEmpty($studentrole);
|
||||
|
||||
$instance1id = $selfplugin->add_instance($course1, array('status' => ENROL_INSTANCE_ENABLED,
|
||||
'name' => 'Test instance 1',
|
||||
'customint6' => 1,
|
||||
'roleid' => $studentrole->id));
|
||||
|
||||
$instance1 = $DB->get_record('enrol', array('id' => $instance1id), '*', MUST_EXIST);
|
||||
|
||||
self::setUser($user2);
|
||||
// Self enrol me (user2).
|
||||
$result = enrol_self_external::enrol_user($course1->id);
|
||||
|
||||
// Enrol user1 manually.
|
||||
$this->getDataGenerator()->enrol_user($user1->id, $course1->id, null, 'manual');
|
||||
|
||||
$course2 = $this->getDataGenerator()->create_course();
|
||||
$this->getDataGenerator()->enrol_user($user1->id, $course2->id);
|
||||
|
||||
$course3 = $this->getDataGenerator()->create_course();
|
||||
$this->getDataGenerator()->enrol_user($user2->id, $course3->id);
|
||||
|
||||
// Test that user1 and user2 have courses in common.
|
||||
$this->assertTrue(enrol_get_shared_courses($user1, $user2, false, true));
|
||||
// Test that user1 and user3 have no courses in common.
|
||||
$this->assertFalse(enrol_get_shared_courses($user1, $user3, false, true));
|
||||
|
||||
// Test retrieving the courses in common.
|
||||
$sharedcourses = enrol_get_shared_courses($user1, $user2, true);
|
||||
|
||||
// Only should be one shared course.
|
||||
$this->assertCount(1, $sharedcourses);
|
||||
$sharedcourse = array_shift($sharedcourses);
|
||||
// It should be course 1.
|
||||
$this->assertEquals($sharedcourse->id, $course1->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test user enrolment created event.
|
||||
*/
|
||||
@@ -422,4 +478,58 @@ class core_enrollib_testcase extends advanced_testcase {
|
||||
$this->assertEquals('self', $event->other['enrol']);
|
||||
$this->assertEventContextNotUsed($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that timemodified field was updated after modification of user enrollment
|
||||
*/
|
||||
public function test_enrollment_update_timemodified() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
$datagen = $this->getDataGenerator();
|
||||
|
||||
/** @var enrol_manual_plugin $manualplugin */
|
||||
$manualplugin = enrol_get_plugin('manual');
|
||||
$this->assertNotNull($manualplugin);
|
||||
|
||||
$studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST);
|
||||
$course = $datagen->create_course();
|
||||
$user = $datagen->create_user();
|
||||
|
||||
$instanceid = null;
|
||||
$instances = enrol_get_instances($course->id, true);
|
||||
foreach ($instances as $inst) {
|
||||
if ($inst->enrol == 'manual') {
|
||||
$instanceid = (int)$inst->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (empty($instanceid)) {
|
||||
$instanceid = $manualplugin->add_default_instance($course);
|
||||
if (empty($instanceid)) {
|
||||
$instanceid = $manualplugin->add_instance($course);
|
||||
}
|
||||
}
|
||||
$this->assertNotNull($instanceid);
|
||||
|
||||
$instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST);
|
||||
$manualplugin->enrol_user($instance, $user->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
|
||||
$userenrolorig = (int)$DB->get_field(
|
||||
'user_enrolments',
|
||||
'timemodified',
|
||||
['enrolid' => $instance->id, 'userid' => $user->id],
|
||||
MUST_EXIST
|
||||
);
|
||||
$this->waitForSecond();
|
||||
$this->waitForSecond();
|
||||
$manualplugin->update_user_enrol($instance, $user->id, ENROL_USER_SUSPENDED);
|
||||
$userenrolpost = (int)$DB->get_field(
|
||||
'user_enrolments',
|
||||
'timemodified',
|
||||
['enrolid' => $instance->id, 'userid' => $user->id],
|
||||
MUST_EXIST
|
||||
);
|
||||
|
||||
$this->assertGreaterThan($userenrolorig, $userenrolpost);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ function xmldb_filter_mathjaxloader_upgrade($oldversion) {
|
||||
|
||||
$dbman = $DB->get_manager();
|
||||
|
||||
require_once($CFG->dirroot . '/filter/mathjaxloader/db/upgradelib.php');
|
||||
|
||||
if ($oldversion < 2014081100) {
|
||||
|
||||
$sslcdnurl = get_config('filter_mathjaxloader', 'httpsurl');
|
||||
@@ -157,6 +159,42 @@ MathJax.Hub.Config({
|
||||
}
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
if ($oldversion < 2016120501) {
|
||||
$httpsurl = get_config('filter_mathjaxloader', 'httpsurl');
|
||||
$newcdnurl = filter_mathjaxloader_upgrade_cdn_cloudflare($httpsurl, false);
|
||||
|
||||
set_config('httpsurl', $newcdnurl, 'filter_mathjaxloader');
|
||||
|
||||
$mathjaxconfig = get_config('filter_mathjaxloader', 'mathjaxconfig');
|
||||
if (strpos($mathjaxconfig, 'MathJax.Ajax.config.path') === false) {
|
||||
$newconfig = 'MathJax.Ajax.config.path["Contrib"] = "{wwwroot}/filter/mathjaxloader/contrib";' . "\n";
|
||||
$newconfig .= $mathjaxconfig;
|
||||
|
||||
set_config('mathjaxconfig', $newconfig, 'filter_mathjaxloader');
|
||||
}
|
||||
|
||||
upgrade_plugin_savepoint(true, 2016120501, 'filter', 'mathjaxloader');
|
||||
}
|
||||
if ($oldversion < 2016120502) {
|
||||
|
||||
$httpsurl = get_config('filter_mathjaxloader', 'httpsurl');
|
||||
if ($httpsurl === "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js") {
|
||||
set_config('httpsurl', 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js', 'filter_mathjaxloader');
|
||||
}
|
||||
|
||||
$mathjaxconfig = get_config('filter_mathjaxloader', 'mathjaxconfig');
|
||||
|
||||
if (strpos($mathjaxconfig, 'MathJax.Ajax.config.path') !== false) {
|
||||
// Now we need to remove this config again because mathjax 2.7.1 supports the extensions on the CDN.
|
||||
$configtoremove = 'MathJax.Ajax.config.path["Contrib"] = "{wwwroot}/filter/mathjaxloader/contrib";';
|
||||
|
||||
$mathjaxconfig = str_replace($configtoremove, '', $mathjaxconfig);
|
||||
|
||||
set_config('mathjaxconfig', $mathjaxconfig, 'filter_mathjaxloader');
|
||||
}
|
||||
|
||||
upgrade_plugin_savepoint(true, 2016120502, 'filter', 'mathjaxloader');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Random functions for mathjax upgrades.
|
||||
*
|
||||
* @package filter_mathjaxloader
|
||||
* @copyright 2017 Damyon Wiese (damyon@moodle.com)
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* This function takes an existing mathjax url and, if it was using the standard mathjax cdn,
|
||||
* upgrades it to use the cloudflare matchjax cdn (because the standard one is shutting down).
|
||||
* @param string $mathjaxurl - The current url.
|
||||
* @param boolean $httponly - Use http instead of https - really only for 3.1 upgrades.
|
||||
* @return string The new url.
|
||||
*/
|
||||
function filter_mathjaxloader_upgrade_cdn_cloudflare($mathjaxurl, $httponly = false) {
|
||||
$newcdnurl = $mathjaxurl;
|
||||
$cdnroot = 'https://cdn.mathjax.org/mathjax/';
|
||||
if ($httponly) {
|
||||
$cdnroot = 'http://cdn.mathjax.org/mathjax/';
|
||||
}
|
||||
$usingcdn = strpos($mathjaxurl, $cdnroot) === 0;
|
||||
if ($usingcdn) {
|
||||
$majorversion = substr($mathjaxurl, strlen($cdnroot), 3);
|
||||
$latestversion = '2.7.0';
|
||||
if ($majorversion == '2.6') {
|
||||
$latestversion = '2.6.1';
|
||||
} else if ($majorversion == '2.5') {
|
||||
$latestversion = '2.5.3';
|
||||
}
|
||||
|
||||
$offset = strpos($mathjaxurl, '/', strlen($cdnroot));
|
||||
if ($offset === false) {
|
||||
return $newcdnurl;
|
||||
}
|
||||
|
||||
$endofurl = substr($mathjaxurl, $offset + 1);
|
||||
|
||||
$newcdnbase = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/';
|
||||
if ($httponly) {
|
||||
$newcdnbase = 'http://cdnjs.cloudflare.com/ajax/libs/mathjax/';
|
||||
}
|
||||
|
||||
$newcdnurl = $newcdnbase . $latestversion . '/' . $endofurl;
|
||||
}
|
||||
|
||||
return $newcdnurl;
|
||||
}
|
||||
@@ -111,6 +111,9 @@ class filter_mathjaxloader extends moodle_text_filter {
|
||||
$page->requires->js_module($moduleconfig);
|
||||
|
||||
$config = get_config('filter_mathjaxloader', 'mathjaxconfig');
|
||||
$wwwroot = new moodle_url('/');
|
||||
|
||||
$config = str_replace('{wwwroot}', $wwwroot->out(true), $config);
|
||||
|
||||
$params = array('mathjaxconfig' => $config, 'lang' => $lang);
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ if ($ADMIN->fulltree) {
|
||||
$item = new admin_setting_configtext('filter_mathjaxloader/httpsurl',
|
||||
new lang_string('httpsurl', 'filter_mathjaxloader'),
|
||||
new lang_string('httpsurl_help', 'filter_mathjaxloader'),
|
||||
'https://cdn.mathjax.org/mathjax/2.7-latest/MathJax.js',
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js',
|
||||
PARAM_RAW);
|
||||
$settings->add($item);
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Unit test for the upgrade MathJax code.
|
||||
*
|
||||
* @package filter_mathjaxloader
|
||||
* @copyright 2017 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/filter/mathjaxloader/db/upgradelib.php');
|
||||
|
||||
/**
|
||||
* Unit test for the upgrade MathJax code.
|
||||
*
|
||||
* Test the functions in upgradelib.php
|
||||
*
|
||||
* @copyright 2017 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class filter_mathjax_upgradelib_testcase extends advanced_testcase {
|
||||
|
||||
public function test_upgradelib() {
|
||||
$current = 'https://cdn.mathjax.org/mathjax/2.7-latest/MathJax.js?...';
|
||||
$expected = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?...';
|
||||
$this->assertEquals($expected, filter_mathjaxloader_upgrade_cdn_cloudflare($current));
|
||||
|
||||
$current = 'http://cdn.mathjax.org/mathjax/2.7-latest/MathJax.js?...';
|
||||
$expected = 'http://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?...';
|
||||
$this->assertEquals($expected, filter_mathjaxloader_upgrade_cdn_cloudflare($current, true));
|
||||
|
||||
$current = 'http://cdn.mathjax.org/mathjax/2.7-latest/MathJax.js?...';
|
||||
$expected = 'http://cdn.mathjax.org/mathjax/2.7-latest/MathJax.js?...';
|
||||
$this->assertEquals($expected, filter_mathjaxloader_upgrade_cdn_cloudflare($current, false));
|
||||
|
||||
$current = 'https://cdn.mathjax.org/mathjax/2.7-latest/MathJax.js?...';
|
||||
$expected = 'https://cdn.mathjax.org/mathjax/2.7-latest/MathJax.js?...';
|
||||
$this->assertEquals($expected, filter_mathjaxloader_upgrade_cdn_cloudflare($current, true));
|
||||
|
||||
$current = 'https://cdn.mathjax.org/mathjax/2.6-latest/MathJax.js?...';
|
||||
$expected = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.6.1/MathJax.js?...';
|
||||
$this->assertEquals($expected, filter_mathjaxloader_upgrade_cdn_cloudflare($current));
|
||||
|
||||
$current = 'https://cdn.mathjax.org/mathjax/2.5-latest/MathJax.js?...';
|
||||
$expected = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.5.3/MathJax.js?...';
|
||||
$this->assertEquals($expected, filter_mathjaxloader_upgrade_cdn_cloudflare($current));
|
||||
|
||||
// Dont touch non https links.
|
||||
$current = 'http://cdn.mathjax.org/mathjax/2.5-latest/MathJax.js?...';
|
||||
$expected = 'http://cdn.mathjax.org/mathjax/2.5-latest/MathJax.js?...';
|
||||
$this->assertEquals($expected, filter_mathjaxloader_upgrade_cdn_cloudflare($current));
|
||||
|
||||
// Dont touch non local links.
|
||||
$current = 'https://mylocalmirror/mathjax/2.7-latest/MathJax.js?...';
|
||||
$expected = 'https://mylocalmirror/mathjax/2.7-latest/MathJax.js?...';
|
||||
$this->assertEquals($expected, filter_mathjaxloader_upgrade_cdn_cloudflare($current));
|
||||
|
||||
// Try some unexpected things.
|
||||
$current = '';
|
||||
$expected = '';
|
||||
$this->assertEquals($expected, filter_mathjaxloader_upgrade_cdn_cloudflare($current));
|
||||
|
||||
// Try some unexpected things.
|
||||
$current = 'https://cdn.mathjax.org/mathjax/2.7-latest/MathJax.js';
|
||||
$expected = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js';
|
||||
$this->assertEquals($expected, filter_mathjaxloader_upgrade_cdn_cloudflare($current));
|
||||
|
||||
// Try some unexpected things.
|
||||
$current = 'https://cdn.mathjax.org/mathjax/2.7-latest';
|
||||
$expected = 'https://cdn.mathjax.org/mathjax/2.7-latest';
|
||||
$this->assertEquals($expected, filter_mathjaxloader_upgrade_cdn_cloudflare($current));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0"?>
|
||||
<libraries>
|
||||
<!-- Accessibility extensions for mathjax were briefly included - and then removed again -->
|
||||
</libraries>
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2016120500;
|
||||
$plugin->version = 2016120502;
|
||||
$plugin->requires = 2016112900; // Requires this Moodle version.
|
||||
$plugin->component= 'filter_mathjaxloader';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user