filter/algebra/AlgParser.pm
filter/tex/mimetex.*
h5p/h5plib/v124/joubel/core/
+h5p/h5plib/v124/joubel/editor/
lib/editor/atto/plugins/html/yui/src/codemirror/
lib/editor/atto/plugins/html/yui/src/beautify/
lib/editor/atto/yui/src/rangy/js/*.*
lib/babel-polyfill/
lib/polyfills/
lib/emoji-data/
+lib/plist/
media/player/videojs/amd/src/video-lazy.js
media/player/videojs/amd/src/Youtube-lazy.js
media/player/videojs/videojs/
--- /dev/null
+# Primary donations pages.
+custom: ["https://moodle.com/donations/", moodle.org]
filter/algebra/AlgParser.pm
filter/tex/mimetex.*
h5p/h5plib/v124/joubel/core/
+h5p/h5plib/v124/joubel/editor/
lib/editor/atto/plugins/html/yui/src/codemirror/
lib/editor/atto/plugins/html/yui/src/beautify/
lib/editor/atto/yui/src/rangy/js/*.*
lib/babel-polyfill/
lib/polyfills/
lib/emoji-data/
+lib/plist/
media/player/videojs/amd/src/video-lazy.js
media/player/videojs/amd/src/Youtube-lazy.js
media/player/videojs/videojs/
# process (which uses our internal CI system) this file is here for the benefit
# of community developers git clones - see MDL-51458.
-sudo: required
-
# We currently disable Travis notifications entirely until https://github.com/travis-ci/travis-ci/issues/4976
# is fixed.
notifications:
language: php
+os: linux
+
dist: xenial
services:
# Perform an upgrade test too.
- DB=pgsql TASK=UPGRADE
-matrix:
+jobs:
# Enable fast finish.
# This will fail the build if a single job fails (except those in allow_failures).
# It will not stop the jobs from running.
eslint: {
// Even though warnings dont stop the build we don't display warnings by default because
// at this moment we've got too many core warnings.
- options: {quiet: !grunt.option('show-lint-warnings')},
+ // To display warnings call: grunt eslint --show-lint-warnings
+ // To fail on warnings call: grunt eslint --max-lint-warnings=0
+ // Also --max-lint-warnings=-1 can be used to display warnings but not fail.
+ options: {
+ quiet: (!grunt.option('show-lint-warnings')) && (typeof grunt.option('max-lint-warnings') === 'undefined'),
+ maxWarnings: ((typeof grunt.option('max-lint-warnings') !== 'undefined') ? grunt.option('max-lint-warnings') : -1)
+ },
amd: {src: files ? files : amdSrc},
// Check YUI module source files.
yui: {src: files ? files : yuiSrc},
formatter.printResults(results);
// Report on the results.
- // We exit 1 if there is at least one error, otherwise we exit cleanly.
- if (results.some(result => result.errors.length > 0)) {
- done(1);
- } else {
- done(0);
- }
+ // The done function takes a bool whereby a falsey statement causes the task to fail.
+ done(results.every(result => result.errors.length === 0));
};
tasks.startup = function() {
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Content bank and its plugins settings.
+ *
+ * @package core
+ * @subpackage contentbank
+ * @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../config.php');
+require_once($CFG->libdir.'/adminlib.php');
+
+$action = required_param('action', PARAM_ALPHANUMEXT);
+$name = required_param('name', PARAM_PLUGIN);
+
+$syscontext = context_system::instance();
+$PAGE->set_url('/admin/contentbank.php');
+$PAGE->set_context($syscontext);
+
+require_admin();
+require_sesskey();
+
+$return = new moodle_url('/admin/settings.php', array('section' => 'managecontentbanktypes'));
+
+$plugins = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
+$sortorder = array_flip(array_keys($plugins));
+
+if (!isset($plugins[$name])) {
+ print_error('contenttypenotfound', 'error', $return, $name);
+}
+
+switch ($action) {
+ case 'disable':
+ if ($plugins[$name]->is_enabled()) {
+ set_config('disabled', 1, 'contentbank_'. $name);
+ }
+ break;
+ case 'enable':
+ if (!$plugins[$name]->is_enabled()) {
+ unset_config('disabled', 'contentbank_'. $name);
+ }
+ break;
+ case 'up':
+ if ($sortorder[$name]) {
+ $currentindex = $sortorder[$name];
+ $seq = array_keys($plugins);
+ $seq[$currentindex] = $seq[$currentindex - 1];
+ $seq[$currentindex - 1] = $name;
+ set_config('contentbank_plugins_sortorder', implode(',', $seq));
+ }
+ break;
+ case 'down':
+ if ($sortorder[$name] < count($sortorder) - 1) {
+ $currentindex = $sortorder[$name];
+ $seq = array_keys($plugins);
+ $seq[$currentindex] = $seq[$currentindex + 1];
+ $seq[$currentindex + 1] = $name;
+ set_config('contentbank_plugins_sortorder', implode(',', $seq));
+ }
+ break;
+}
+core_plugin_manager::reset_caches();
+$cache = cache::make('core', 'contentbank_enabled_extensions');
+$cache->purge();
+$cache = cache::make('core', 'contentbank_context_extensions');
+$cache->purge();
+
+redirect($return);
}
} else {
$newsettings = array();
- if (array_key_exists($componentprovidersetting, $form)) {
+ if (property_exists($form, $componentprovidersetting)) {
// We must be processing loggedin or loggedoff checkboxes.
// Store defained comma-separated processors as setting value.
// Using array_filter eliminates elements set to 0 above.
}
}
- $updateinfo .= $this->container_start('checkforupdates');
+ $updateinfo .= $this->container_start('checkforupdates mt-1');
$fetchurl = new moodle_url('/admin/index.php', array('fetchupdates' => 1, 'sesskey' => sesskey(), 'cache' => 0));
$updateinfo .= $this->single_button($fetchurl, get_string('checkforupdates', 'core_plugin'));
if ($fetch) {
*/
protected function moodle_available_update_info(\core\update\info $updateinfo) {
- $boxclasses = 'moodleupdateinfo';
+ $boxclasses = 'moodleupdateinfo mb-2';
$info = array();
if (isset($updateinfo->release)) {
}
if (isset($updateinfo->download)) {
- $info[] = html_writer::link($updateinfo->download, get_string('download'), array('class' => 'info download'));
+ $info[] = html_writer::link($updateinfo->download, get_string('download'),
+ array('class' => 'info download btn btn-secondary'));
}
if (isset($updateinfo->url)) {
array('class' => 'info more'));
}
- $box = $this->output->box_start($boxclasses);
- $box .= $this->output->box(implode(html_writer::tag('span', ' ', array('class' => 'separator')), $info), '');
- $box .= $this->output->box_end();
+ $box = $this->output->container_start($boxclasses);
+ $box .= $this->output->container(implode(html_writer::tag('span', ' | ', array('class' => 'separator')), $info), '');
+ $box .= $this->output->container_end();
return $box;
}
$temp = new admin_settingpage('experimentalsettings', new lang_string('experimentalsettings', 'admin'));
//TODO: Re-enable cc-import once re-implemented in 2.0.x
//$temp->add(new admin_setting_configcheckbox('enableimsccimport', new lang_string('enable_cc_import', 'imscc'), new lang_string('enable_cc_import_description', 'imscc'), 0));
- $temp->add(new admin_setting_configcheckbox('enablesafebrowserintegration', new lang_string('enablesafebrowserintegration', 'admin'), new lang_string('configenablesafebrowserintegration', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('dndallowtextandlinks', new lang_string('dndallowtextandlinks', 'admin'), new lang_string('configdndallowtextandlinks', 'admin'), 0));
}
}
+// Content bank content types.
+if ($hassiteconfig) {
+ $ADMIN->add('modules', new admin_category('contenbanksettings', new lang_string('contentbank')));
+ $temp = new admin_settingpage('managecontentbanktypes', new lang_string('managecontentbanktypes'));
+ $temp->add(new admin_setting_managecontentbankcontenttypes());
+ $ADMIN->add('contenbanksettings', $temp);
+ $plugins = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
+ foreach ($plugins as $plugin) {
+ /** @var \core\plugininfo\contentbank $plugin */
+ $plugin->load_settings($ADMIN, 'contenbanksettings', $hassiteconfig);
+ }
+}
+
/// Add all local plugins - must be always last!
if ($hassiteconfig) {
$ADMIN->add('modules', new admin_category('localplugins', new lang_string('localplugins')));
$emailuser->email = $data->recipient;
$emailuser->id = -99;
- $subject = get_string('testoutgoingmailconf_subject', 'admin', $SITE->fullname);
+ $subject = get_string('testoutgoingmailconf_subject', 'admin',
+ format_string($SITE->fullname, true, ['context' => context_system::instance()]));
$messagetext = get_string('testoutgoingmailconf_message', 'admin');
// Manage Moodle debugging options.
'core/str',
'core/modal_factory',
'core/modal_events',
- 'core/templates'],
-function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates) {
+ 'core/templates',
+ 'core/pending'],
+function($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, Pending) {
/**
* List of action selectors.
});
$(ACTIONS.CONTACT_DPO).click(function(e) {
+ var pendingPromise = new Pending('dataprivacy/crud:initModal:contactdpo');
e.preventDefault();
var replyToEmail = $(this).data('replytoemail');
type: ModalFactory.types.SAVE_CANCEL,
large: true
});
- }).done(function(modal) {
+ }).then(function(modal) {
modal.setSaveButtonText(sendButtonText);
+ // Show the modal!
+ modal.show();
+
// Handle send event.
modal.getRoot().on(ModalEvents.save, function(e) {
var message = $('#message').val().trim();
modal.destroy();
});
- // Show the modal!
- modal.show();
- }).fail(Notification.exception);
+ return;
+ }).then(pendingPromise.resolve)
+ .catch(Notification.exception);
});
};
$string['classname'] = 'Class name';
$string['component'] = 'Component';
-$string['configmessageinboundhost'] = 'The address of the server that Moodle should check mail against. To specify a non-default port, use [server]:[port], for example mail.example.com:587. If a port isn\'t specified, the default port for the type of mail server will be used.';
+$string['configmessageinboundhost'] = 'The address of the server that Moodle should check mail against. To specify a non-default port, use [server]:[port], for example mail.example.com:993. If a port isn\'t specified, the default port for the type of mail server will be used.';
$string['defaultexpiration'] = 'Default address expiry period';
$string['defaultexpiration_help'] = 'When an email address is generated by the handler, it can be set to automatically expire after a period of time, so that it can no longer be used. It is advisable to set an expiry period.';
$string['description'] = 'Description';
And I press "Yes"
And I should see "Recycle bin has been emptied"
And I should see "There are no items in the recycle bin."
+
+ @javascript
+ Scenario: Show recycle bin on category action menu
+ Given I log in as "admin"
+ And I navigate to "Courses > Manage courses and categories" in site administration
+ And I click on "Actions menu" "link"
+ And I click on "Recycle bin" "link"
+ Then I should see "There are no items in the recycle bin."
+
+ @javascript
+ Scenario: Not show recycle bin empty on category action menu whit autohide enable
+ Given I log in as "admin"
+ And the following config values are set as admin:
+ | categorybinenable | 0 | tool_recyclebin |
+ And I navigate to "Courses > Manage courses and categories" in site administration
+ And I click on "Actions menu" "link"
+ Then I should not see "Recycle bin"
+
+ @javascript
+ Scenario: Show recycle bin not empty on category action menu whit autohide enable
+ Given I log in as "admin"
+ And the following config values are set as admin:
+ | autohide | 1 | tool_recyclebin |
+ And I navigate to "Courses > Manage courses and categories" in site administration
+ And I click on "Actions menu" "link"
+ Then I should not see "Recycle bin"
+ And I click on "delete" action for "Course 2" in management course listing
+ And I press "Delete"
+ And I should see "Deleting C2"
+ And I should see "C2 has been completely deleted"
+ And I press "Continue"
+ When I click on "Actions menu" "link"
+ Then I should see "Recycle bin"
*/
$string['asap'] = 'ASAP';
-$string['adhocempty'] = 'Adhoc task queue is empty';
-$string['adhocqueuesize'] = 'Adhoc task queue has {$a} tasks';
+$string['adhocempty'] = 'Ad hoc task queue is empty';
+$string['adhocqueuesize'] = 'Ad hoc task queue has {$a} tasks';
$string['adhocqueueold'] = 'Oldest task is {$a->age} which is more than {$a->max}';
$string['backtoscheduledtasks'] = 'Back to scheduled tasks';
$string['blocking'] = 'Blocking';
$string['cannotfindthepathtothecli'] = 'Cannot find the path to the PHP CLI executable so task execution aborted. Set the \'Path to PHP CLI\' setting in Site administration / Server / System paths.';
-$string['checkadhocqueue'] = 'Adhoc task queue';
+$string['checkadhocqueue'] = 'Ad hoc task queue';
$string['checkcronrunning'] = 'Cron running';
$string['checkmaxfaildelay'] = 'Tasks max fail delay';
$string['clearfaildelay_confirm'] = 'Are you sure you want to clear the fail delay for task \'{$a}\'? After clearing the delay, the task will run according to its normal schedule.';
$string['scheduledtasks'] = 'Scheduled tasks';
$string['scheduledtaskchangesdisabled'] = 'Modifications to the list of scheduled tasks have been prevented in Moodle configuration';
$string['taskdisabled'] = 'Task disabled';
-$string['taskfailures'] = 'There are {$a} task(s) failing';
+$string['taskfailures'] = '{$a} task(s) failing';
$string['tasklogs'] = 'Task logs';
$string['tasknofailures'] = 'There are no tasks failing';
$string['taskscheduleday'] = 'Day';
And I should not see "Clear" in the "Send new user passwords" "table_row"
And I should see "Send new user passwords" in the "tr.table-primary" "css_element"
-
Scenario: Cancel clearing the fail delay
When I click on "Clear" "text" in the "Send new user passwords" "table_row"
And I press "Cancel"
// Get needed strings
$this->loadStrings(array(
+ 'extraindexesfound' => 'tool_xmldb',
'missing' => 'tool_xmldb',
'key' => 'tool_xmldb',
'index' => 'tool_xmldb',
'missingindexes' => 'tool_xmldb',
- 'nomissingindexesfound' => 'tool_xmldb',
+ 'nomissingorextraindexesfound' => 'tool_xmldb',
+ 'yesextraindexesfound' => 'tool_xmldb',
'yesmissingindexesfound' => 'tool_xmldb',
));
}
$dbman = $DB->get_manager();
$o = '';
+ $dbindexes = $DB->get_indexes($xmldb_table->getName());
$missing_indexes = array();
// Keys
// Check if the index exists in DB
if ($dbman->index_exists($xmldb_table, $xmldb_index)) {
$o.='<font color="green">' . $this->str['ok'] . '</font>';
+ $this->remove_index_from_dbindex($dbindexes, $xmldb_index);
} else {
$o.='<font color="red">' . $this->str['missing'] . '</font>';
// Add the missing index to the list
// Check if the index exists in DB
if ($dbman->index_exists($xmldb_table, $xmldb_index)) {
$o.='<font color="green">' . $this->str['ok'] . '</font>';
+ $this->remove_index_from_dbindex($dbindexes, $xmldb_index);
} else {
$o.='<font color="red">' . $this->str['missing'] . '</font>';
// Add the missing index to the list
$o.=' </ul>';
}
+ // Hack - skip for table 'search_simpledb_index' as this plugin adds indexes dynamically on install
+ // which are not included in install.xml. See search/engine/simpledb/db/install.php.
+ if ($xmldb_table->getName() != 'search_simpledb_index') {
+ foreach ($dbindexes as $indexname => $index) {
+ $missing_indexes[] = $indexname;
+ }
+ }
+
return array($o, $missing_indexes);
}
global $DB;
$dbman = $DB->get_manager();
+ $missingindexes = [];
+ $extraindexes = [];
+
+ foreach ($missing_indexes as $missingindex) {
+ if (is_object($missingindex)) {
+ $missingindexes[] = $missingindex;
+ } else {
+ $extraindexes[] = $missingindex;
+ }
+ }
+
$s = '';
$r = '<table class="generaltable boxaligncenter boxwidthwide" border="0" cellpadding="5" cellspacing="0" id="results">';
$r.= ' <tr><td class="generalboxcontent">';
$r.= ' <h2 class="main">' . $this->str['searchresults'] . '</h2>';
- $r.= ' <p class="centerpara">' . $this->str['missingindexes'] . ': ' . count($missing_indexes) . '</p>';
+ $r .= ' <p class="centerpara">' . $this->str['missingindexes'] . ': ' . count($missingindexes) . '</p>';
+ $r .= ' <p class="centerpara">' . $this->str['extraindexesfound'] . ': ' . count($extraindexes) . '</p>';
$r.= ' </td></tr>';
$r.= ' <tr><td class="generalboxcontent">';
- // If we have found missing indexes inform about them
- if (count($missing_indexes)) {
- $r.= ' <p class="centerpara">' . $this->str['yesmissingindexesfound'] . '</p>';
- $r.= ' <ul>';
- foreach ($missing_indexes as $obj) {
- $xmldb_table = $obj->table;
- $xmldb_index = $obj->index;
- $sqlarr = $dbman->generator->getAddIndexSQL($xmldb_table, $xmldb_index);
- $r.= ' <li>' . $this->str['table'] . ': ' . $xmldb_table->getName() . '. ' .
- $this->str['index'] . ': ' . $xmldb_index->readableInfo() . '</li>';
- $sqlarr = $dbman->generator->getEndedStatements($sqlarr);
- $s.= '<code>' . str_replace("\n", '<br />', implode('<br />', $sqlarr)) . '</code><br />';
+ // If we have found missing indexes or extra indexes inform the user about them.
+ if (!empty($missingindexes) || !empty($extraindexes)) {
+ if ($missingindexes) {
+ $r.= ' <p class="centerpara">' . $this->str['yesmissingindexesfound'] . '</p>';
+ $r.= ' <ul>';
+ foreach ($missingindexes as $obj) {
+ $xmldb_table = $obj->table;
+ $xmldb_index = $obj->index;
+ $sqlarr = $dbman->generator->getAddIndexSQL($xmldb_table, $xmldb_index);
+ $r.= ' <li>' . $this->str['table'] . ': ' . $xmldb_table->getName() . '. ' .
+ $this->str['index'] . ': ' . $xmldb_index->readableInfo() . '</li>';
+ $sqlarr = $dbman->generator->getEndedStatements($sqlarr);
+ $s.= '<code>' . str_replace("\n", '<br />', implode('<br />', $sqlarr)) . '</code><br />';
+ }
+ $r.= ' </ul>';
+ // Add the SQL statements (all together)
+ $r.= '<hr />' . $s;
+ }
+ if ($extraindexes) {
+ $r .= '<p class="centerpara">' . $this->str['yesextraindexesfound'] . '</p>';
+ $r .= '<ul>';
+ foreach ($extraindexes as $ei) {
+ $r .= '<li>' . $ei . '</li>';
+ }
+ $r .= '</ul>';
+ $r .= '<hr />';
}
- $r.= ' </ul>';
- // Add the SQL statements (all together)
- $r.= '<hr />' . $s;
} else {
- $r.= ' <p class="centerpara">' . $this->str['nomissingindexesfound'] . '</p>';
+ $r .= '<p class="centerpara">' . $this->str['nomissingorextraindexesfound'] . '</p>';
}
$r.= ' </td></tr>';
$r.= ' <tr><td class="generalboxcontent">';
return $r;
}
+
+ /**
+ * Removes an index from the array $dbindexes if it is found.
+ *
+ * @param array $dbindexes
+ * @param xmldb_index $index
+ */
+ private function remove_index_from_dbindex(array &$dbindexes, xmldb_index $index) {
+ foreach ($dbindexes as $key => $dbindex) {
+ if ($dbindex['columns'] == $index->getFields()) {
+ unset($dbindexes[$key]);
+ }
+ }
+ }
}
$string['enumvaluesincorrect'] = 'Incorrect values for enum field';
$string['expected'] = 'Expected';
$string['extensionrequired'] = 'Sorry - the PHP extension \'{$a}\' is required for this action. Please install the extension if you want to use this feature.';
+$string['extraindexesfound'] = 'Extra indexes found';
$string['field'] = 'Field';
$string['fieldnameempty'] = 'Name field empty';
$string['fields'] = 'Fields';
$string['new_table_from_mysql'] = 'New table from MySQL';
$string['nofieldsspecified'] = 'No fields specified';
$string['nomasterprimaryuniquefound'] = 'The column(s) that your foreign key references must be included in a primary or unique KEY in the referenced table. Note that the column being in a UNIQUE INDEX is not good enough.';
-$string['nomissingindexesfound'] = 'No missing indexes have been found, your DB doesn\'t need further actions.';
+$string['nomissingorextraindexesfound'] = 'No missing or extra indexes have been found, so no further action is required.';
$string['noreffieldsspecified'] = 'No reference fields specified';
$string['noreftablespecified'] = 'Specified reference table not found';
$string['noviolatedforeignkeysfound'] = 'No violated foreign keys found';
$string['wrongnumberofreffields'] = 'Wrong number of reference fields';
$string['wrongreservedwords'] = 'Currently used reserved words<br />(note that table names aren\'t important if using $CFG->prefix)';
$string['wrongoraclesemantics'] = 'Wrong Oracle BYTE semantics found';
+$string['yesextraindexesfound'] = 'The following additional indexes were found.';
$string['yesmissingindexesfound'] = '<p>Some missing indexes have been found in your DB. Here are their details and the needed SQL statements to be executed with your favourite SQL interface to create all of them. Remember to backup your data first!</p>
<p>After doing that, it\'s highly recommended to execute this utility again to check that no more missing indexes are found.</p>';
$string['yeswrongdefaultsfound'] = '<p>Some inconsistent defaults have been found in your DB. Here are their details and the needed SQL statements to be executed with your favourite SQL interface to fix them all. Remember to backup your data first!</p>
// Note: for DB deleted users md5(username) is stored *sometimes* in the email field,
// hence we are looking there for usernames if not empty. See delete_user()
// If match by id and mnethost and user is deleted in DB and
- // match by username LIKE 'backup_email.%' or by non empty email = md5(username) => ok, return target user
+ // match by username LIKE 'substring(backup_email).%' where the substr length matches the retained data in the
+ // username field (100 - (timestamp + 1) characters), or by non empty email = md5(username) => ok, return target user.
+ $usernamelookup = core_text::substr($user->email, 0, 89) . '.%';
if ($rec = $DB->get_record_sql("SELECT *
FROM {user} u
WHERE id = ?
AND email = ?
)
)",
- array($user->id, $user->mnethostid, $user->email.'.%', md5($user->username)))) {
+ array($user->id, $user->mnethostid, $usernamelookup, md5($user->username)))) {
return $rec; // Matching user, deleted in DB found, return it
}
// 1D - Handle users deleted in backup file and "alive" in DB
// If match by id and mnethost and user is deleted in backup file
- // and match by email = email_without_time(backup_email) => ok, return target user
+ // and match by substring(email) = email_without_time(backup_email) where the substr length matches the retained data
+ // in the username field (100 - (timestamp + 1) characters) => ok, return target user.
if ($user->deleted) {
// Note: for DB deleted users email is stored in username field, hence we
// are looking there for emails. See delete_user()
FROM {user} u
WHERE id = ?
AND mnethostid = ?
- AND UPPER(email) = UPPER(?)",
+ AND " . $DB->sql_substr('UPPER(email)', 1, 89) . " = UPPER(?)",
array($user->id, $user->mnethostid, $trimemail))) {
return $rec; // Matching user, deleted in backup file found, return it
}
// Note: for DB deleted users md5(username) is stored *sometimes* in the email field,
// hence we are looking there for usernames if not empty. See delete_user()
// 2B1 - If match by mnethost and user is deleted in DB and not empty email = md5(username) and
- // (by username LIKE 'backup_email.%' or non-zero firstaccess) => ok, return target user
+ // (by username LIKE 'substring(backup_email).%' or non-zero firstaccess) => ok, return target user.
+ $usernamelookup = core_text::substr($user->email, 0, 89) . '.%';
if ($rec = $DB->get_record_sql("SELECT *
FROM {user} u
WHERE mnethostid = ?
AND firstaccess = ?
)
)",
- array($user->mnethostid, md5($user->username), $user->email.'.%', $user->firstaccess))) {
+ array($user->mnethostid, md5($user->username), $usernamelookup, $user->firstaccess))) {
return $rec; // Matching user found, return it
}
// 2B2 - If match by mnethost and user is deleted in DB and
- // username LIKE 'backup_email.%' and non-zero firstaccess) => ok, return target user
+ // username LIKE 'substring(backup_email).%' and non-zero firstaccess) => ok, return target user
// (this covers situations where md5(username) wasn't being stored so we require both
// the email & non-zero firstaccess to match)
+ $usernamelookup = core_text::substr($user->email, 0, 89) . '.%';
if ($rec = $DB->get_record_sql("SELECT *
FROM {user} u
WHERE mnethostid = ?
AND UPPER(username) LIKE UPPER(?)
AND firstaccess != 0
AND firstaccess = ?",
- array($user->mnethostid, $user->email.'.%', $user->firstaccess))) {
+ array($user->mnethostid, $usernamelookup, $user->firstaccess))) {
return $rec; // Matching user found, return it
}
// 2C - Handle users deleted in backup file and "alive" in DB
// If match mnethost and user is deleted in backup file
- // and match by email = email_without_time(backup_email) and non-zero firstaccess=> ok, return target user
+ // and match by substring(email) = email_without_time(backup_email) and non-zero firstaccess=> ok, return target user.
if ($user->deleted) {
// Note: for DB deleted users email is stored in username field, hence we
// are looking there for emails. See delete_user()
if ($rec = $DB->get_record_sql("SELECT *
FROM {user} u
WHERE mnethostid = ?
- AND UPPER(email) = UPPER(?)
+ AND " . $DB->sql_substr('UPPER(email)', 1, 89) . " = UPPER(?)
AND firstaccess != 0
AND firstaccess = ?",
array($user->mnethostid, $trimemail, $user->firstaccess))) {
$this->assertSame('Table "backup_ids_temp" does not exist', $e->getMessage());
}
}
+
+ /**
+ * Data provider for {@link test_precheck_user()}
+ */
+ public function precheck_user_provider() {
+
+ $emailmultiplier = [
+ 'shortmail' => 'normalusername@example.com',
+ 'longmail' => str_repeat('a', 100) // It's not validated, hence any string is ok.
+ ];
+
+ $providercases = [];
+
+ foreach ($emailmultiplier as $emailk => $email) {
+ // Get the related cases.
+ $cases = $this->precheck_user_cases($email);
+ // Rename them (keys).
+ foreach ($cases as $key => $case) {
+ $providercases[$key . ' - ' . $emailk] = $case;
+ }
+ }
+
+ return $providercases;
+ }
+
+ /**
+ * Get all the cases implemented in {@link restore_dbops::precheck_users()}
+ *
+ * @param string $email
+ */
+ private function precheck_user_cases($email) {
+ global $CFG;
+
+ $baseuserarr = [
+ 'username' => 'normalusername',
+ 'email' => $email,
+ 'mnethostid' => $CFG->mnet_localhost_id,
+ 'firstaccess' => 123456789,
+ 'deleted' => 0,
+ 'forceemailcleanup' => false, // Hack to force the DB record to have empty mail.
+ 'forceduplicateadminallowed' => false]; // Hack to enable import_general_duplicate_admin_allowed.
+
+ return [
+ // Cases with samesite = true.
+ 'samesite match existing (1A)' => [
+ 'dbuser' => $baseuserarr,
+ 'backupuser' => $baseuserarr,
+ 'samesite' => true,
+ 'outcome' => 'match'
+ ],
+ 'samesite match existing anon (1B)' => [
+ 'dbuser' => array_merge($baseuserarr, [
+ 'username' => 'anon01']),
+ 'backupuser' => array_merge($baseuserarr, [
+ 'id' => -1, 'username' => 'anon01', 'firstname' => 'anonfirstname01',
+ 'lastname' => 'anonlastname01', 'email' => 'anon01@doesntexist.invalid']),
+ 'samesite' => true,
+ 'outcome' => 'match'
+ ],
+ 'samesite match existing deleted in db, alive in backup, by db username (1C)' => [
+ 'dbuser' => array_merge($baseuserarr, [
+ 'deleted' => 1]),
+ 'backupuser' => array_merge($baseuserarr, [
+ 'username' => 'this_wont_match']),
+ 'samesite' => true,
+ 'outcome' => 'match'
+ ],
+ 'samesite match existing deleted in db, alive in backup, by db email (1C)' => [
+ 'dbuser' => array_merge($baseuserarr, [
+ 'deleted' => 1]),
+ 'backupuser' => array_merge($baseuserarr, [
+ 'email' => 'this_wont_match']),
+ 'samesite' => true,
+ 'outcome' => 'match'
+ ],
+ 'samesite match existing alive in db, deleted in backup (1D)' => [
+ 'dbuser' => $baseuserarr,
+ 'backupuser' => array_merge($baseuserarr, [
+ 'deleted' => 1]),
+ 'samesite' => true,
+ 'outcome' => 'match'
+ ],
+ 'samesite conflict (1E)' => [
+ 'dbuser' => $baseuserarr,
+ 'backupuser' => array_merge($baseuserarr, ['id' => -1]),
+ 'samesite' => true,
+ 'outcome' => false
+ ],
+ 'samesite create user (1F)' => [
+ 'dbuser' => $baseuserarr,
+ 'backupuser' => array_merge($baseuserarr, [
+ 'username' => 'newusername']),
+ 'samesite' => false,
+ 'outcome' => true
+ ],
+
+ // Cases with samesite = false.
+ 'no samesite match existing, by db email (2A1)' => [
+ 'dbuser' => $baseuserarr,
+ 'backupuser' => array_merge($baseuserarr, [
+ 'firstaccess' => 0]),
+ 'samesite' => false,
+ 'outcome' => 'match'
+ ],
+ 'no samesite match existing, by db firstaccess (2A1)' => [
+ 'dbuser' => $baseuserarr,
+ 'backupuser' => array_merge($baseuserarr, [
+ 'email' => 'this_wont_match@example.con']),
+ 'samesite' => false,
+ 'outcome' => 'match'
+ ],
+ 'no samesite match existing anon (2A1 too)' => [
+ 'dbuser' => array_merge($baseuserarr, [
+ 'username' => 'anon01']),
+ 'backupuser' => array_merge($baseuserarr, [
+ 'id' => -1, 'username' => 'anon01', 'firstname' => 'anonfirstname01',
+ 'lastname' => 'anonlastname01', 'email' => 'anon01@doesntexist.invalid']),
+ 'samesite' => false,
+ 'outcome' => 'match'
+ ],
+ 'no samesite match dupe admin (2A2)' => [
+ 'dbuser' => array_merge($baseuserarr, [
+ 'username' => 'admin_old_site_id',
+ 'forceduplicateadminallowed' => true]),
+ 'backupuser' => array_merge($baseuserarr, [
+ 'username' => 'admin']),
+ 'samesite' => false,
+ 'outcome' => 'match'
+ ],
+ 'no samesite match existing deleted in db, alive in backup, by db username (2B1)' => [
+ 'dbuser' => array_merge($baseuserarr, [
+ 'deleted' => 1]),
+ 'backupuser' => array_merge($baseuserarr, [
+ 'firstaccess' => 0]),
+ 'samesite' => false,
+ 'outcome' => 'match'
+ ],
+ 'no samesite match existing deleted in db, alive in backup, by db firstaccess (2B1)' => [
+ 'dbuser' => array_merge($baseuserarr, [
+ 'deleted' => 1]),
+ 'backupuser' => array_merge($baseuserarr, [
+ 'mail' => 'this_wont_match']),
+ 'samesite' => false,
+ 'outcome' => 'match'
+ ],
+ 'no samesite match existing deleted in db, alive in backup (2B2)' => [
+ 'dbuser' => array_merge($baseuserarr, [
+ 'deleted' => 1,
+ 'forceemailcleanup' => true]),
+ 'backupuser' => $baseuserarr,
+ 'samesite' => false,
+ 'outcome' => 'match'
+ ],
+ 'no samesite match existing alive in db, deleted in backup (2C)' => [
+ 'dbuser' => $baseuserarr,
+ 'backupuser' => array_merge($baseuserarr, [
+ 'deleted' => 1]),
+ 'samesite' => false,
+ 'outcome' => 'match'
+ ],
+ 'no samesite conflict (2D)' => [
+ 'dbuser' => $baseuserarr,
+ 'backupuser' => array_merge($baseuserarr, [
+ 'email' => 'anotheruser@example.com', 'firstaccess' => 0]),
+ 'samesite' => false,
+ 'outcome' => false
+ ],
+ 'no samesite create user (2E)' => [
+ 'dbuser' => $baseuserarr,
+ 'backupuser' => array_merge($baseuserarr, [
+ 'username' => 'newusername']),
+ 'samesite' => false,
+ 'outcome' => true
+ ],
+
+ ];
+ }
+
+ /**
+ * Test restore precheck_user method
+ *
+ * @dataProvider precheck_user_provider
+ * @covers restore_dbops::precheck_user()
+ *
+ * @param array $dbuser
+ * @param array $backupuser
+ * @param bool $samesite
+ * @param mixed $outcome
+ **/
+ public function test_precheck_user($dbuser, $backupuser, $samesite, $outcome) {
+ global $DB;
+
+ $this->resetAfterTest();
+
+ $dbuser = (object)$dbuser;
+ $backupuser = (object)$backupuser;
+
+ $siteid = null;
+
+ // If the backup user must be deleted, simulate it (by temp inserting to DB, deleting and fetching it back).
+ if ($backupuser->deleted) {
+ $backupuser->id = $DB->insert_record('user', array_merge((array)$backupuser, ['deleted' => 0]));
+ delete_user($backupuser);
+ $backupuser = $DB->get_record('user', ['id' => $backupuser->id]);
+ $DB->delete_records('user', ['id' => $backupuser->id]);
+ unset($backupuser->id);
+ }
+
+ // Create the db user, normally.
+ $dbuser->id = $DB->insert_record('user', array_merge((array)$dbuser, ['deleted' => 0]));
+ $backupuser->id = $backupuser->id ?? $dbuser->id;
+
+ // We may want to enable the import_general_duplicate_admin_allowed setting and look for old admin records.
+ if ($dbuser->forceduplicateadminallowed) {
+ set_config('import_general_duplicate_admin_allowed', true, 'backup');
+ $siteid = 'old_site_id';
+ }
+
+ // If the DB user must be deleted, do it and fetch it back.
+ if ($dbuser->deleted) {
+ delete_user($dbuser);
+ // We may want to clean the mail field (old behavior, not containing the current md5(username).
+ if ($dbuser->forceemailcleanup) {
+ $DB->set_field('user', 'email', '', ['id' => $dbuser->id]);
+ }
+ }
+
+ // Get the dbuser record, because we may have changed it above.
+ $dbuser = $DB->get_record('user', ['id' => $dbuser->id]);
+
+ $method = (new ReflectionClass('restore_dbops'))->getMethod('precheck_user');
+ $method->setAccessible(true);
+ $result = $method->invoke(null, $backupuser, $samesite, $siteid);
+
+ if (is_bool($result)) {
+ $this->assertSame($outcome, $result);
+ } else {
+ $outcome = $dbuser; // Outcome is not bool, matching found, so it must be the dbuser,
+ // Just check ids, it means the expected match has been found in database.
+ $this->assertSame($outcome->id, $result->id);
+ }
+ }
}
$archetypes = array();
foreach($modinfo->cms as $cm) {
- // Exclude activities which are not visible or have no link (=label)
- if (!$cm->uservisible or !$cm->has_view()) {
+ // Exclude activities that aren't visible or have no view link (e.g. label). Account for folder being displayed inline.
+ if (!$cm->uservisible || (!$cm->has_view() && strcmp($cm->modname, 'folder') !== 0)) {
continue;
}
if (array_key_exists($cm->modname, $modfullnames)) {
| External URL | http://www.google.com |
| id_display | In pop-up |
Then "google" "link" should exist in the "Main menu" "block"
- And "Add an activity or resource" "button" should exist in the "Main menu" "block"
+ And "Add an activity" "button" should exist in the "Main menu" "block"
/**
* Ensure that the stats array is ready to collect information for the given store and definition.
* @param string $store
+ * @param string $storeclass
* @param string $definition A string that identifies the definition.
* @param int $mode One of cache_store::MODE_*. Since 2.9.
*/
- protected static function ensure_ready_for_stats($store, $definition, $mode = cache_store::MODE_APPLICATION) {
+ protected static function ensure_ready_for_stats($store, $storeclass, $definition, $mode = cache_store::MODE_APPLICATION) {
// This function is performance-sensitive, so exit as quickly as possible
// if we do not need to do anything.
if (isset(self::$stats[$definition]['stores'][$store])) {
return;
}
+
if (!array_key_exists($definition, self::$stats)) {
self::$stats[$definition] = array(
'mode' => $mode,
'stores' => array(
$store => array(
+ 'class' => $storeclass,
'hits' => 0,
'misses' => 0,
'sets' => 0,
);
} else if (!array_key_exists($store, self::$stats[$definition]['stores'])) {
self::$stats[$definition]['stores'][$store] = array(
+ 'class' => $storeclass,
'hits' => 0,
'misses' => 0,
'sets' => 0,
* In Moodle 2.9 the $definition argument changed from accepting only a string to accepting a string or a
* cache_definition instance. It is preferable to pass a cache definition instance.
*
+ * In Moodle 3.9 the first argument changed to also accept a cache_store.
+ *
* @internal
- * @param cache_definition $store
+ * @param string|cache_store $store
* @param cache_definition $definition You used to be able to pass a string here, however that is deprecated please pass the
* actual cache_definition object now.
* @param int $hits The number of hits to record (by default 1)
*/
public static function record_cache_hit($store, $definition, $hits = 1) {
+ $storeclass = '';
+ if ($store instanceof cache_store) {
+ $storeclass = get_class($store);
+ $store = $store->my_name();
+ }
list($definitionstr, $mode) = self::get_definition_stat_id_and_mode($definition);
- self::ensure_ready_for_stats($store, $definitionstr, $mode);
+ self::ensure_ready_for_stats($store, $storeclass, $definitionstr, $mode);
self::$stats[$definitionstr]['stores'][$store]['hits'] += $hits;
}
* In Moodle 2.9 the $definition argument changed from accepting only a string to accepting a string or a
* cache_definition instance. It is preferable to pass a cache definition instance.
*
+ * In Moodle 3.9 the first argument changed to also accept a cache_store.
+ *
* @internal
- * @param string $store
+ * @param string|cache_store $store
* @param cache_definition $definition You used to be able to pass a string here, however that is deprecated please pass the
* actual cache_definition object now.
* @param int $misses The number of misses to record (by default 1)
*/
public static function record_cache_miss($store, $definition, $misses = 1) {
+ $storeclass = '';
+ if ($store instanceof cache_store) {
+ $storeclass = get_class($store);
+ $store = $store->my_name();
+ }
list($definitionstr, $mode) = self::get_definition_stat_id_and_mode($definition);
- self::ensure_ready_for_stats($store, $definitionstr, $mode);
+ self::ensure_ready_for_stats($store, $storeclass, $definitionstr, $mode);
self::$stats[$definitionstr]['stores'][$store]['misses'] += $misses;
}
* In Moodle 2.9 the $definition argument changed from accepting only a string to accepting a string or a
* cache_definition instance. It is preferable to pass a cache definition instance.
*
+ * In Moodle 3.9 the first argument changed to also accept a cache_store.
+ *
* @internal
- * @param string $store
+ * @param string|cache_store $store
* @param cache_definition $definition You used to be able to pass a string here, however that is deprecated please pass the
* actual cache_definition object now.
* @param int $sets The number of sets to record (by default 1)
*/
public static function record_cache_set($store, $definition, $sets = 1) {
+ $storeclass = '';
+ if ($store instanceof cache_store) {
+ $storeclass = get_class($store);
+ $store = $store->my_name();
+ }
list($definitionstr, $mode) = self::get_definition_stat_id_and_mode($definition);
- self::ensure_ready_for_stats($store, $definitionstr, $mode);
+ self::ensure_ready_for_stats($store, $storeclass, $definitionstr, $mode);
self::$stats[$definitionstr]['stores'][$store]['sets'] += $sets;
}
$setaftervalidation = false;
if ($result === false) {
if ($this->perfdebug) {
- cache_helper::record_cache_miss($this->storetype, $this->definition);
+ cache_helper::record_cache_miss($this->store, $this->definition);
}
if ($this->loader !== false) {
// We must pass the original (unparsed) key to the next loader in the chain.
}
$setaftervalidation = ($result !== false);
} else if ($this->perfdebug) {
- cache_helper::record_cache_hit($this->storetype, $this->definition);
+ cache_helper::record_cache_hit($this->store, $this->definition);
}
// 5. Validate strictness.
if ($strictness === MUST_EXIST && $result === false) {
$hits++;
}
}
- cache_helper::record_cache_hit($this->storetype, $this->definition, $hits);
- cache_helper::record_cache_miss($this->storetype, $this->definition, $misses);
+ cache_helper::record_cache_hit($this->store, $this->definition, $hits);
+ cache_helper::record_cache_miss($this->store, $this->definition, $misses);
}
// Return the result. Phew!
*/
public function set($key, $data) {
if ($this->perfdebug) {
- cache_helper::record_cache_set($this->storetype, $this->definition);
+ cache_helper::record_cache_set($this->store, $this->definition);
}
if ($this->loader !== false) {
// We have a loader available set it there as well.
}
$successfullyset = $this->store->set_many($data);
if ($this->perfdebug && $successfullyset) {
- cache_helper::record_cache_set($this->storetype, $this->definition, $successfullyset);
+ cache_helper::record_cache_set($this->store, $this->definition, $successfullyset);
}
return $successfullyset;
}
}
if ($result !== false) {
if ($this->perfdebug) {
- cache_helper::record_cache_hit('** static acceleration **', $this->definition);
+ cache_helper::record_cache_hit(cache_store::STATIC_ACCEL, $this->definition);
}
if ($this->staticaccelerationsize > 1 && $this->staticaccelerationcount > 1) {
// Check to see if this is the last item on the static acceleration keys array.
return $result;
} else {
if ($this->perfdebug) {
- cache_helper::record_cache_miss('** static acceleration **', $this->definition);
+ cache_helper::record_cache_miss(cache_store::STATIC_ACCEL, $this->definition);
}
return false;
}
// 4. Load if from the loader/datasource if we don't already have it.
if ($result === false) {
if ($this->perfdebug) {
- cache_helper::record_cache_miss($this->storetype, $this->get_definition());
+ cache_helper::record_cache_miss($this->get_store(), $this->get_definition());
}
if ($this->get_loader() !== false) {
// We must pass the original (unparsed) key to the next loader in the chain.
$this->set($key, $result);
}
} else if ($this->perfdebug) {
- cache_helper::record_cache_hit($this->storetype, $this->get_definition());
+ cache_helper::record_cache_hit($this->get_store(), $this->get_definition());
}
// 5. Validate strictness.
if ($strictness === MUST_EXIST && $result === false) {
$loader->set($key, $data);
}
if ($this->perfdebug) {
- cache_helper::record_cache_set($this->storetype, $this->get_definition());
+ cache_helper::record_cache_set($this->get_store(), $this->get_definition());
}
if (is_object($data) && $data instanceof cacheable_object) {
$data = new cache_cached_object($data);
$hits++;
}
}
- cache_helper::record_cache_hit($this->storetype, $this->get_definition(), $hits);
- cache_helper::record_cache_miss($this->storetype, $this->get_definition(), $misses);
+ cache_helper::record_cache_hit($this->get_store(), $this->get_definition(), $hits);
+ cache_helper::record_cache_miss($this->get_store(), $this->get_definition(), $misses);
}
return $return;
}
$successfullyset = $this->get_store()->set_many($data);
if ($this->perfdebug && $successfullyset) {
- cache_helper::record_cache_set($this->storetype, $this->get_definition(), $successfullyset);
+ cache_helper::record_cache_set($this->store, $this->get_definition(), $successfullyset);
}
return $successfullyset;
}
* Request caches. Static caches really.
*/
const MODE_REQUEST = 4;
+ /**
+ * Static caches.
+ */
+ const STATIC_ACCEL = '** static accel. **';
/**
* Constructs an instance of the cache store.
$this->assertFalse($request->get('missMe'));
$endstats = cache_helper::get_stats();
- $this->assertEquals(2, $endstats[$applicationid]['stores']['cachestore_file']['misses']);
- $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['hits']);
- $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['sets']);
- $this->assertEquals(3, $endstats[$sessionid]['stores']['cachestore_session']['misses']);
- $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['hits']);
- $this->assertEquals(1, $endstats[$sessionid]['stores']['cachestore_session']['sets']);
- $this->assertEquals(4, $endstats[$requestid]['stores']['cachestore_static']['misses']);
- $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['hits']);
- $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['sets']);
+ $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['misses']);
+ $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits']);
+ $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets']);
+ $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['misses']);
+ $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits']);
+ $this->assertEquals(1, $endstats[$sessionid]['stores']['default_session']['sets']);
+ $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['misses']);
+ $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits']);
+ $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets']);
$startstats = cache_helper::get_stats();
$this->assertTrue($request->set('setMe4', 4));
$endstats = cache_helper::get_stats();
- $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['misses'] -
- $startstats[$applicationid]['stores']['cachestore_file']['misses']);
- $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['hits'] -
- $startstats[$applicationid]['stores']['cachestore_file']['hits']);
- $this->assertEquals(2, $endstats[$applicationid]['stores']['cachestore_file']['sets'] -
- $startstats[$applicationid]['stores']['cachestore_file']['sets']);
- $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['misses'] -
- $startstats[$sessionid]['stores']['cachestore_session']['misses']);
- $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['hits'] -
- $startstats[$sessionid]['stores']['cachestore_session']['hits']);
- $this->assertEquals(3, $endstats[$sessionid]['stores']['cachestore_session']['sets'] -
- $startstats[$sessionid]['stores']['cachestore_session']['sets']);
- $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['misses'] -
- $startstats[$requestid]['stores']['cachestore_static']['misses']);
- $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['hits'] -
- $startstats[$requestid]['stores']['cachestore_static']['hits']);
- $this->assertEquals(4, $endstats[$requestid]['stores']['cachestore_static']['sets'] -
- $startstats[$requestid]['stores']['cachestore_static']['sets']);
+ $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] -
+ $startstats[$applicationid]['stores']['default_application']['misses']);
+ $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits'] -
+ $startstats[$applicationid]['stores']['default_application']['hits']);
+ $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['sets'] -
+ $startstats[$applicationid]['stores']['default_application']['sets']);
+ $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] -
+ $startstats[$sessionid]['stores']['default_session']['misses']);
+ $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits'] -
+ $startstats[$sessionid]['stores']['default_session']['hits']);
+ $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['sets'] -
+ $startstats[$sessionid]['stores']['default_session']['sets']);
+ $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] -
+ $startstats[$requestid]['stores']['default_request']['misses']);
+ $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits'] -
+ $startstats[$requestid]['stores']['default_request']['hits']);
+ $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['sets'] -
+ $startstats[$requestid]['stores']['default_request']['sets']);
$startstats = cache_helper::get_stats();
$this->assertEquals($request->get('setMe4'), 4);
$endstats = cache_helper::get_stats();
- $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['misses'] -
- $startstats[$applicationid]['stores']['cachestore_file']['misses']);
- $this->assertEquals(2, $endstats[$applicationid]['stores']['cachestore_file']['hits'] -
- $startstats[$applicationid]['stores']['cachestore_file']['hits']);
- $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['sets'] -
- $startstats[$applicationid]['stores']['cachestore_file']['sets']);
- $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['misses'] -
- $startstats[$sessionid]['stores']['cachestore_session']['misses']);
- $this->assertEquals(3, $endstats[$sessionid]['stores']['cachestore_session']['hits'] -
- $startstats[$sessionid]['stores']['cachestore_session']['hits']);
- $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['sets'] -
- $startstats[$sessionid]['stores']['cachestore_session']['sets']);
- $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['misses'] -
- $startstats[$requestid]['stores']['cachestore_static']['misses']);
- $this->assertEquals(4, $endstats[$requestid]['stores']['cachestore_static']['hits'] -
- $startstats[$requestid]['stores']['cachestore_static']['hits']);
- $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['sets'] -
- $startstats[$requestid]['stores']['cachestore_static']['sets']);
+ $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] -
+ $startstats[$applicationid]['stores']['default_application']['misses']);
+ $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['hits'] -
+ $startstats[$applicationid]['stores']['default_application']['hits']);
+ $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets'] -
+ $startstats[$applicationid]['stores']['default_application']['sets']);
+ $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] -
+ $startstats[$sessionid]['stores']['default_session']['misses']);
+ $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['hits'] -
+ $startstats[$sessionid]['stores']['default_session']['hits']);
+ $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['sets'] -
+ $startstats[$sessionid]['stores']['default_session']['sets']);
+ $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] -
+ $startstats[$requestid]['stores']['default_request']['misses']);
+ $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['hits'] -
+ $startstats[$requestid]['stores']['default_request']['hits']);
+ $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets'] -
+ $startstats[$requestid]['stores']['default_request']['sets']);
$startstats = cache_helper::get_stats();
$request->get_many(array('setMe1', 'setMe2', 'setMe3', 'setMe4'));
$endstats = cache_helper::get_stats();
- $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['misses'] -
- $startstats[$applicationid]['stores']['cachestore_file']['misses']);
- $this->assertEquals(2, $endstats[$applicationid]['stores']['cachestore_file']['hits'] -
- $startstats[$applicationid]['stores']['cachestore_file']['hits']);
- $this->assertEquals(0, $endstats[$applicationid]['stores']['cachestore_file']['sets'] -
- $startstats[$applicationid]['stores']['cachestore_file']['sets']);
- $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['misses'] -
- $startstats[$sessionid]['stores']['cachestore_session']['misses']);
- $this->assertEquals(3, $endstats[$sessionid]['stores']['cachestore_session']['hits'] -
- $startstats[$sessionid]['stores']['cachestore_session']['hits']);
- $this->assertEquals(0, $endstats[$sessionid]['stores']['cachestore_session']['sets'] -
- $startstats[$sessionid]['stores']['cachestore_session']['sets']);
- $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['misses'] -
- $startstats[$requestid]['stores']['cachestore_static']['misses']);
- $this->assertEquals(4, $endstats[$requestid]['stores']['cachestore_static']['hits'] -
- $startstats[$requestid]['stores']['cachestore_static']['hits']);
- $this->assertEquals(0, $endstats[$requestid]['stores']['cachestore_static']['sets'] -
- $startstats[$requestid]['stores']['cachestore_static']['sets']);
+ $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] -
+ $startstats[$applicationid]['stores']['default_application']['misses']);
+ $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['hits'] -
+ $startstats[$applicationid]['stores']['default_application']['hits']);
+ $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets'] -
+ $startstats[$applicationid]['stores']['default_application']['sets']);
+ $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] -
+ $startstats[$sessionid]['stores']['default_session']['misses']);
+ $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['hits'] -
+ $startstats[$sessionid]['stores']['default_session']['hits']);
+ $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['sets'] -
+ $startstats[$sessionid]['stores']['default_session']['sets']);
+ $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] -
+ $startstats[$requestid]['stores']['default_request']['misses']);
+ $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['hits'] -
+ $startstats[$requestid]['stores']['default_request']['hits']);
+ $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets'] -
+ $startstats[$requestid]['stores']['default_request']['sets']);
}
public function test_static_cache() {
// 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']);
+ $this->assertEquals(0, $endstats[$applicationid]['stores']['** static accel. **']['misses']);
+ $this->assertEquals(3, $endstats[$applicationid]['stores']['** static accel. **']['hits']);
}
public function test_performance_debug_off() {
This files describes API changes in /cache/stores/* - cache store plugins.
Information provided here is intended especially for developers.
+=== 3.9 ===
+* The record_cache_hit/miss/set methods now take a cache_store instead of a cache_definition object
+
=== 3.8 ===
* The Redis cache store can now make use of the Zstandard compression algorithm (see MDL-66428).
'core_calendar/events',
'core_calendar/modal_delete',
'core_calendar/selectors',
+ 'core/pending',
],
function(
$,
CalendarRepository,
CalendarEvents,
ModalDelete,
- CalendarSelectors
+ CalendarSelectors,
+ Pending
) {
/**
);
}
- deletePromise.then(function(deleteModal) {
- deleteModal.show();
-
- return;
- })
- .fail(Notification.exception);
-
var stringsPromise = Str.get_strings(deleteStrings);
var finalPromise = $.when(stringsPromise, deletePromise)
deleteModal.setSaveButtonText(strings[0]);
}
+ deleteModal.show();
+
deleteModal.getRoot().on(ModalEvents.save, function() {
+ var pendingPromise = new Pending('calendar/crud:initModal:deletedevent');
CalendarRepository.deleteEvent(eventId, false)
.then(function() {
$('body').trigger(CalendarEvents.deleted, [eventId, false]);
return;
})
+ .then(pendingPromise.resolve)
.catch(Notification.exception);
});
deleteModal.getRoot().on(CalendarEvents.deleteAll, function() {
+ var pendingPromise = new Pending('calendar/crud:initModal:deletedallevent');
CalendarRepository.deleteEvent(eventId, true)
.then(function() {
$('body').trigger(CalendarEvents.deleted, [eventId, true]);
return;
})
+ .then(pendingPromise.resolve)
.catch(Notification.exception);
});
return deleteModal;
})
- .fail(Notification.exception);
+ .catch(Notification.exception);
return finalPromise;
}
"require-dev": {
"phpunit/phpunit": "7.5.*",
"phpunit/dbunit": "4.0.*",
- "moodlehq/behat-extension": "3.39.1",
+ "moodlehq/behat-extension": "3.39.3",
"mikey179/vfsstream": "^1.6",
"instaclick/php-webdriver": "dev-local as 1.x-dev"
}
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "7d4095e9af1e9ef59e2a74273a5c55b2",
+ "content-hash": "b1953ceec577434625a7aee12f650daa",
"packages": [],
"packages-dev": [
{
"name": "behat/behat",
- "version": "v3.5.0",
+ "version": "v3.6.1",
"source": {
"type": "git",
"url": "https://github.com/Behat/Behat.git",
- &nb