Compare commits
287 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e4341402a | |||
| 0a5af1649f | |||
| b194f1a2e3 | |||
| 14ecaeea08 | |||
| c28de2e556 | |||
| 706571e94f | |||
| 0801d7dc95 | |||
| 77da99fe77 | |||
| c88b34c0fa | |||
| 23dbc35541 | |||
| e9609517d2 | |||
| efc9c7572b | |||
| 05263df3b0 | |||
| dc30c8fa2f | |||
| 8b3ee7d311 | |||
| 5823f7e1bd | |||
| 4dfa466691 | |||
| 226ca38be3 | |||
| 145e6deee7 | |||
| d53a5789a8 | |||
| 640534676c | |||
| 49371ca433 | |||
| 5697a42fc2 | |||
| 0ba4ffe353 | |||
| 31bae37249 | |||
| 2423a989fc | |||
| 67103939ac | |||
| 4f91eb0ba1 | |||
| 07cb3212c5 | |||
| 550e03463b | |||
| 4f2c5e18c0 | |||
| 73aad88f63 | |||
| 88d7acff69 | |||
| 59f5f4a9df | |||
| 14509286e4 | |||
| 6b661ab0a2 | |||
| 57cb067173 | |||
| 65e5e9f454 | |||
| 7984ae3581 | |||
| f786e59224 | |||
| c46c38d355 | |||
| 67c112bf9c | |||
| 61812e3dd8 | |||
| eaeed5b281 | |||
| d259cafd3c | |||
| e632f4ece5 | |||
| aff28d6243 | |||
| 653ec0d0b0 | |||
| 62a71e4a17 | |||
| 35657809ed | |||
| c61c5009a8 | |||
| fe9e229248 | |||
| db500a32a1 | |||
| 75ead1e8cf | |||
| c7c9f4a46e | |||
| 8e9c8f6583 | |||
| 037bf82652 | |||
| 5341d76031 | |||
| e71b162d09 | |||
| 58099bd24d | |||
| c7da8264d0 | |||
| ca47e80375 | |||
| 40e7ac2c70 | |||
| d7a2d1a2c8 | |||
| d971e7dffe | |||
| 56855a2842 | |||
| 7563339608 | |||
| 89d85f5a4b | |||
| 74823e4e45 | |||
| 7a0e1cd70c | |||
| 4534c7b531 | |||
| 16f2fe5514 | |||
| eee0d699c3 | |||
| 0d74c11cf7 | |||
| 6aebb4375f | |||
| 32f2e90522 | |||
| 8e62bc79c7 | |||
| c36c507277 | |||
| 813f289b60 | |||
| e318be3772 | |||
| a67ff779d5 | |||
| 222a87ae98 | |||
| 8d0dd90520 | |||
| cccb1a6d21 | |||
| 3d8a2c5cd9 | |||
| ebe0cf3ed4 | |||
| 9938165bb8 | |||
| 101271fd28 | |||
| 4ca3991e56 | |||
| f6736dedd1 | |||
| 5be425cf9e | |||
| 0f0b45fd59 | |||
| f05e2e6690 | |||
| b0b68a0bd4 | |||
| 7abe668bd6 | |||
| b31f3097c2 | |||
| 82b3050e72 | |||
| 153f74956d | |||
| 8b017ea78c | |||
| cea093be87 | |||
| 970c74b280 | |||
| 04e5ef2a3e | |||
| b2a90870ce | |||
| ae60722b14 | |||
| 26277438ed | |||
| 17eb11f801 | |||
| 3ae9ed890f | |||
| 0ff0caefc0 | |||
| a5efab81c9 | |||
| bc84e4e4ae | |||
| e70d1e66d6 | |||
| e256446c75 | |||
| e4780bf151 | |||
| 4b8d403606 | |||
| 8a32c0fdb3 | |||
| a27fa5b50e | |||
| 3cf5c5152e | |||
| c484b0574e | |||
| dec00f6a53 | |||
| d51518e24d | |||
| 94925c38d0 | |||
| 7abd8de776 | |||
| a358927e9e | |||
| 49e5b8438c | |||
| 2b026b44f7 | |||
| 63bfa5afdb | |||
| d08cd28dae | |||
| 5d7d2c0a45 | |||
| 9eb46e7d75 | |||
| 481220cb4c | |||
| d47b680074 | |||
| 76b6407cb8 | |||
| ca1e040bbf | |||
| 1312c844a1 | |||
| a21c3208ea | |||
| a1692f6362 | |||
| 9bcb6e1bec | |||
| 192a355707 | |||
| 3709386ed5 | |||
| c159483796 | |||
| 9bdff1611b | |||
| ca18b0c33d | |||
| 0fb6f059a9 | |||
| 7dc35ed9d2 | |||
| 481a5ce58b | |||
| af153671de | |||
| 48b929667e | |||
| 6dc00c8423 | |||
| 290ec233f5 | |||
| 56aa74ed3c | |||
| fe3d722d69 | |||
| 792560a608 | |||
| 7041dc6f8a | |||
| 57fb48743f | |||
| a9712dacbe | |||
| c0f4b8b3c1 | |||
| 9a9b9e53f0 | |||
| e1d15194bc | |||
| 5b2f32ca92 | |||
| a53a439c08 | |||
| 2c12e7f534 | |||
| 7255306cc8 | |||
| b545e84294 | |||
| 72ebc73897 | |||
| 856e1c24c2 | |||
| 5b1dffd5b0 | |||
| 6ebff58d8c | |||
| 54bee25c65 | |||
| 355fefbe71 | |||
| 11a24a823b | |||
| 75797500ae | |||
| e2b8d30052 | |||
| 571e862135 | |||
| 1e9ba1916b | |||
| ddd849e1d5 | |||
| 760ed1dfde | |||
| 95a6fe8b05 | |||
| 0fca28e197 | |||
| cc5235bb77 | |||
| 1099b7ce1d | |||
| dc403b8386 | |||
| 23bec87007 | |||
| 924db1a0a6 | |||
| f8326931a1 | |||
| 0a98628841 | |||
| d72a096797 | |||
| a6379acaf3 | |||
| e002d8c2db | |||
| 2b7268d645 | |||
| 6e34867b93 | |||
| 9a6e95557f | |||
| 88daff0ff3 | |||
| 3309800768 | |||
| adbba36a63 | |||
| 37ff4578be | |||
| cbc3742d71 | |||
| 56e407f8a0 | |||
| 362e7721f5 | |||
| 53fd95f715 | |||
| 078aaae0cf | |||
| 4f66cd826f | |||
| 028f587670 | |||
| 0b2e862c7a | |||
| 8db0bcc018 | |||
| bb6a2082f8 | |||
| dfac161651 | |||
| f41c1f82bc | |||
| 443f993467 | |||
| 0e72aac68a | |||
| 4cd564dd65 | |||
| c6c462903e | |||
| be4a2e7773 | |||
| 283140134b | |||
| c779c4fa0b | |||
| 4577aa88fd | |||
| 3add309d96 | |||
| 1c2a935975 | |||
| 574aa817c1 | |||
| 98a7bb9cd5 | |||
| f35ff40f4a | |||
| 2529f11ce3 | |||
| 26f4cf3ad8 | |||
| 23ec9e80b4 | |||
| 8710174991 | |||
| e399833263 | |||
| 59411b45f6 | |||
| 767622050e | |||
| 6696cff321 | |||
| f59741cc6c | |||
| 524724c642 | |||
| ca31084da2 | |||
| 52ecbf8f4c | |||
| d9c2abaa65 | |||
| 30613bca8d | |||
| cb3bad1d50 | |||
| e4eab531bf | |||
| 068d573375 | |||
| df78e4c8ab | |||
| 5fbe152a58 | |||
| 7fe005f6d1 | |||
| 2a615a6031 | |||
| cb065a2d2f | |||
| e0adce5360 | |||
| 613db5afcb | |||
| b38538fbdf | |||
| f96167c02e | |||
| c708549705 | |||
| d14281e215 | |||
| 19f6ca9457 | |||
| 7b4eb6082f | |||
| 8b8fbc6b84 | |||
| 906b385784 | |||
| e13e5a42bc | |||
| 1fcfc991b9 | |||
| a4eaa35e23 | |||
| df6749b03b | |||
| 6fc4a6c17a | |||
| f13c34e4a9 | |||
| 0899f6b9f9 | |||
| b68abdc7c2 | |||
| 7e68b9a0af | |||
| 63752139c4 | |||
| 67803cb430 | |||
| 54b24b8a24 | |||
| 3e97f327cf | |||
| e91ffe1531 | |||
| c4d5303ce0 | |||
| 0712ef7f54 | |||
| ddf6c958f8 | |||
| fbf216799c | |||
| f8b0ee68bf | |||
| 069683984a | |||
| 7a7dd2ff1f | |||
| 8a0ed98b81 | |||
| ce904a961d | |||
| 5ff898804f | |||
| 3bb50a471c | |||
| 660182a172 | |||
| e8f0664d53 | |||
| cb495b995c | |||
| c37bbda3ef | |||
| 81432e74f4 | |||
| d231bf1272 | |||
| 1aee3e9394 | |||
| 9362f598dd | |||
| 761ac5a0aa | |||
| 32ec2da151 |
+27
-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.
|
||||
@@ -18,6 +18,10 @@ php:
|
||||
|
||||
addons:
|
||||
postgresql: "9.3"
|
||||
packages:
|
||||
- mysql-server-5.6
|
||||
- mysql-client-core-5.6
|
||||
- mysql-client-5.6
|
||||
|
||||
services:
|
||||
- redis-server
|
||||
@@ -73,6 +77,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.3
|
||||
fi
|
||||
- >
|
||||
if [ "$TASK" = 'PHPUNIT' ];
|
||||
then
|
||||
|
||||
+1
-1
@@ -338,7 +338,7 @@ module.exports = function(grunt) {
|
||||
opts: {stdio: 'inherit', env: process.env}
|
||||
}, function(error, result, code) {
|
||||
// Propagate the exit code.
|
||||
done(code);
|
||||
done(code === 0);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -193,8 +193,8 @@ function print_report_tree($contextid, $contexts, $systemcontext, $fullname, $al
|
||||
$strgoto = get_string('gotoassignroles', 'core_role', $a);
|
||||
$strcheck = get_string('checkuserspermissionshere', 'core_role', $a);
|
||||
}
|
||||
echo ' <a title="' . $strgoto . '" href="' . $raurl . '">' . $OUTPUT->pix_icon('t/edit', 'core', $stredit) . '</a> ';
|
||||
echo ' <a title="' . $strcheck . '" href="' . $churl . '">' . $OUTPUT->pix_icon('t/preview', 'core', $strcheckpermissions) . '</a> ';
|
||||
echo ' <a title="' . $strgoto . '" href="' . $raurl . '">' . $OUTPUT->pix_icon('t/edit', $stredit) . '</a> ';
|
||||
echo ' <a title="' . $strcheck . '" href="' . $churl . '">' . $OUTPUT->pix_icon('t/preview', $strcheckpermissions) . '</a> ';
|
||||
echo "</p>\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -232,7 +232,7 @@ if ($hassiteconfig) {
|
||||
|
||||
// Convert plugins.
|
||||
$ADMIN->add('modules', new admin_category('fileconverterplugins', new lang_string('type_fileconverter_plural', 'plugin')));
|
||||
$temp = new admin_settingpage('managefileconverterplugins', new lang_string('type_fileconverter', 'plugin'));
|
||||
$temp = new admin_settingpage('managefileconverterplugins', new lang_string('type_fileconvertermanage', 'plugin'));
|
||||
$temp->add(new admin_setting_manage_fileconverter_plugins());
|
||||
$ADMIN->add('fileconverterplugins', $temp);
|
||||
|
||||
|
||||
@@ -55,9 +55,6 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
|
||||
$optionalsubsystems->add(new admin_setting_configcheckbox('enableglobalsearch', new lang_string('enableglobalsearch', 'admin'),
|
||||
new lang_string('enableglobalsearch_desc', 'admin'), 0, 1, 0));
|
||||
|
||||
$choices = array();
|
||||
$choices[0] = new lang_string('no');
|
||||
$choices[1] = new lang_string('yes');
|
||||
$optionalsubsystems->add(new admin_setting_configselect('allowstealth', new lang_string('allowstealthmodules'),
|
||||
new lang_string('allowstealthmodules_help'), 0, $choices));
|
||||
$optionalsubsystems->add(new admin_setting_configcheckbox('allowstealth', new lang_string('allowstealthmodules'),
|
||||
new lang_string('allowstealthmodules_help'), 0, 1, 0));
|
||||
}
|
||||
|
||||
@@ -44,5 +44,8 @@ function xmldb_tool_customlang_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -48,5 +48,8 @@ function xmldb_tool_log_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -42,5 +42,8 @@ function xmldb_logstore_database_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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.3.1 ===
|
||||
* 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 = 2017051500; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2017051501; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2017050500; // Requires this Moodle version.
|
||||
$plugin->component = 'logstore_database'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -59,5 +59,8 @@ function xmldb_logstore_standard_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -97,5 +97,8 @@ function xmldb_tool_monitor_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2017021300, 'tool', 'monitor');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -72,15 +72,15 @@ $string['issuerimage'] = 'Logo URL';
|
||||
$string['issuerloginparams'] = 'Additional parameters included in a login request.';
|
||||
$string['issuerloginparams_help'] = 'Some systems require additional parameters for a login request in order to read the user\'s basic profile.';
|
||||
$string['issuerloginparamsoffline'] = 'Additional parameters included in a login request for offline access.';
|
||||
$string['issuerloginparamsoffline_help'] = 'Each OAuth system defines a different way to request offline access. E.g. Google requires the additional params: "access_type=offline&prompt=consent" these parameters should be in url query parameter format.';
|
||||
$string['issuerloginscopes_help'] = 'Some systems require additional scopes for a login request in order to read the users basic profile. The standard scopes for an OpenID Connect compliant system are "openid profile email".';
|
||||
$string['issuerloginscopesoffline_help'] = 'Each OAuth system defines a different way to request offline access. E.g. Microsoft requires an additional scope "offline_access"';
|
||||
$string['issuerloginparamsoffline_help'] = 'Each OAuth system defines a different way to request offline access. E.g. Google requires the additional params: "access_type=offline&prompt=consent". These parameters should be in URL query parameter format.';
|
||||
$string['issuerloginscopes_help'] = 'Some systems require additional scopes for a login request in order to read the user\'s basic profile. The standard scopes for an OpenID Connect compliant system are "openid profile email".';
|
||||
$string['issuerloginscopesoffline_help'] = 'Each OAuth system defines a different way to request offline access. E.g. Microsoft requires an additional scope "offline_access".';
|
||||
$string['issuerloginscopesoffline'] = 'Scopes included in a login request for offline access.';
|
||||
$string['issuerloginscopes'] = 'Scopes included in a login request.';
|
||||
$string['issuername_help'] = 'Name of the identity issuer. May be displayed on login page.';
|
||||
$string['issuername'] = 'Name';
|
||||
$string['issuershowonloginpage_help'] = 'If the OpenID Connect Authentication plugin is enabled, this login issuer will be listed on the login page to allow users to log in with accounts from this issuer.';
|
||||
$string['issuershowonloginpage'] = 'Show on login page.';
|
||||
$string['issuershowonloginpage_help'] = 'If the OAuth 2 authentication plugin is enabled, this login issuer will be listed on the login page to allow users to log in with accounts from this issuer.';
|
||||
$string['issuershowonloginpage'] = 'Show on login page';
|
||||
$string['issuers'] = 'Issuers';
|
||||
$string['loginissuer'] = 'Allow login';
|
||||
$string['notconfigured'] = 'Not configured';
|
||||
|
||||
@@ -31,7 +31,7 @@ $string['default'] = 'Default';
|
||||
$string['disabled'] = 'Disabled';
|
||||
$string['disabled_help'] = 'Disabled scheduled tasks are not executed from cron, however they can still be executed manually via the CLI tool.';
|
||||
$string['edittaskschedule'] = 'Edit task schedule: {$a}';
|
||||
$string['enablerunnow'] = 'Allow ‘Run now’ for scheduled tasks';
|
||||
$string['enablerunnow'] = 'Allow \'Run now\' for scheduled tasks';
|
||||
$string['enablerunnow_desc'] = 'Allows administrators to run a single scheduled task immediately, rather than waiting for it to run as scheduled. The task runs on the web server, so some sites may wish to disable this feature to avoid potential performance issues.';
|
||||
$string['faildelay'] = 'Fail delay';
|
||||
$string['lastruntime'] = 'Last run';
|
||||
@@ -41,7 +41,7 @@ $string['pluginname'] = 'Scheduled task configuration';
|
||||
$string['resettasktodefaults'] = 'Reset task schedule to defaults';
|
||||
$string['resettasktodefaults_help'] = 'This will discard any local changes and revert the schedule for this task back to its original settings.';
|
||||
$string['runnow'] = 'Run now';
|
||||
$string['runnow_confirm'] = 'Are you sure you want to run this task ‘{$a}’ now? The task will run on the web server and may take some time to complete.';
|
||||
$string['runnow_confirm'] = 'Are you sure you want to run this task \'{$a}\' now? The task will run on the web server and may take some time to complete.';
|
||||
$string['scheduledtasks'] = 'Scheduled tasks';
|
||||
$string['scheduledtaskchangesdisabled'] = 'Modifications to the list of scheduled tasks have been prevented in Moodle configuration';
|
||||
$string['taskdisabled'] = 'Task disabled';
|
||||
|
||||
@@ -45,5 +45,8 @@ function xmldb_tool_usertours_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+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.
|
||||
@@ -66,5 +66,8 @@ function xmldb_auth_cas_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2017020700, 'auth', 'cas');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,5 +42,8 @@ function xmldb_auth_db_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2017032800, 'auth', 'db');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,5 +42,8 @@ function xmldb_auth_email_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2017020700, 'auth', 'email');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,5 +42,8 @@ function xmldb_auth_fc_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2017020700, 'auth', 'fc');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,5 +42,8 @@ function xmldb_auth_imap_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2017020700, 'auth', 'imap');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+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
|
||||
@@ -1844,7 +1844,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.
|
||||
@@ -1889,17 +1889,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.
|
||||
@@ -1907,7 +1907,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];
|
||||
}
|
||||
|
||||
@@ -66,5 +66,8 @@ function xmldb_auth_ldap_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2017020700, 'auth', 'ldap');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -54,5 +54,8 @@ function xmldb_auth_manual_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2017020700, 'auth', 'manual');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -53,5 +53,8 @@ function xmldb_auth_mnet_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2017020700, 'auth', 'mnet');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,5 +42,8 @@ function xmldb_auth_nntp_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2017020700, 'auth', 'nntp');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,5 +42,8 @@ function xmldb_auth_none_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2017020700, 'auth', 'none');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -192,10 +192,10 @@ class api {
|
||||
];
|
||||
$confirmationurl = new moodle_url('/auth/oauth2/confirm-linkedlogin.php', $params);
|
||||
|
||||
// Remove data parameter just in case it was included in the confirmation so we can add it manually later.
|
||||
$data->link = $confirmationurl->out();
|
||||
$data->link = $confirmationurl->out(false);
|
||||
$message = get_string('confirmlinkedloginemail', 'auth_oauth2', $data);
|
||||
|
||||
$message = get_string('confirmlinkedloginemail', 'auth_oauth2', $data);
|
||||
$data->link = $confirmationurl->out();
|
||||
$messagehtml = text_to_html(get_string('confirmlinkedloginemail', 'auth_oauth2', $data), false, false, true);
|
||||
|
||||
$user->mailformat = 1; // Always send HTML version as well.
|
||||
@@ -303,9 +303,10 @@ class api {
|
||||
];
|
||||
$confirmationurl = new moodle_url('/auth/oauth2/confirm-account.php', $params);
|
||||
|
||||
$data->link = $confirmationurl->out();
|
||||
$data->link = $confirmationurl->out(false);
|
||||
$message = get_string('confirmaccountemail', 'auth_oauth2', $data);
|
||||
|
||||
$message = get_string('confirmaccountemail', 'auth_oauth2', $data);
|
||||
$data->link = $confirmationurl->out();
|
||||
$messagehtml = text_to_html(get_string('confirmaccountemail', 'auth_oauth2', $data), false, false, true);
|
||||
|
||||
$user->mailformat = 1; // Always send HTML version as well.
|
||||
|
||||
@@ -247,6 +247,10 @@ class auth extends \auth_plugin_base {
|
||||
if (!empty($user->picture)) {
|
||||
return false;
|
||||
}
|
||||
if (!empty($CFG->enablegravatar)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$picture = $this->get_static_user_picture();
|
||||
if (empty($picture)) {
|
||||
return false;
|
||||
@@ -342,7 +346,7 @@ class auth extends \auth_plugin_base {
|
||||
* Complete the login process after oauth handshake is complete.
|
||||
* @param \core\oauth2\client $client
|
||||
* @param string $redirecturl
|
||||
* @return none Either redirects or throws an exception
|
||||
* @return void Either redirects or throws an exception
|
||||
*/
|
||||
public function complete_login(client $client, $redirecturl) {
|
||||
global $CFG, $SESSION, $PAGE;
|
||||
@@ -352,7 +356,7 @@ class auth extends \auth_plugin_base {
|
||||
if (!$userinfo) {
|
||||
// Trigger login failed event.
|
||||
$failurereason = AUTH_LOGIN_NOUSER;
|
||||
$event = \core\event\user_login_failed::create(['other' => ['username' => $userinfo['username'],
|
||||
$event = \core\event\user_login_failed::create(['other' => ['username' => 'unknown',
|
||||
'reason' => $failurereason]]);
|
||||
$event->trigger();
|
||||
|
||||
@@ -364,7 +368,7 @@ class auth extends \auth_plugin_base {
|
||||
if (empty($userinfo['username']) || empty($userinfo['email'])) {
|
||||
// Trigger login failed event.
|
||||
$failurereason = AUTH_LOGIN_NOUSER;
|
||||
$event = \core\event\user_login_failed::create(['other' => ['username' => $userinfo['username'],
|
||||
$event = \core\event\user_login_failed::create(['other' => ['username' => 'unknown',
|
||||
'reason' => $failurereason]]);
|
||||
$event->trigger();
|
||||
|
||||
|
||||
@@ -38,5 +38,8 @@ function xmldb_auth_oauth2_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,5 +42,8 @@ function xmldb_auth_pam_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2017020700, 'auth', 'pam');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,5 +42,8 @@ function xmldb_auth_pop3_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2017020700, 'auth', 'pop3');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,5 +42,8 @@ function xmldb_auth_shibboleth_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2017020700, 'auth', 'shibboleth');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -2658,8 +2658,9 @@ class restore_calendarevents_structure_step extends restore_structure_step {
|
||||
$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.
|
||||
if (!$this->task->get_setting_value('userinfo') && $isuseroverride) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -3082,7 +3083,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;
|
||||
@@ -3129,7 +3131,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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -88,5 +88,8 @@ function xmldb_block_badges_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -88,5 +88,8 @@ function xmldb_block_calendar_month_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -88,5 +88,8 @@ function xmldb_block_calendar_upcoming_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -61,5 +61,8 @@ function xmldb_block_community_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -63,5 +63,8 @@ function xmldb_block_completionstatus_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,5 +63,8 @@ function xmldb_block_course_summary_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -48,5 +48,8 @@ function xmldb_block_html_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ class block_login extends block_base {
|
||||
$this->content->text .= '<div class="potentialidplist">';
|
||||
foreach ($potentialidps as $idp) {
|
||||
$this->content->text .= '<div class="potentialidp">';
|
||||
$this->content->text .= '<a class="btn btn-secondary btn-block" ';
|
||||
$this->content->text .= '<a class="btn btn-default btn-block" ';
|
||||
$this->content->text .= 'href="' . $idp['url']->out() . '" title="' . s($idp['name']) . '">';
|
||||
if (!empty($idp['iconurl'])) {
|
||||
$this->content->text .= '<img src="' . s($idp['iconurl']) . '" width="24" height="24" class="m-r-1"/>';
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
define(["jquery","core/ajax","core/custom_interaction_events","core/notification"],function(a,b,c,d){var e=function(e){c.define(e,[c.events.activate]),e.on(c.events.activate,"[data-toggle='tab']",function(c){var e=a(c.currentTarget).data("tabname");"function"==typeof window.history.pushState&&window.history.pushState(null,null,"?myoverviewtab="+e);var f={methodname:"core_user_update_user_preferences",args:{preferences:[{type:"block_myoverview_last_tab",value:e}]}};b.call([f])[0].fail(d.exception)})};return{registerEventListeners:e}});
|
||||
@@ -0,0 +1,61 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Javascript used to save the user's tab preference.
|
||||
*
|
||||
* @package block_myoverview
|
||||
* @copyright 2017 Mark Nelson <markn@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery', 'core/ajax', 'core/custom_interaction_events',
|
||||
'core/notification'], function($, Ajax, CustomEvents, Notification) {
|
||||
|
||||
/**
|
||||
* Registers an event that saves the user's tab preference when switching between them.
|
||||
*
|
||||
* @param {object} root The container element
|
||||
*/
|
||||
var registerEventListeners = function(root) {
|
||||
CustomEvents.define(root, [CustomEvents.events.activate]);
|
||||
root.on(CustomEvents.events.activate, "[data-toggle='tab']", function(e) {
|
||||
var tabname = $(e.currentTarget).data('tabname');
|
||||
// Bootstrap does not change the URL when using BS tabs, so need to do this here.
|
||||
// Also check to make sure the browser supports the history API.
|
||||
if (typeof window.history.pushState === "function") {
|
||||
window.history.pushState(null, null, '?myoverviewtab=' + tabname);
|
||||
}
|
||||
var request = {
|
||||
methodname: 'core_user_update_user_preferences',
|
||||
args: {
|
||||
preferences: [
|
||||
{
|
||||
type: 'block_myoverview_last_tab',
|
||||
value: tabname
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
Ajax.call([request])[0]
|
||||
.fail(Notification.exception);
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
registerEventListeners: registerEventListeners
|
||||
};
|
||||
});
|
||||
@@ -50,7 +50,16 @@ class block_myoverview extends block_base {
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
$renderable = new \block_myoverview\output\main();
|
||||
// Check if the tab to select wasn't passed in the URL, if so see if the user has any preference.
|
||||
if (!$tab = optional_param('myoverviewtab', null, PARAM_ALPHA)) {
|
||||
// Check if the user has no preference, if so get the site setting.
|
||||
if (!$tab = get_user_preferences('block_myoverview_last_tab')) {
|
||||
$config = get_config('block_myoverview');
|
||||
$tab = $config->defaulttab;
|
||||
}
|
||||
}
|
||||
|
||||
$renderable = new \block_myoverview\output\main($tab);
|
||||
$renderer = $this->page->get_renderer('block_myoverview');
|
||||
|
||||
$this->content = new stdClass();
|
||||
@@ -68,4 +77,13 @@ class block_myoverview extends block_base {
|
||||
public function applicable_formats() {
|
||||
return array('my' => true);
|
||||
}
|
||||
|
||||
/**
|
||||
* This block does contain a configuration settings.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function has_config() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,8 @@ class courses_view implements renderable, templatable {
|
||||
* @return array
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
$today = time();
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot.'/course/lib.php');
|
||||
|
||||
// Build courses view data structure.
|
||||
$coursesview = [
|
||||
@@ -73,8 +74,6 @@ class courses_view implements renderable, templatable {
|
||||
// How many courses we have per status?
|
||||
$coursesbystatus = ['past' => 0, 'inprogress' => 0, 'future' => 0];
|
||||
foreach ($this->courses as $course) {
|
||||
$startdate = $course->startdate;
|
||||
$enddate = $course->enddate;
|
||||
$courseid = $course->id;
|
||||
$context = \context_course::instance($courseid);
|
||||
$exporter = new course_summary_exporter($course, [
|
||||
@@ -84,14 +83,17 @@ class courses_view implements renderable, templatable {
|
||||
// Convert summary to plain text.
|
||||
$exportedcourse->summary = content_to_text($exportedcourse->summary, $exportedcourse->summaryformat);
|
||||
|
||||
$courseprogress = null;
|
||||
|
||||
$classified = course_classify_for_timeline($course);
|
||||
|
||||
if (isset($this->coursesprogress[$courseid])) {
|
||||
$coursecompleted = $this->coursesprogress[$courseid]['completed'];
|
||||
$courseprogress = $this->coursesprogress[$courseid]['progress'];
|
||||
$exportedcourse->hasprogress = !is_null($courseprogress);
|
||||
$exportedcourse->progress = $courseprogress;
|
||||
}
|
||||
|
||||
if ((isset($coursecompleted) && $coursecompleted) || (!empty($enddate) && $enddate < $today)) {
|
||||
if ($classified == COURSE_TIMELINE_PAST) {
|
||||
// Courses that have already ended.
|
||||
$pastpages = floor($coursesbystatus['past'] / $this::COURSES_PER_PAGE);
|
||||
|
||||
@@ -100,7 +102,7 @@ class courses_view implements renderable, templatable {
|
||||
$coursesview['past']['pages'][$pastpages]['page'] = $pastpages + 1;
|
||||
$coursesview['past']['haspages'] = true;
|
||||
$coursesbystatus['past']++;
|
||||
} else if ($startdate > $today) {
|
||||
} else if ($classified == COURSE_TIMELINE_FUTURE) {
|
||||
// Courses that have not started yet.
|
||||
$futurepages = floor($coursesbystatus['future'] / $this::COURSES_PER_PAGE);
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ use renderer_base;
|
||||
use templatable;
|
||||
use core_completion\progress;
|
||||
|
||||
require_once($CFG->dirroot . '/blocks/myoverview/lib.php');
|
||||
require_once($CFG->libdir . '/completionlib.php');
|
||||
|
||||
/**
|
||||
@@ -39,6 +40,20 @@ require_once($CFG->libdir . '/completionlib.php');
|
||||
*/
|
||||
class main implements renderable, templatable {
|
||||
|
||||
/**
|
||||
* @var string The tab to display.
|
||||
*/
|
||||
public $tab;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $tab The tab to display.
|
||||
*/
|
||||
public function __construct($tab) {
|
||||
$this->tab = $tab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this data so it can be used as the context for a mustache template.
|
||||
*
|
||||
@@ -73,13 +88,24 @@ class main implements renderable, templatable {
|
||||
$nocoursesurl = $output->image_url('courses', 'block_myoverview')->out();
|
||||
$noeventsurl = $output->image_url('activities', 'block_myoverview')->out();
|
||||
|
||||
// Now, set the tab we are going to be viewing.
|
||||
$viewingtimeline = false;
|
||||
$viewingcourses = false;
|
||||
if ($this->tab == BLOCK_MYOVERVIEW_TIMELINE_VIEW) {
|
||||
$viewingtimeline = true;
|
||||
} else {
|
||||
$viewingcourses = true;
|
||||
}
|
||||
|
||||
return [
|
||||
'midnight' => usergetmidnight(time()),
|
||||
'coursesview' => $coursesview->export_for_template($output),
|
||||
'urls' => [
|
||||
'nocourses' => $nocoursesurl,
|
||||
'noevents' => $noeventsurl
|
||||
]
|
||||
],
|
||||
'viewingtimeline' => $viewingtimeline,
|
||||
'viewingcourses' => $viewingcourses
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string['defaulttab'] = 'Default tab';
|
||||
$string['defaulttab_desc'] = 'The tab that will be displayed when a user first views their course overview. When returning to their course overview, the user\'s active tab is remembered.';
|
||||
$string['future'] = 'Future';
|
||||
$string['inprogress'] = 'In progress';
|
||||
$string['morecourses'] = 'More courses';
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Contains functions called by core.
|
||||
*
|
||||
* @package block_myoverview
|
||||
* @copyright 2017 Mark Nelson <markn@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* The timeline view.
|
||||
*/
|
||||
define('BLOCK_MYOVERVIEW_TIMELINE_VIEW', 'timeline');
|
||||
|
||||
/**
|
||||
* The courses view.
|
||||
*/
|
||||
define('BLOCK_MYOVERVIEW_COURSES_VIEW', 'courses');
|
||||
|
||||
/**
|
||||
* Returns the name of the user preferences as well as the details this plugin uses.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function block_myoverview_user_preferences() {
|
||||
$preferences = array();
|
||||
$preferences['block_myoverview_last_tab'] = array(
|
||||
'type' => PARAM_ALPHA,
|
||||
'null' => NULL_NOT_ALLOWED,
|
||||
'default' => BLOCK_MYOVERVIEW_TIMELINE_VIEW,
|
||||
'choices' => array(BLOCK_MYOVERVIEW_TIMELINE_VIEW, BLOCK_MYOVERVIEW_COURSES_VIEW)
|
||||
);
|
||||
|
||||
return $preferences;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Settings for the overview block.
|
||||
*
|
||||
* @package block_myoverview
|
||||
* @copyright 2017 Mark Nelson <markn@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
require_once($CFG->dirroot . '/blocks/myoverview/lib.php');
|
||||
|
||||
if ($ADMIN->fulltree) {
|
||||
|
||||
$options = [
|
||||
BLOCK_MYOVERVIEW_TIMELINE_VIEW => get_string('timeline', 'block_myoverview'),
|
||||
BLOCK_MYOVERVIEW_COURSES_VIEW => get_string('courses')
|
||||
];
|
||||
|
||||
$settings->add(new admin_setting_configselect('block_myoverview/defaulttab',
|
||||
get_string('defaulttab', 'block_myoverview'),
|
||||
get_string('defaulttab_desc', 'block_myoverview'), 'timeline', $options));
|
||||
}
|
||||
@@ -24,26 +24,32 @@
|
||||
}}
|
||||
|
||||
<div id="block-myoverview-{{uniqid}}" class="block-myoverview" data-region="myoverview">
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<ul id="block-myoverview-view-choices-{{uniqid}}" class="nav nav-tabs" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="#myoverview_timeline_view" role="tab" data-toggle="tab">
|
||||
<a class="nav-link {{#viewingtimeline}}active{{/viewingtimeline}}" href="#myoverview_timeline_view" role="tab" data-toggle="tab" data-tabname="timeline">
|
||||
{{#str}} timeline, block_myoverview {{/str}}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#myoverview_courses_view" role="tab" data-toggle="tab">
|
||||
<a class="nav-link {{#viewingcourses}}active{{/viewingcourses}}" href="#myoverview_courses_view" role="tab" data-toggle="tab" data-tabname="courses">
|
||||
{{#str}} courses {{/str}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content content-centred">
|
||||
<div role="tabpanel" class="tab-pane fade in active" id="myoverview_timeline_view">
|
||||
<div role="tabpanel" class="tab-pane fade {{#viewingtimeline}}in active{{/viewingtimeline}}" id="myoverview_timeline_view">
|
||||
{{> block_myoverview/timeline-view }}
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane fade" id="myoverview_courses_view">
|
||||
<div role="tabpanel" class="tab-pane fade {{#viewingcourses}}in active{{/viewingcourses}}" id="myoverview_courses_view">
|
||||
{{#coursesview}}
|
||||
{{> block_myoverview/courses-view }}
|
||||
{{/coursesview}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{#js}}
|
||||
require(['jquery', 'block_myoverview/tab_preferences'], function($, TabPreferences) {
|
||||
var root = $('#block-myoverview-view-choices-{{uniqid}}');
|
||||
TabPreferences.registerEventListeners(root);
|
||||
});
|
||||
{{/js}}
|
||||
|
||||
@@ -53,6 +53,7 @@ Feature: Course overview block show users their progress on courses
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test choice 1"
|
||||
And I follow "Dashboard" in the user menu
|
||||
And I click on "Timeline" "link" in the "Course overview" "block"
|
||||
And I click on "Sort by courses" "link" in the "Course overview" "block"
|
||||
And I should see "100%" in the "Course overview" "block"
|
||||
And I click on "Courses" "link" in the "Course overview" "block"
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2017051500; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2017051502; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2017050500; // Requires this Moodle version.
|
||||
$plugin->component = 'block_myoverview'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -1 +1 @@
|
||||
define(["jquery"],function(a){function b(d,e){var f=a("<ul></ul>");f.attr("role","group"),f.attr("aria-hidden",!0),a.each(e,function(d,e){if("object"==typeof e){var g=a("<li></li>"),h=a("<p></p>"),i=e.id||e.key+"_tree_item",j=null,k=!(!e.expandable&&!e.haschildren);if(h.addClass("tree_item"),h.attr("id",i),h.attr("role","treeitem"),h.attr("tabindex","-1"),e.requiresajaxloading&&(h.attr("data-requires-ajax",!0),h.attr("data-node-id",e.id),h.attr("data-node-key",e.key),h.attr("data-node-type",e.type)),k&&(g.addClass("collapsed contains_branch"),h.attr("aria-expanded",!1),h.addClass("branch")),!e.icon||k&&e.type!==c.ACTIVITY&&e.type!==c.RESOURCE||(g.addClass("item_with_icon"),h.addClass("hasicon"),j=a("<img/>"),j.attr("alt",e.icon.alt),j.attr("title",e.icon.title),j.attr("src",M.util.image_url(e.icon.pix,e.icon.component)),a.each(e.icon.classes,function(a,b){j.addClass(b)})),e.link){var l=a('<a title="'+e.title+'" href="'+e.link+'"></a>');j?(l.append(j),l.append('<span class="item-content-wrap">'+e.name+"</span>")):l.append(e.name),e.hidden&&l.addClass("dimmed"),h.append(l)}else{var m=a("<span></span>");j?(m.append(j),m.append('<span class="item-content-wrap">'+e.name+"</span>")):m.append(e.name),e.hidden&&m.addClass("dimmed"),h.append(m)}g.append(h),f.append(g),e.children&&e.children.length?b(h,e.children):k&&!e.requiresajaxloading&&(g.removeClass("contains_branch"),h.addClass("emptybranch"))}}),d.parent().append(f);var g=d.attr("id")+"_group";f.attr("id",g),d.attr("aria-owns",g),d.attr("role","treeitem")}var c={ACTIVITY:40,RESOURCE:50};return{render:function(a,c){if(c.children&&c.children.length){b(a,c.children);var d=a.children("[role='treeitem']").first(),e=a.find("#"+d.attr("aria-owns"));d.attr("aria-expanded",!0),e.attr("aria-hidden",!1)}else a.parent().hasClass("contains_branch")&&(a.parent().removeClass("contains_branch"),a.addClass("emptybranch"))}}});
|
||||
define(["jquery","core/templates","core/notification","core/url"],function(a,b,c,d){function e(g,h){var i=a("<ul></ul>");i.attr("role","group"),i.attr("aria-hidden",!0),a.each(h,function(g,h){if("object"==typeof h){var j=a("<li></li>"),k=a("<p></p>"),l=h.id||h.key+"_tree_item",m=null,n=!(!h.expandable&&!h.haschildren);k.addClass("tree_item"),k.attr("id",l),k.attr("role","treeitem"),k.attr("tabindex","-1"),h.requiresajaxloading&&(k.attr("data-requires-ajax",!0),k.attr("data-node-id",h.id),k.attr("data-node-key",h.key),k.attr("data-node-type",h.type)),n&&(j.addClass("collapsed contains_branch"),k.attr("aria-expanded",!1),k.addClass("branch"));var o=null;if(h.link){var p=a('<a title="'+h.title+'" href="'+h.link+'"></a>');o=p,p.append('<span class="item-content-wrap">'+h.name+"</span>"),h.hidden&&p.addClass("dimmed"),k.append(p)}else{var q=a("<span></span>");o=q,q.append('<span class="item-content-wrap">'+h.name+"</span>"),h.hidden&&q.addClass("dimmed"),k.append(q)}!h.icon||n&&h.type!==f.ACTIVITY&&h.type!==f.RESOURCE||(j.addClass("item_with_icon"),k.addClass("hasicon"),h.type===f.ACTIVITY||h.type===f.RESOURCE?(m=a("<img/>"),m.attr("alt",h.icon.alt),m.attr("title",h.icon.title),m.attr("src",d.imageUrl(h.icon.pix,h.icon.component)),a.each(h.icon.classes,function(a,b){m.addClass(b)}),o.prepend(m)):("moodle"==h.icon.component&&(h.icon.component="core"),b.renderPix(h.icon.pix,h.icon.component,h.icon.title).then(function(a){o.prepend(a)})["catch"](c.exception))),j.append(k),i.append(j),h.children&&h.children.length?e(k,h.children):n&&!h.requiresajaxloading&&(j.removeClass("contains_branch"),k.addClass("emptybranch"))}}),g.parent().append(i);var j=g.attr("id")+"_group";i.attr("id",j),g.attr("aria-owns",j),g.attr("role","treeitem")}var f={ACTIVITY:40,RESOURCE:50};return{render:function(a,b){if(b.children&&b.children.length){e(a,b.children);var c=a.children("[role='treeitem']").first(),d=a.find("#"+c.attr("aria-owns"));c.attr("aria-expanded",!0),d.attr("aria-hidden",!1)}else a.parent().hasClass("contains_branch")&&(a.parent().removeClass("contains_branch"),a.addClass("emptybranch"))}}});
|
||||
@@ -22,7 +22,7 @@
|
||||
* @copyright 2015 John Okely <john@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery'], function($) {
|
||||
define(['jquery', 'core/templates', 'core/notification', 'core/url'], function($, Templates, Notification, Url) {
|
||||
|
||||
// Mappings for the different types of nodes coming from the navigation.
|
||||
// Copied from lib/navigationlib.php navigation_node constants.
|
||||
@@ -75,28 +75,12 @@ define(['jquery'], function($) {
|
||||
p.addClass('branch');
|
||||
}
|
||||
|
||||
if (node.icon && (!isBranch || node.type === NODETYPE.ACTIVITY || node.type === NODETYPE.RESOURCE)) {
|
||||
li.addClass('item_with_icon');
|
||||
p.addClass('hasicon');
|
||||
|
||||
icon = $('<img/>');
|
||||
icon.attr('alt', node.icon.alt);
|
||||
icon.attr('title', node.icon.title);
|
||||
icon.attr('src', M.util.image_url(node.icon.pix, node.icon.component));
|
||||
$.each(node.icon.classes, function(index, className) {
|
||||
icon.addClass(className);
|
||||
});
|
||||
}
|
||||
|
||||
var eleToAddIcon = null;
|
||||
if (node.link) {
|
||||
var link = $('<a title="' + node.title + '" href="' + node.link + '"></a>');
|
||||
|
||||
if (icon) {
|
||||
link.append(icon);
|
||||
link.append('<span class="item-content-wrap">' + node.name + '</span>');
|
||||
} else {
|
||||
link.append(node.name);
|
||||
}
|
||||
eleToAddIcon = link;
|
||||
link.append('<span class="item-content-wrap">' + node.name + '</span>');
|
||||
|
||||
if (node.hidden) {
|
||||
link.addClass('dimmed');
|
||||
@@ -106,12 +90,8 @@ define(['jquery'], function($) {
|
||||
} else {
|
||||
var span = $('<span></span>');
|
||||
|
||||
if (icon) {
|
||||
span.append(icon);
|
||||
span.append('<span class="item-content-wrap">' + node.name + '</span>');
|
||||
} else {
|
||||
span.append(node.name);
|
||||
}
|
||||
eleToAddIcon = span;
|
||||
span.append('<span class="item-content-wrap">' + node.name + '</span>');
|
||||
|
||||
if (node.hidden) {
|
||||
span.addClass('dimmed');
|
||||
@@ -120,6 +100,31 @@ define(['jquery'], function($) {
|
||||
p.append(span);
|
||||
}
|
||||
|
||||
if (node.icon && (!isBranch || node.type === NODETYPE.ACTIVITY || node.type === NODETYPE.RESOURCE)) {
|
||||
li.addClass('item_with_icon');
|
||||
p.addClass('hasicon');
|
||||
|
||||
if (node.type === NODETYPE.ACTIVITY || node.type === NODETYPE.RESOURCE) {
|
||||
icon = $('<img/>');
|
||||
icon.attr('alt', node.icon.alt);
|
||||
icon.attr('title', node.icon.title);
|
||||
icon.attr('src', Url.imageUrl(node.icon.pix, node.icon.component));
|
||||
$.each(node.icon.classes, function(index, className) {
|
||||
icon.addClass(className);
|
||||
});
|
||||
eleToAddIcon.prepend(icon);
|
||||
} else {
|
||||
if (node.icon.component == 'moodle') {
|
||||
node.icon.component = 'core';
|
||||
}
|
||||
Templates.renderPix(node.icon.pix, node.icon.component, node.icon.title).then(function(html) {
|
||||
// Prepend.
|
||||
eleToAddIcon.prepend(html);
|
||||
return;
|
||||
}).catch(Notification.exception);
|
||||
}
|
||||
}
|
||||
|
||||
li.append(p);
|
||||
ul.append(li);
|
||||
|
||||
|
||||
@@ -70,5 +70,8 @@ function xmldb_block_navigation_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -71,6 +71,6 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.block_navigation .block_tree [aria-hidden="true"] {
|
||||
.block_navigation .block_tree [aria-hidden="true"]:not(.icon) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -113,5 +113,8 @@ function xmldb_block_quiz_results_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -62,5 +62,8 @@ function xmldb_block_recent_activity_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -59,5 +59,8 @@ function xmldb_block_rss_client_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -64,5 +64,8 @@ function xmldb_block_section_links_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -63,5 +63,8 @@ function xmldb_block_selfcompletion_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -70,5 +70,8 @@ function xmldb_block_settings_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v3.2.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.block_settings .block_tree [aria-hidden="true"] {
|
||||
.block_settings .block_tree [aria-hidden="true"]:not(.icon) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -1077,7 +1077,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);
|
||||
}
|
||||
@@ -2162,4 +2162,4 @@ class cache_session extends cache {
|
||||
*/
|
||||
class cache_request extends cache {
|
||||
// This comment appeases code pre-checker ;) !
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+33
@@ -2177,6 +2177,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);
|
||||
|
||||
@@ -71,11 +71,6 @@ class container {
|
||||
*/
|
||||
protected static $eventretrievalstrategy;
|
||||
|
||||
/**
|
||||
* @var array A list of callbacks to use.
|
||||
*/
|
||||
protected static $callbacks = array();
|
||||
|
||||
/**
|
||||
* @var \stdClass[] An array of cached courses to use with the event factory.
|
||||
*/
|
||||
@@ -91,16 +86,6 @@ class container {
|
||||
*/
|
||||
private static function init() {
|
||||
if (empty(self::$eventfactory)) {
|
||||
// When testing the container's components, we need to make sure
|
||||
// the callback implementations in modules are not executed, since
|
||||
// we cannot control their output from PHPUnit. To do this we have
|
||||
// a set of 'testing' callbacks that the factory can use. This way
|
||||
// we know exactly how the factory behaves when being tested.
|
||||
$getcallback = function($which) {
|
||||
return self::$callbacks[PHPUNIT_TEST ? 'testing' : 'production'][$which];
|
||||
};
|
||||
|
||||
self::initcallbacks();
|
||||
self::$actionfactory = new action_factory();
|
||||
self::$eventmapper = new event_mapper(
|
||||
// The event mapper we return from here needs to know how to
|
||||
@@ -129,8 +114,8 @@ class container {
|
||||
);
|
||||
|
||||
self::$eventfactory = new event_factory(
|
||||
$getcallback('action'),
|
||||
$getcallback('visibility'),
|
||||
[self::class, 'apply_component_provide_event_action'],
|
||||
[self::class, 'apply_component_is_event_visible'],
|
||||
function ($dbrow) {
|
||||
// At present we only have a bail-out check for events in course modules.
|
||||
if (empty($dbrow->modulename)) {
|
||||
@@ -152,10 +137,15 @@ class container {
|
||||
// have that capability set on the "Authenticated User" role rather than
|
||||
// on "Student" role, which means uservisible returns true even when the user
|
||||
// is no longer enrolled in the course.
|
||||
$modulecontext = \context_module::instance($cm->id);
|
||||
// A user with the 'moodle/course:view' capability is able to see courses
|
||||
// that they are not a participant in.
|
||||
$canseecourse = (has_capability('moodle/course:view', $modulecontext) || is_enrolled($modulecontext));
|
||||
// So, with the following we are checking -
|
||||
// 1) Only process modules if $cm->uservisible is true.
|
||||
// 2) Only process modules for courses a user has the capability to view OR they are enrolled in.
|
||||
// 3) Only process modules for courses that are visible OR if the course is not visible, the user
|
||||
// has the capability to view hidden courses.
|
||||
$coursecontext = \context_course::instance($dbrow->courseid);
|
||||
$canseecourse = has_capability('moodle/course:view', $coursecontext) || is_enrolled($coursecontext);
|
||||
$canseecourse = $canseecourse &&
|
||||
($cm->get_course()->visible || has_capability('moodle/course:viewhiddencourses', $coursecontext));
|
||||
if (!$cm->uservisible || !$canseecourse) {
|
||||
return true;
|
||||
}
|
||||
@@ -183,6 +173,19 @@ class container {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all static caches, called between tests.
|
||||
*/
|
||||
public static function reset_caches() {
|
||||
self::$eventfactory = null;
|
||||
self::$eventmapper = null;
|
||||
self::$eventvault = null;
|
||||
self::$actionfactory = null;
|
||||
self::$eventretrievalstrategy = null;
|
||||
self::$coursecache = [];
|
||||
self::$modulecache = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the event factory.
|
||||
*
|
||||
@@ -214,88 +217,74 @@ class container {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the callbacks.
|
||||
* Calls callback 'core_calendar_provide_event_action' from the component responsible for the event
|
||||
*
|
||||
* There are two sets here, one is used during PHPUnit runs.
|
||||
* See the comment at the start of the init method for more
|
||||
* detail.
|
||||
* If no callback is present or callback returns null, there is no action on the event
|
||||
* and it will not be displayed on the dashboard.
|
||||
*
|
||||
* @param event_interface $event
|
||||
* @return action_event|event_interface
|
||||
*/
|
||||
private static function initcallbacks() {
|
||||
self::$callbacks = array(
|
||||
'testing' => array(
|
||||
// Always return an action event.
|
||||
'action' => function (event_interface $event) {
|
||||
return new action_event(
|
||||
$event,
|
||||
new \core_calendar\local\event\value_objects\action(
|
||||
'test',
|
||||
new \moodle_url('http://example.com'),
|
||||
420,
|
||||
true
|
||||
));
|
||||
},
|
||||
// Always be visible.
|
||||
'visibility' => function (event_interface $event) {
|
||||
return true;
|
||||
}
|
||||
),
|
||||
'production' => array(
|
||||
// This function has type event_interface -> event_interface.
|
||||
// This is enforced by the event_factory.
|
||||
'action' => function (event_interface $event) {
|
||||
// Callbacks will get supplied a "legacy" version
|
||||
// of the event class.
|
||||
$mapper = self::$eventmapper;
|
||||
$action = null;
|
||||
if ($event->get_course_module()) {
|
||||
// TODO MDL-58866 Only activity modules currently support this callback.
|
||||
// Any other event will not be displayed on the dashboard.
|
||||
$action = component_callback(
|
||||
'mod_' . $event->get_course_module()->get('modname'),
|
||||
'core_calendar_provide_event_action',
|
||||
[
|
||||
$mapper->from_event_to_legacy_event($event),
|
||||
self::$actionfactory
|
||||
]
|
||||
);
|
||||
}
|
||||
public static function apply_component_provide_event_action(event_interface $event) {
|
||||
// Callbacks will get supplied a "legacy" version
|
||||
// of the event class.
|
||||
$mapper = self::$eventmapper;
|
||||
$action = null;
|
||||
if ($event->get_course_module()) {
|
||||
// TODO MDL-58866 Only activity modules currently support this callback.
|
||||
// Any other event will not be displayed on the dashboard.
|
||||
$action = component_callback(
|
||||
'mod_' . $event->get_course_module()->get('modname'),
|
||||
'core_calendar_provide_event_action',
|
||||
[
|
||||
$mapper->from_event_to_legacy_event($event),
|
||||
self::$actionfactory
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// If we get an action back, return an action event, otherwise
|
||||
// continue piping through the original event.
|
||||
//
|
||||
// If a module does not implement the callback, component_callback
|
||||
// returns null.
|
||||
return $action ? new action_event($event, $action) : $event;
|
||||
},
|
||||
// This function has type event_interface -> bool.
|
||||
// This is enforced by the event_factory.
|
||||
'visibility' => function (event_interface $event) {
|
||||
$mapper = self::$eventmapper;
|
||||
$eventvisible = null;
|
||||
if ($event->get_course_module()) {
|
||||
// TODO MDL-58866 Only activity modules currently support this callback.
|
||||
$eventvisible = component_callback(
|
||||
'mod_' . $event->get_course_module()->get('modname'),
|
||||
'core_calendar_is_event_visible',
|
||||
[
|
||||
$mapper->from_event_to_legacy_event($event)
|
||||
]
|
||||
);
|
||||
}
|
||||
// If we get an action back, return an action event, otherwise
|
||||
// continue piping through the original event.
|
||||
//
|
||||
// If a module does not implement the callback, component_callback
|
||||
// returns null.
|
||||
return $action ? new action_event($event, $action) : $event;
|
||||
}
|
||||
|
||||
// Do not display the event if there is nothing to action.
|
||||
if ($event instanceof action_event_interface && $event->get_action()->get_item_count() === 0) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Calls callback 'core_calendar_is_event_visible' from the component responsible for the event
|
||||
*
|
||||
* The visibility callback is optional, if not present it is assumed as visible.
|
||||
* If it is an actionable event but the get_item_count() returns 0 the visibility
|
||||
* is set to false.
|
||||
*
|
||||
* @param event_interface $event
|
||||
* @return bool
|
||||
*/
|
||||
public static function apply_component_is_event_visible(event_interface $event) {
|
||||
$mapper = self::$eventmapper;
|
||||
$eventvisible = null;
|
||||
if ($event->get_course_module()) {
|
||||
// TODO MDL-58866 Only activity modules currently support this callback.
|
||||
$eventvisible = component_callback(
|
||||
'mod_' . $event->get_course_module()->get('modname'),
|
||||
'core_calendar_is_event_visible',
|
||||
[
|
||||
$mapper->from_event_to_legacy_event($event)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Module does not implement the callback, event should be visible.
|
||||
if (is_null($eventvisible)) {
|
||||
return true;
|
||||
}
|
||||
// Do not display the event if there is nothing to action.
|
||||
if ($event instanceof action_event_interface && $event->get_action()->get_item_count() === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $eventvisible ? true : false;
|
||||
}
|
||||
),
|
||||
);
|
||||
// Module does not implement the callback, event should be visible.
|
||||
if (is_null($eventvisible)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $eventvisible ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ class event_vault implements event_vault_interface {
|
||||
$coursesfilter,
|
||||
$where,
|
||||
$params,
|
||||
"e.timesort ASC, e.id ASC",
|
||||
"COALESCE(e.timesort, e.timestart) ASC, e.id ASC",
|
||||
$offset,
|
||||
$limitnum,
|
||||
$ignorehidden
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -194,10 +194,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
|
||||
|
||||
+1
-1
@@ -1481,7 +1481,7 @@ function calendar_get_mini($courses, $groups, $users, $calmonth = false, $calyea
|
||||
$class .= ' duration_finish';
|
||||
}
|
||||
$data = array(
|
||||
'url' => $dayhref,
|
||||
'url' => $dayhref->out(false),
|
||||
'day' => $day,
|
||||
'content' => $popupdata['data-core_calendar-popupcontent'],
|
||||
'title' => $popupdata['data-core_calendar-title']
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -183,6 +183,63 @@ class core_calendar_container_testcase extends advanced_testcase {
|
||||
$this->assertNull($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the event factory deals with invisible courses as an admin.
|
||||
*
|
||||
* @dataProvider get_event_factory_testcases()
|
||||
* @param \stdClass $dbrow Row from the "database".
|
||||
*/
|
||||
public function test_event_factory_when_course_visibility_is_toggled_as_admin($dbrow) {
|
||||
$legacyevent = $this->create_event($dbrow);
|
||||
$factory = \core_calendar\local\event\container::get_event_factory();
|
||||
|
||||
// Create a hidden course with an assignment.
|
||||
$course = $this->getDataGenerator()->create_course(['visible' => 0]);
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
|
||||
$moduleinstance = $generator->create_instance(['course' => $course->id]);
|
||||
|
||||
$dbrow->id = $legacyevent->id;
|
||||
$dbrow->courseid = $course->id;
|
||||
$dbrow->instance = $moduleinstance->id;
|
||||
$dbrow->modulename = 'assign';
|
||||
$event = $factory->create_instance($dbrow);
|
||||
|
||||
// Module is still visible to admins even if the course is invisible.
|
||||
$this->assertInstanceOf(event_interface::class, $event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the event factory deals with invisible courses as a student.
|
||||
*
|
||||
* @dataProvider get_event_factory_testcases()
|
||||
* @param \stdClass $dbrow Row from the "database".
|
||||
*/
|
||||
public function test_event_factory_when_course_visibility_is_toggled_as_student($dbrow) {
|
||||
$legacyevent = $this->create_event($dbrow);
|
||||
$factory = \core_calendar\local\event\container::get_event_factory();
|
||||
|
||||
// Create a hidden course with an assignment.
|
||||
$course = $this->getDataGenerator()->create_course(['visible' => 0]);
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
|
||||
$moduleinstance = $generator->create_instance(['course' => $course->id]);
|
||||
|
||||
// Enrol a student into this course.
|
||||
$student = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($student->id, $course->id);
|
||||
|
||||
// Set the user to the student.
|
||||
$this->setUser($student);
|
||||
|
||||
$dbrow->id = $legacyevent->id;
|
||||
$dbrow->courseid = $course->id;
|
||||
$dbrow->instance = $moduleinstance->id;
|
||||
$dbrow->modulename = 'assign';
|
||||
$event = $factory->create_instance($dbrow);
|
||||
|
||||
// Module is invisible to students if the course is invisible.
|
||||
$this->assertNull($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the event factory deals with completion related events properly.
|
||||
*/
|
||||
@@ -285,6 +342,31 @@ class core_calendar_container_testcase extends advanced_testcase {
|
||||
$this->assertInstanceOf(event_interface::class, $factory->create_instance($event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that when course module is deleted all events are also deleted.
|
||||
*/
|
||||
public function test_delete_module_delete_events() {
|
||||
global $DB;
|
||||
$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]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting the event mapper.
|
||||
*/
|
||||
|
||||
@@ -674,13 +674,15 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
|
||||
*/
|
||||
public function test_get_calendar_events_override() {
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$teacher = $this->getDataGenerator()->create_user();
|
||||
$anotheruser = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
|
||||
$moduleinstance = $generator->create_instance(['course' => $course->id]);
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id);
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
$this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
|
||||
$this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
@@ -692,11 +694,12 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
|
||||
];
|
||||
|
||||
$now = time();
|
||||
// Create two events - one for everybody in the course and one only for the first student.
|
||||
$event1 = $this->create_calendar_event('Base event', 0, 'due', 0, $now + DAYSECS, $params + ['courseid' => $course->id]);
|
||||
$event2 = $this->create_calendar_event('User event', $user->id, 'due', 0, $now + 2*DAYSECS, $params + ['courseid' => 0]);
|
||||
|
||||
// Retrieve course events for teacher - only one "Base event" is returned.
|
||||
$this->setUser($teacher);
|
||||
// Retrieve course events for the second student - only one "Base event" is returned.
|
||||
$this->setUser($user2);
|
||||
$paramevents = array('courseids' => array($course->id));
|
||||
$options = array ('siteevents' => true, 'userevents' => true);
|
||||
$events = core_calendar_external::get_calendar_events($paramevents, $options);
|
||||
@@ -705,7 +708,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
|
||||
$this->assertEquals(0, count($events['warnings']));
|
||||
$this->assertEquals('Base event', $events['events'][0]['name']);
|
||||
|
||||
// Retrieve events for user - both events are returned.
|
||||
// Retrieve events for the first student - both events are returned.
|
||||
$this->setUser($user);
|
||||
$events = core_calendar_external::get_calendar_events($paramevents, $options);
|
||||
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
|
||||
|
||||
@@ -451,6 +451,9 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
*/
|
||||
public function test_every_300_days_forever() {
|
||||
global $DB;
|
||||
|
||||
// Change the start date for forever events to 9am of the current date.
|
||||
$this->change_event_startdate(date('Ymd\T090000'));
|
||||
$startdatetime = new DateTime(date('Y-m-d H:i:s', $this->event->timestart));
|
||||
|
||||
$interval = new DateInterval('P300D');
|
||||
@@ -463,7 +466,8 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
$mang = new rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
$records = $DB->get_records('event', array('repeatid' => $this->event->id), 'timestart ASC');
|
||||
// Get the first 100 samples. This should be enough to verify that we have generated the recurring events correctly.
|
||||
$records = $DB->get_records('event', array('repeatid' => $this->event->id), 'timestart ASC', 0, 100);
|
||||
|
||||
$expecteddate = clone($startdatetime);
|
||||
$first = true;
|
||||
@@ -552,8 +556,20 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
$expecteddate->add($interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forever event. This should generate events over time() + 10 year period, every 50th Monday.
|
||||
/**
|
||||
* Test recurrence rules for weekly frequency for RRULE with BYDAY rule set, recurring forever.
|
||||
*/
|
||||
public function test_weekly_byday_forever() {
|
||||
global $DB;
|
||||
|
||||
// Set the next Monday as the starting date of this event.
|
||||
$startdate = new DateTime('next Monday');
|
||||
// Change the start date of the parent event.
|
||||
$startdate = $this->change_event_startdate($startdate->format('Ymd\T090000'));
|
||||
|
||||
// Forever event. This should generate events over time() + 10 year period, every 50 weeks.
|
||||
$rrule = 'FREQ=WEEKLY;BYDAY=MO;INTERVAL=50';
|
||||
|
||||
$mang = new rrule_manager($rrule);
|
||||
@@ -564,13 +580,14 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
$untildate->add(new DateInterval('P10Y'));
|
||||
$until = $untildate->getTimestamp();
|
||||
|
||||
$interval = new DateInterval('P50W');
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart');
|
||||
|
||||
// First instance of this set of recurring events: Monday, 17-08-1998.
|
||||
$interval = new DateInterval('P50W');
|
||||
|
||||
// First instance of this set of recurring events.
|
||||
$expecteddate = clone($startdate);
|
||||
$expecteddate->modify('1998-08-17');
|
||||
$expecteddate->add($offsetinterval);
|
||||
|
||||
// Iterate over each record and increment the expected date accordingly.
|
||||
foreach ($records as $record) {
|
||||
$eventdateexpected = $expecteddate->format('Y-m-d H:i:s');
|
||||
$eventdateactual = date('Y-m-d H:i:s', $record->timestart);
|
||||
@@ -676,17 +693,21 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
public function test_monthly_events_with_bymonthday_forever() {
|
||||
global $DB;
|
||||
|
||||
// Change the start date for forever events to 9am of the 2nd day of the current month and year.
|
||||
$this->change_event_startdate(date('Ym02\T090000'));
|
||||
$startdatetime = new DateTime(date('Y-m-d H:i:s', $this->event->timestart));
|
||||
$startdate = new DateTime(date('Y-m-d', $this->event->timestart));
|
||||
|
||||
$offsetinterval = $startdatetime->diff($startdate, true);
|
||||
$interval = new DateInterval('P12M');
|
||||
|
||||
// Forever event. This should generate events over 10 year period, on 2nd day of every 12th month.
|
||||
// Forever event. This should generate events over a 10-year period, on 2nd day of the month, every 12 months.
|
||||
$rrule = "FREQ=MONTHLY;INTERVAL=12;BYMONTHDAY=2";
|
||||
|
||||
$mang = new rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$untildate = new DateTime();
|
||||
$untildate->add(new DateInterval('P' . $mang::TIME_UNLIMITED_YEARS . 'Y'));
|
||||
$until = $untildate->getTimestamp();
|
||||
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
@@ -828,24 +849,33 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
public function test_monthly_events_with_byday_forever() {
|
||||
global $DB;
|
||||
|
||||
// Change the start date for forever events to 9am of the 2nd day of the current month and year.
|
||||
$this->change_event_startdate(date('Ym02\T090000'));
|
||||
|
||||
$startdatetime = new DateTime(date('Y-m-d H:i:s', $this->event->timestart));
|
||||
$startdate = new DateTime(date('Y-m-d', $this->event->timestart));
|
||||
|
||||
$offsetinterval = $startdatetime->diff($startdate, true);
|
||||
$interval = new DateInterval('P12M');
|
||||
|
||||
// Forever event. This should generate events over 10 year period, on 2nd day of every 12th month.
|
||||
// Forever event. This should generate events over a 10 year period, on 1st Monday of the month every 12 months.
|
||||
$rrule = "FREQ=MONTHLY;INTERVAL=12;BYDAY=1MO";
|
||||
|
||||
$mang = new rrule_manager($rrule);
|
||||
$until = time() + (YEARSECS * $mang::TIME_UNLIMITED_YEARS);
|
||||
$untildate = new DateTime();
|
||||
$untildate->add(new DateInterval('P' . $mang::TIME_UNLIMITED_YEARS . 'Y'));
|
||||
$until = $untildate->getTimestamp();
|
||||
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart');
|
||||
|
||||
$expecteddate = new DateTime('first Monday of September 1998');
|
||||
$expecteddate = new DateTime('first Monday of this month');
|
||||
// Move to the next interval's first Monday if the calculated start date is after this month's first Monday.
|
||||
if ($expecteddate->getTimestamp() < $startdatetime->getTimestamp()) {
|
||||
$expecteddate->add($interval);
|
||||
$expecteddate->modify('first Monday of this month');
|
||||
}
|
||||
foreach ($records as $record) {
|
||||
$expecteddate->add($offsetinterval);
|
||||
$this->assertLessThanOrEqual($until, $record->timestart);
|
||||
@@ -890,7 +920,7 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
// Create a yearly event, until the time limit is hit.
|
||||
$until = strtotime('+20 day +10 years', $this->event->timestart);
|
||||
$until = date('Ymd\THis\Z', $until);
|
||||
$rrule = "FREQ=YEARLY;BYMONTH=9;UNTIL=$until"; // Forever event.
|
||||
$rrule = "FREQ=YEARLY;BYMONTH=9;UNTIL=$until";
|
||||
$mang = new rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
@@ -917,18 +947,6 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
$rrule = "FREQ=YEARLY;BYMONTH=9;INTERVAL=2"; // Forever event.
|
||||
$mang = new 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++, $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;COUNT=3;BYMONTH=9;BYDAY=1MO"; // This should generate 3 events in total.
|
||||
$mang = new rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
@@ -1004,12 +1022,45 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for rrule with FREQ=YEARLY and INTERVAL=2 with BYMONTH rule set, recurring forever.
|
||||
*/
|
||||
public function test_yearly_september_every_two_years_forever() {
|
||||
global $DB;
|
||||
|
||||
// Change the start date for forever events to 9am on the 2nd day of September of the current year.
|
||||
$this->change_event_startdate(date('Y0902\T090000'));
|
||||
|
||||
$rrule = "FREQ=YEARLY;BYMONTH=9;INTERVAL=2"; // Forever event.
|
||||
$mang = new rrule_manager($rrule);
|
||||
$untildate = new DateTime();
|
||||
$untildate->add(new DateInterval('P' . $mang::TIME_UNLIMITED_YEARS . 'Y'));
|
||||
$untiltimestamp = $untildate->getTimestamp();
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart');
|
||||
|
||||
$interval = new DateInterval('P2Y');
|
||||
$expecteddate = new DateTime(date('Y0902\T090000'));
|
||||
foreach ($records as $record) {
|
||||
$this->assertLessThanOrEqual($untiltimestamp, $record->timestart);
|
||||
$this->assertEquals($expecteddate->format('Y-m-d H:i:s'), date('Y-m-d H:i:s', $record->timestart));
|
||||
|
||||
// Go to the next expected date.
|
||||
$expecteddate->add($interval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for rrule with FREQ=YEARLY with BYMONTH and BYDAY rules set, recurring forever.
|
||||
*/
|
||||
public function test_yearly_bymonth_byday_forever() {
|
||||
global $DB;
|
||||
|
||||
// Change the start date for forever events to the first day of September of the current year at 9am.
|
||||
$this->change_event_startdate(date('Y0901\T090000'));
|
||||
|
||||
// Every 2 years on the first Monday of September.
|
||||
$rrule = "FREQ=YEARLY;BYMONTH=9;INTERVAL=2;BYDAY=1MO";
|
||||
$mang = new rrule_manager($rrule);
|
||||
@@ -1028,9 +1079,9 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
$offsetinterval = $startdatetime->diff($startdate, true);
|
||||
$interval = new DateInterval('P2Y');
|
||||
|
||||
// First occurrence of this set of events is on the first Monday of September 1999.
|
||||
// First occurrence of this set of events is on the first Monday of September.
|
||||
$expecteddate = clone($startdatetime);
|
||||
$expecteddate->modify('first Monday of September 1999');
|
||||
$expecteddate->modify('first Monday of September');
|
||||
$expecteddate->add($offsetinterval);
|
||||
foreach ($records as $record) {
|
||||
$this->assertLessThanOrEqual($untiltimestamp, $record->timestart);
|
||||
@@ -1050,6 +1101,9 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
public function test_yearly_forever() {
|
||||
global $DB;
|
||||
|
||||
// Change the start date for forever events to 9am of the current date.
|
||||
$this->change_event_startdate(date('Ymd\T090000'));
|
||||
|
||||
$startdatetime = new DateTime(date('Y-m-d H:i:s', $this->event->timestart));
|
||||
|
||||
$interval = new DateInterval('P2Y');
|
||||
@@ -1147,14 +1201,19 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Every other day - forever:
|
||||
*
|
||||
* DTSTART;TZID=US-Eastern:19970902T090000
|
||||
* DTSTART;TZID=US-Eastern:[Current date]T090000
|
||||
* RRULE:FREQ=DAILY;INTERVAL=2
|
||||
* ==> (1997 9:00 AM EDT)September2,4,6,8...24,26,28,30;October 2,4,6...20,22,24
|
||||
* (1997 9:00 AM EST)October 26,28,30;November 1,3,5,7...25,27,29;Dec 1,3,...
|
||||
*
|
||||
* Sample results (e.g. in the year 1997):
|
||||
* (1997 9:00 AM EDT)September2,4,6,8...24,26,28,30;October 2,4,6...20,22,24
|
||||
* (1997 9:00 AM EST)October 26,28,30;November 1,3,5,7...25,27,29;Dec 1,3,...
|
||||
*/
|
||||
public function test_every_other_day_forever() {
|
||||
global $DB;
|
||||
|
||||
// Change the start date for forever events to 9am of the current date in US/Eastern time.
|
||||
$this->change_event_startdate(date('Ymd\T090000'), 'US/Eastern');
|
||||
|
||||
$startdatetime = new DateTime(date('Y-m-d H:i:s', $this->event->timestart));
|
||||
$interval = new DateInterval('P2D');
|
||||
|
||||
@@ -1163,7 +1222,8 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart');
|
||||
// Get the first 100 samples. This should be enough to verify that we have generated the recurring events correctly.
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart', 0, 100);
|
||||
|
||||
$untildate = new DateTime();
|
||||
$untildate->add(new DateInterval('P' . $mang::TIME_UNLIMITED_YEARS . 'Y'));
|
||||
@@ -1341,16 +1401,21 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Every other week - forever:
|
||||
*
|
||||
* DTSTART;TZID=US-Eastern:19970902T090000
|
||||
* DTSTART;TZID=US-Eastern:[Current date]T090000
|
||||
* RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU
|
||||
* ==> (1997 9:00 AM EDT)September 2,16,30;October 14
|
||||
* (1997 9:00 AM EST)October 28;November 11,25;December 9,23
|
||||
* (1998 9:00 AM EST)January 6,20;February
|
||||
* ...
|
||||
*
|
||||
* Sample results (e.g. in the year 1997):
|
||||
* (1997 9:00 AM EDT)September 2,16,30;October 14
|
||||
* (1997 9:00 AM EST)October 28;November 11,25;December 9,23
|
||||
* (1998 9:00 AM EST)January 6,20;February
|
||||
* ...
|
||||
*/
|
||||
public function test_every_other_week_forever() {
|
||||
global $DB;
|
||||
|
||||
// Change the start date for forever events to 9am of the current date in US/Eastern time.
|
||||
$this->change_event_startdate(date('Ymd\T090000'), 'US/Eastern');
|
||||
|
||||
$interval = new DateInterval('P2W');
|
||||
|
||||
$rrule = 'FREQ=WEEKLY;INTERVAL=2;WKST=SU';
|
||||
@@ -1358,7 +1423,8 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart');
|
||||
// Get the first 100 samples. This should be enough to verify that we have generated the recurring events correctly.
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart', 0, 100);
|
||||
|
||||
$untildate = new DateTime();
|
||||
$untildate->add(new DateInterval('P' . $mang::TIME_UNLIMITED_YEARS . 'Y'));
|
||||
@@ -1704,25 +1770,28 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Monthly on the third to the last day of the month, forever:
|
||||
*
|
||||
* DTSTART;TZID=US-Eastern:19970928T090000
|
||||
* DTSTART;TZID=US-Eastern:[Current year]0928T090000
|
||||
* RRULE:FREQ=MONTHLY;BYMONTHDAY=-3
|
||||
* ==> (1997 9:00 AM EDT)September 28
|
||||
* (1997 9:00 AM EST)October 29;November 28;December 29
|
||||
* (1998 9:00 AM EST)January 29;February 26
|
||||
* ...
|
||||
*
|
||||
* Sample results (e.g. in the year 1997):
|
||||
* (1997 9:00 AM EDT)September 28
|
||||
* (1997 9:00 AM EST)October 29;November 28;December 29
|
||||
* (1998 9:00 AM EST)January 29;February 26
|
||||
* ...
|
||||
*/
|
||||
public function test_third_to_the_last_day_of_the_month_forever() {
|
||||
global $DB;
|
||||
|
||||
// Change our event's date to 05-09-1997, based on the example from the RFC.
|
||||
$this->change_event_startdate('19970928T090000', 'US/Eastern');
|
||||
// Change our event's date to 28 September of the current year, based on the example from the RFC.
|
||||
$this->change_event_startdate(date('Y0928\T090000'), 'US/Eastern');
|
||||
|
||||
$rrule = 'FREQ=MONTHLY;BYMONTHDAY=-3';
|
||||
$mang = new rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart');
|
||||
// Get the first 100 samples. This should be enough to verify that we have generated the recurring events correctly.
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart', 0, 100);
|
||||
|
||||
$untildate = new DateTime();
|
||||
$untildate->add(new DateInterval('P' . $mang::TIME_UNLIMITED_YEARS . 'Y'));
|
||||
@@ -1878,22 +1947,29 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Every Tuesday, every other month:
|
||||
*
|
||||
* DTSTART;TZID=US-Eastern:19970902T090000
|
||||
* DTSTART;TZID=US-Eastern:[Next Tuesday]T090000
|
||||
* RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU
|
||||
* ==> (1997 9:00 AM EDT)September 2,9,16,23,30
|
||||
* (1997 9:00 AM EST)November 4,11,18,25
|
||||
* (1998 9:00 AM EST)January 6,13,20,27;March 3,10,17,24,31
|
||||
* ...
|
||||
*
|
||||
* Sample results (e.g. in the year 1997):
|
||||
* (1997 9:00 AM EDT)September 2,9,16,23,30
|
||||
* (1997 9:00 AM EST)November 4,11,18,25
|
||||
* (1998 9:00 AM EST)January 6,13,20,27;March 3,10,17,24,31
|
||||
* ...
|
||||
*/
|
||||
public function test_every_tuesday_every_other_month_forever() {
|
||||
global $DB;
|
||||
|
||||
// Change the start date for forever events to 9am of the Tuesday on or before of the current date in US/Eastern time.
|
||||
$nexttuesday = new DateTime('next Tuesday');
|
||||
$this->change_event_startdate($nexttuesday->format('Ymd\T090000'), 'US/Eastern');
|
||||
|
||||
$rrule = 'FREQ=MONTHLY;INTERVAL=2;BYDAY=TU';
|
||||
$mang = new rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart');
|
||||
// Get the first 100 samples. This should be enough to verify that we have generated the recurring events correctly.
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart', 0, 100);
|
||||
|
||||
$untildate = new DateTime();
|
||||
$untildate->add(new DateInterval('P' . $mang::TIME_UNLIMITED_YEARS . 'Y'));
|
||||
@@ -2063,18 +2139,22 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Every 20th Monday of the year, forever:
|
||||
*
|
||||
* DTSTART;TZID=US-Eastern:19970519T090000
|
||||
* DTSTART;TZID=US-Eastern:[20th Monday of the current year]T090000
|
||||
* RRULE:FREQ=YEARLY;BYDAY=20MO
|
||||
* ==> (1997 9:00 AM EDT)May 19
|
||||
* (1998 9:00 AM EDT)May 18
|
||||
* (1999 9:00 AM EDT)May 17
|
||||
* ...
|
||||
*
|
||||
* Sample results (e.g. in the year 1997):
|
||||
* (1997 9:00 AM EDT)May 19
|
||||
* (1998 9:00 AM EDT)May 18
|
||||
* (1999 9:00 AM EDT)May 17
|
||||
* ...
|
||||
*/
|
||||
public function test_yearly_every_20th_monday_forever() {
|
||||
global $DB;
|
||||
|
||||
// Change our event's date to 19-05-1997, based on the example from the RFC.
|
||||
$startdatetime = $this->change_event_startdate('19970519T090000', 'US/Eastern');
|
||||
// Change our event's date to the 20th Monday of the current year.
|
||||
$twentiethmonday = new DateTime(date('Y-01-01'));
|
||||
$twentiethmonday->modify('+20 Monday');
|
||||
$startdatetime = $this->change_event_startdate($twentiethmonday->format('Ymd\T090000'), 'US/Eastern');
|
||||
|
||||
$startdate = new DateTime($startdatetime->format('Y-m-d'));
|
||||
|
||||
@@ -2110,18 +2190,22 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Monday of week number 20 (where the default start of the week is Monday), forever:
|
||||
*
|
||||
* DTSTART;TZID=US-Eastern:19970512T090000
|
||||
* DTSTART;TZID=US-Eastern:[1st day of the 20th week this year]T090000
|
||||
* RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO
|
||||
* ==> (1997 9:00 AM EDT)May 12
|
||||
* (1998 9:00 AM EDT)May 11
|
||||
* (1999 9:00 AM EDT)May 17
|
||||
* ...
|
||||
*
|
||||
* Sample results (e.g. in the year 1997):
|
||||
* (1997 9:00 AM EDT)May 12
|
||||
* (1998 9:00 AM EDT)May 11
|
||||
* (1999 9:00 AM EDT)May 17
|
||||
* ...
|
||||
*/
|
||||
public function test_yearly_byweekno_forever() {
|
||||
global $DB;
|
||||
|
||||
// Change our event's date to 12-05-1997, based on the example from the RFC.
|
||||
$startdatetime = $this->change_event_startdate('19970512T090000', 'US/Eastern');
|
||||
// Change our event's date to the start of the 20th week of the current year.
|
||||
$twentiethweek = new DateTime(date('Y-01-01'));
|
||||
$twentiethweek->setISODate($twentiethweek->format('Y'), 20);
|
||||
$startdatetime = $this->change_event_startdate($twentiethweek->format('Ymd\T090000'), 'US/Eastern');
|
||||
|
||||
$startdate = clone($startdatetime);
|
||||
$startdate->modify($startdate->format('Y-m-d'));
|
||||
@@ -2156,18 +2240,21 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Every Thursday in March, forever:
|
||||
*
|
||||
* DTSTART;TZID=US-Eastern:19970313T090000
|
||||
* DTSTART;TZID=US-Eastern:[First thursday of March of the current year]T090000
|
||||
* RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH
|
||||
* ==> (1997 9:00 AM EST)March 13,20,27
|
||||
* (1998 9:00 AM EST)March 5,12,19,26
|
||||
* (1999 9:00 AM EST)March 4,11,18,25
|
||||
* ...
|
||||
*
|
||||
* Sample results (e.g. in the year 1997):
|
||||
* (1997 9:00 AM EST)March 13,20,27
|
||||
* (1998 9:00 AM EST)March 5,12,19,26
|
||||
* (1999 9:00 AM EST)March 4,11,18,25
|
||||
* ...
|
||||
*/
|
||||
public function test_every_thursday_in_march_forever() {
|
||||
global $DB;
|
||||
|
||||
// Change our event's date to 12-05-1997, based on the example from the RFC.
|
||||
$startdatetime = $this->change_event_startdate('19970313T090000', 'US/Eastern');
|
||||
// Change our event's date to the first Thursday of March of the current year at 9am US/Eastern time.
|
||||
$firstthursdayofmarch = new DateTime('first Thursday of March');
|
||||
$startdatetime = $this->change_event_startdate($firstthursdayofmarch->format('Ymd\T090000'), 'US/Eastern');
|
||||
|
||||
$interval = new DateInterval('P1Y');
|
||||
|
||||
@@ -2176,7 +2263,8 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart');
|
||||
// Get the first 100 samples. This should be enough to verify that we have generated the recurring events correctly.
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart', 0, 100);
|
||||
|
||||
$untildate = new DateTime();
|
||||
$untildate->add(new DateInterval('P' . $mang::TIME_UNLIMITED_YEARS . 'Y'));
|
||||
@@ -2186,7 +2274,7 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
$startdate = new DateTime($startdatetime->format('Y-m-d'));
|
||||
$offsetinterval = $startdatetime->diff($startdate, true);
|
||||
$expecteddate->setTimezone(new DateTimeZone(get_user_timezone()));
|
||||
$april1st = new DateTime('1997-04-01');
|
||||
$april1st = new DateTime('April 1');
|
||||
foreach ($records as $record) {
|
||||
$this->assertLessThanOrEqual($untiltimestamp, $record->timestart);
|
||||
$this->assertEquals($expecteddate->format('Y-m-d H:i:s'), date('Y-m-d H:i:s', $record->timestart));
|
||||
@@ -2211,18 +2299,21 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Every Thursday, but only during June, July, and August, forever:
|
||||
*
|
||||
* DTSTART;TZID=US-Eastern:19970605T090000
|
||||
* DTSTART;TZID=US-Eastern:[First Thursday of June of the current year]T090000
|
||||
* RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8
|
||||
* ==> (1997 9:00 AM EDT)June 5,12,19,26;July 3,10,17,24,31;August 7,14,21,28
|
||||
* (1998 9:00 AM EDT)June 4,11,18,25;July 2,9,16,23,30;August 6,13,20,27
|
||||
* (1999 9:00 AM EDT)June 3,10,17,24;July 1,8,15,22,29;August 5,12,19,26
|
||||
* ...
|
||||
*
|
||||
* Sample results (e.g. in the year 1997):
|
||||
* (1997 9:00 AM EDT)June 5,12,19,26;July 3,10,17,24,31;August 7,14,21,28
|
||||
* (1998 9:00 AM EDT)June 4,11,18,25;July 2,9,16,23,30;August 6,13,20,27
|
||||
* (1999 9:00 AM EDT)June 3,10,17,24;July 1,8,15,22,29;August 5,12,19,26
|
||||
* ...
|
||||
*/
|
||||
public function test_every_thursday_june_july_august_forever() {
|
||||
global $DB;
|
||||
|
||||
// Change our event's date to 05-06-1997, based on the example from the RFC.
|
||||
$startdatetime = $this->change_event_startdate('19970605T090000', 'US/Eastern');
|
||||
// Change our event's date to the first Thursday of June in the current year at 9am US/Eastern time.
|
||||
$firstthursdayofjune = new DateTime('first Thursday of June');
|
||||
$startdatetime = $this->change_event_startdate($firstthursdayofjune->format('Ymd\T090000'), 'US/Eastern');
|
||||
|
||||
$startdate = new DateTime($startdatetime->format('Y-m-d'));
|
||||
|
||||
@@ -2235,14 +2326,15 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart');
|
||||
// Get the first 100 samples. This should be enough to verify that we have generated the recurring events correctly.
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart', 0, 100);
|
||||
|
||||
$untildate = new DateTime();
|
||||
$untildate->add(new DateInterval('P' . $mang::TIME_UNLIMITED_YEARS . 'Y'));
|
||||
$untiltimestamp = $untildate->getTimestamp();
|
||||
|
||||
$expecteddate = new DateTime(date('Y-m-d H:i:s', $startdatetime->getTimestamp()));
|
||||
$september1st = new DateTime('1997-09-01');
|
||||
$september1st = new DateTime('September 1');
|
||||
foreach ($records as $record) {
|
||||
$this->assertLessThanOrEqual($untiltimestamp, $record->timestart);
|
||||
$this->assertEquals($expecteddate->format('Y-m-d H:i:s'), date('Y-m-d H:i:s', $record->timestart));
|
||||
@@ -2264,22 +2356,28 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Every Friday the 13th, forever:
|
||||
*
|
||||
* DTSTART;TZID=US-Eastern:19970902T090000
|
||||
* EXDATE;TZID=US-Eastern:19970902T090000
|
||||
* DTSTART;TZID=US-Eastern:[Current date]T090000
|
||||
* RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13
|
||||
* ==> (1998 9:00 AM EST)February 13;March 13;November 13
|
||||
* (1999 9:00 AM EDT)August 13
|
||||
* (2000 9:00 AM EDT)October 13
|
||||
*
|
||||
* Sample results (e.g. in the year 1997):
|
||||
* (1998 9:00 AM EST)February 13;March 13;November 13
|
||||
* (1999 9:00 AM EDT)August 13
|
||||
* (2000 9:00 AM EDT)October 13
|
||||
* ...
|
||||
*/
|
||||
public function test_friday_the_thirteenth_forever() {
|
||||
global $DB;
|
||||
|
||||
// Change our event's date to the first Thursday of June in the current year at 9am US/Eastern time.
|
||||
$this->change_event_startdate(date('Ymd\T090000'), 'US/Eastern');
|
||||
|
||||
$rrule = 'FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13';
|
||||
$mang = new rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart');
|
||||
// Get the first 100 samples. This should be enough to verify that we have generated the recurring events correctly.
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart', 0, 100);
|
||||
|
||||
$untildate = new DateTime();
|
||||
$untildate->add(new DateInterval('P' . $mang::TIME_UNLIMITED_YEARS . 'Y'));
|
||||
@@ -2295,17 +2393,22 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
/**
|
||||
* The first Saturday that follows the first Sunday of the month, forever:
|
||||
*
|
||||
* DTSTART;TZID=US-Eastern:19970913T090000
|
||||
* DTSTART;TZID=US-Eastern:[The Saturday after the month's first Sunday]T090000
|
||||
* RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13
|
||||
* ==> (1997 9:00 AM EDT)September 13;October 11
|
||||
* (1997 9:00 AM EST)November 8;December 13
|
||||
* (1998 9:00 AM EST)January 10;February 7;March 7
|
||||
* (1998 9:00 AM EDT)April 11;May 9;June 13...
|
||||
*
|
||||
* Sample results (e.g. from 13 September 1997):
|
||||
* (1997 9:00 AM EDT)September 13;October 11
|
||||
* (1997 9:00 AM EST)November 8;December 13
|
||||
* (1998 9:00 AM EST)January 10;February 7;March 7
|
||||
* (1998 9:00 AM EDT)April 11;May 9;June 13...
|
||||
*/
|
||||
public function test_first_saturday_following_first_sunday_forever() {
|
||||
global $DB;
|
||||
|
||||
$startdatetime = $this->change_event_startdate('19970913T090000', 'US/Eastern');
|
||||
// Change our event's date to the next Saturday after the first Sunday of the the current month at 9am US/Eastern time.
|
||||
$firstsaturdayafterfirstsunday = new DateTime('first Sunday of this month');
|
||||
$firstsaturdayafterfirstsunday->modify('next Saturday');
|
||||
$startdatetime = $this->change_event_startdate($firstsaturdayafterfirstsunday->format('Ymd\T090000'), 'US/Eastern');
|
||||
$startdate = new DateTime($startdatetime->format('Y-m-d'));
|
||||
$offset = $startdatetime->diff($startdate, true);
|
||||
|
||||
@@ -2314,7 +2417,8 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart');
|
||||
// Get the first 100 samples. This should be enough to verify that we have generated the recurring events correctly.
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart', 0, 100);
|
||||
|
||||
$untildate = new DateTime();
|
||||
$untildate->add(new DateInterval('P' . $mang::TIME_UNLIMITED_YEARS . 'Y'));
|
||||
@@ -2340,17 +2444,29 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Every four years, the first Tuesday after a Monday in November, forever (U.S. Presidential Election day):
|
||||
*
|
||||
* DTSTART;TZID=US-Eastern:19961105T090000
|
||||
* DTSTART;TZID=US-Eastern:[Most recent election date]T090000
|
||||
* RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8
|
||||
* ==> (1996 9:00 AM EST)November 5
|
||||
* (2000 9:00 AM EST)November 7
|
||||
* (2004 9:00 AM EST)November 2
|
||||
* ...
|
||||
*
|
||||
* Sample results (e.g. from 05 November 1996):
|
||||
* (1996 9:00 AM EST)November 5
|
||||
* (2000 9:00 AM EST)November 7
|
||||
* (2004 9:00 AM EST)November 2
|
||||
* ...
|
||||
*/
|
||||
public function test_every_us_presidential_election_forever() {
|
||||
global $DB;
|
||||
|
||||
$startdatetime = $this->change_event_startdate('19961105T090000', 'US/Eastern');
|
||||
// Calculate the most recent election date, starting from 1996 (e.g. today's 2017 so the most recent election was in 2016).
|
||||
$currentyear = (int) date('Y');
|
||||
$electionyear = 1996;
|
||||
while ($electionyear + 4 < $currentyear) {
|
||||
$electionyear += 4;
|
||||
}
|
||||
$electiondate = new DateTime('first Monday of November ' . $electionyear);
|
||||
$electiondate->modify('+1 Tuesday');
|
||||
|
||||
// Use the most recent election date as the starting date of our recurring events.
|
||||
$startdatetime = $this->change_event_startdate($electiondate->format('Ymd\T090000'), 'US/Eastern');
|
||||
$startdate = new DateTime($startdatetime->format('Y-m-d'));
|
||||
$offset = $startdatetime->diff($startdate, true);
|
||||
|
||||
@@ -2417,23 +2533,27 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
* The 2nd to last weekday of the month:
|
||||
*
|
||||
* DTSTART;TZID=US-Eastern:19970929T090000
|
||||
* RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2
|
||||
* RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2;COUNT=7
|
||||
* ==> (1997 9:00 AM EDT)September 29
|
||||
* (1997 9:00 AM EST)October 30;November 27;December 30
|
||||
* (1998 9:00 AM EST)January 29;February 26;March 30
|
||||
* ...
|
||||
*
|
||||
* (Original RFC example is set to recur forever. But we just want to verify that the results match the dates listed from
|
||||
* the RFC example. So just limit the count to 7.)
|
||||
*/
|
||||
public function test_second_to_the_last_weekday_of_the_month_forever() {
|
||||
public function test_second_to_the_last_weekday_of_the_month() {
|
||||
global $DB;
|
||||
|
||||
$this->change_event_startdate('19970929T090000', 'US/Eastern');
|
||||
|
||||
$rrule = 'FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2';
|
||||
$rrule = 'FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2;COUNT=7';
|
||||
$mang = new rrule_manager($rrule);
|
||||
$mang->parse_rrule();
|
||||
$mang->create_events($this->event);
|
||||
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart');
|
||||
// Get the first 7 samples. This should be enough to verify that we have generated the recurring events correctly.
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart', 0, 7);
|
||||
|
||||
$expecteddates = [
|
||||
(new DateTime('1997-09-29 09:00:00 EDT'))->getTimestamp(),
|
||||
@@ -2454,10 +2574,8 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
$this->assertLessThanOrEqual($untiltimestamp, $record->timestart);
|
||||
|
||||
// Confirm that the first 7 records correspond to the expected dates listed above.
|
||||
if ($i < 7) {
|
||||
$this->assertEquals($expecteddates[$i], $record->timestart);
|
||||
$i++;
|
||||
}
|
||||
$this->assertEquals($expecteddates[$i], $record->timestart);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2757,13 +2875,19 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
/**
|
||||
* Change the event's timestart (DTSTART) based on the test's needs.
|
||||
*
|
||||
* @param string $datestr The date string. In YYYYmmddThhiiss format. e.g. 19990902T090000.
|
||||
* @param string $timezonestr A valid timezone string. e.g. 'US/Eastern'.
|
||||
* @param string $datestr The date string. In 'Ymd\This' format. e.g. 19990902T090000.
|
||||
* @param null|string $timezonestr A valid timezone string. e.g. 'US/Eastern'.
|
||||
* If not provided, the default timezone will be used.
|
||||
* @return bool|DateTime
|
||||
*/
|
||||
protected function change_event_startdate($datestr, $timezonestr) {
|
||||
$timezone = new DateTimeZone($timezonestr);
|
||||
$newdatetime = DateTime::createFromFormat('Ymd\THis', $datestr, $timezone);
|
||||
protected function change_event_startdate($datestr, $timezonestr = null) {
|
||||
// Use default timezone if not provided.
|
||||
if ($timezonestr === null) {
|
||||
$newdatetime = DateTime::createFromFormat('Ymd\THis', $datestr);
|
||||
} else {
|
||||
$timezone = new DateTimeZone($timezonestr);
|
||||
$newdatetime = DateTime::createFromFormat('Ymd\THis', $datestr, $timezone);
|
||||
}
|
||||
|
||||
// Update the start date of the parent event.
|
||||
$calevent = calendar_event::load($this->event->id);
|
||||
|
||||
+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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user