mirror of
https://github.com/moodle/moodle.git
synced 2025-04-13 12:32:08 +02:00
Merge branch 'MDL-71156-master' of git://github.com/lameze/moodle
This commit is contained in:
commit
e853e6b7db
138
admin/cli/fix_orphaned_calendar_events.php
Normal file
138
admin/cli/fix_orphaned_calendar_events.php
Normal file
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Fix orphaned calendar events that were broken by MDL-67494.
|
||||
*
|
||||
* This script will look for all the calendar events which userids
|
||||
* where broken by a wrong upgrade step, affecting to Moodle 3.9.5
|
||||
* and up.
|
||||
*
|
||||
* It performs checks to both:
|
||||
* a) Detect if the site was affected (ran the wrong upgrade step).
|
||||
* b) Look for orphaned calendar events, categorising them as:
|
||||
* - standard: site / category / course / group / user events
|
||||
* - subscription: events created via subscriptions.
|
||||
* - action: normal action events, created to show common important dates.
|
||||
* - override: user and group override events, particular, that some activities support.
|
||||
* - custom: other events, not being any of the above, common or particular.
|
||||
* By specifying it (--fix) try to recover as many broken events (missing userid) as
|
||||
* possible. Standard, subscription, action, override events in core are fully supported but
|
||||
* override or custom events should be fixed by each plugin as far as there isn't any standard
|
||||
* API (plugin-wise) to launch a rebuild of the calendar events.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2021 onwards Simey Lameze <simey@moodle.com>
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require_once(__DIR__ . '/../../config.php');
|
||||
require_once($CFG->libdir . "/clilib.php");
|
||||
require_once($CFG->libdir . '/db/upgradelib.php');
|
||||
|
||||
// Supported options.
|
||||
$long = ['fix' => false, 'help' => false];
|
||||
$short = ['f' => 'fix', 'h' => 'help'];
|
||||
|
||||
// CLI options.
|
||||
[$options, $unrecognized] = cli_get_params($long, $short);
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
}
|
||||
|
||||
if ($options['help']) {
|
||||
$help = <<<EOT
|
||||
Fix orphaned calendar events.
|
||||
|
||||
This script detects calendar events that have had their
|
||||
userid lost. By default it will perform various checks
|
||||
and report them, showing the site status in an easy way.
|
||||
|
||||
Also, optionally (--fix), it wil try to recover as many
|
||||
lost userids as possible from different sources. Note that
|
||||
this script aims to process well-know events in core,
|
||||
leaving custom events in 3rd part plugins mostly unmodified
|
||||
because there isn't any consistent way to regenerate them.
|
||||
|
||||
For more details: https://tracker.moodle.org/browse/MDL-71156
|
||||
|
||||
Options:
|
||||
-h, --help Print out this help.
|
||||
-f, --fix Fix the orphaned calendar events in the DB.
|
||||
If not specified only check and report problems to STDERR.
|
||||
|
||||
Usage:
|
||||
- Only report: \$ sudo -u www-data /usr/bin/php admin/cli/fix_orphaned_calendar_events.php
|
||||
- Report and fix: \$ sudo -u www-data /usr/bin/php admin/cli/fix_orphaned_calendar_events.php -f
|
||||
EOT;
|
||||
|
||||
cli_writeln($help);
|
||||
die;
|
||||
}
|
||||
|
||||
// Check various usual pre-requisites.
|
||||
if (empty($CFG->version)) {
|
||||
cli_error('Database is not yet installed.');
|
||||
}
|
||||
|
||||
$admin = get_admin();
|
||||
if (!$admin) {
|
||||
cli_error('Error: No admin account was found.');
|
||||
}
|
||||
|
||||
if (moodle_needs_upgrading()) {
|
||||
cli_error('Moodle upgrade pending, script execution suspended.');
|
||||
}
|
||||
|
||||
// Do everything as admin by default.
|
||||
\core\session\manager::set_user($admin);
|
||||
|
||||
// Report current site status.
|
||||
cli_heading('Checking the site status');
|
||||
$needsfix = upgrade_calendar_site_status();
|
||||
|
||||
// Report current calendar events status.
|
||||
cli_heading('Checking the calendar events status');
|
||||
$info = upgrade_calendar_events_status();
|
||||
$hasbadevents = $info['total']->bad > 0 || $info['total']->bad != $info['other']->bad;
|
||||
$needsfix = $needsfix || $hasbadevents;
|
||||
|
||||
// If, selected, fix as many calendar events as possible.
|
||||
if ($options['fix']) {
|
||||
|
||||
// If the report has told us that the fix was not needed... ask for confirmation!
|
||||
if (!$needsfix) {
|
||||
cli_writeln("This site DOES NOT NEED to run the calendar events fix.");
|
||||
$input = cli_input('Are you completely sure that you want to run the fix? (y/N)', 'N', ['y', 'Y', 'n', 'N']);
|
||||
if (strtolower($input) != 'y') {
|
||||
exit(0);
|
||||
}
|
||||
cli_writeln("");
|
||||
}
|
||||
cli_heading('Fixing as many as possible calendar events');
|
||||
upgrade_calendar_events_fix_remaining($info);
|
||||
// Report current (after fix) calendar events status.
|
||||
cli_heading('Checking the calendar events status (after fix)');
|
||||
upgrade_calendar_events_status();
|
||||
} else if ($needsfix) {
|
||||
// Fix option was not provided but problem events have been found. Notify the user and provide info how to fix these events.
|
||||
cli_writeln("This site NEEDS to run the calendar events fix!");
|
||||
cli_writeln("To fix the calendar events, re-run this script with the --fix option.");
|
||||
}
|
@ -66,6 +66,102 @@ function create_event($properties) {
|
||||
return $event->create($record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a x number of events for each event type.
|
||||
*
|
||||
* @param int $quantity The quantity of events to be created.
|
||||
* @return array List of created events.
|
||||
*/
|
||||
function create_standard_events(int $quantity): array {
|
||||
$types = ['site', 'category', 'course', 'group', 'user'];
|
||||
|
||||
$events = [];
|
||||
foreach ($types as $eventtype) {
|
||||
// Create five events of each event type.
|
||||
for ($i = 0; $i < $quantity; $i++) {
|
||||
$events[] = create_event(['eventtype' => $eventtype]);
|
||||
}
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create an action event.
|
||||
*
|
||||
* @param array $data The event data.
|
||||
* @return bool|calendar_event
|
||||
*/
|
||||
function create_action_event(array $data) {
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/calendar/lib.php');
|
||||
|
||||
if (!isset($data['modulename']) || !isset($data['instance'])) {
|
||||
throw new coding_exception('Module and instance should be specified when creating an action event.');
|
||||
}
|
||||
|
||||
$isuseroverride = isset($data->priority) && $data->priority == CALENDAR_EVENT_USER_OVERRIDE_PRIORITY;
|
||||
if ($isuseroverride) {
|
||||
if (!in_array($data['modulename'], ['assign', 'lesson', 'quiz'])) {
|
||||
throw new coding_exception('Only assign, lesson and quiz modules supports overrides');
|
||||
}
|
||||
}
|
||||
|
||||
$event = array_merge($data, [
|
||||
'eventtype' => isset($data['eventtype']) ? $data['eventtype'] : 'open',
|
||||
'courseid' => isset($data['courseid']) ? $data['courseid'] : 0,
|
||||
'instance' => $data['instance'],
|
||||
'modulename' => $data['modulename'],
|
||||
'type' => CALENDAR_EVENT_TYPE_ACTION,
|
||||
]);
|
||||
|
||||
return create_event($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create an user override calendar event.
|
||||
*
|
||||
* @param string $modulename The modulename.
|
||||
* @param int $instanceid The instance id.
|
||||
* @param int $userid The user id.
|
||||
* @return calendar_event|false
|
||||
*/
|
||||
function create_user_override_event(string $modulename, int $instanceid, int $userid) {
|
||||
if (!isset($userid)) {
|
||||
throw new coding_exception('Must specify userid when creating a user override.');
|
||||
}
|
||||
|
||||
return create_action_event([
|
||||
'modulename' => $modulename,
|
||||
'instance' => $instanceid,
|
||||
'userid' => $userid,
|
||||
'priority' => CALENDAR_EVENT_USER_OVERRIDE_PRIORITY,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create an group override calendar event.
|
||||
*
|
||||
* @param string $modulename The modulename.
|
||||
* @param int $instanceid The instance id.
|
||||
* @param int $courseid The course id.
|
||||
* @param int $groupid The group id.
|
||||
* @return calendar_event|false
|
||||
*/
|
||||
function create_group_override_event(string $modulename, int $instanceid, int $courseid, int $groupid) {
|
||||
if (!isset($groupid)) {
|
||||
throw new coding_exception('Must specify groupid when creating a group override.');
|
||||
}
|
||||
|
||||
return create_action_event([
|
||||
'groupid' => $groupid,
|
||||
'courseid' => $courseid,
|
||||
'modulename' => $modulename,
|
||||
'instance' => $instanceid,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* A test factory that will create action events.
|
||||
*
|
||||
|
73
lib/classes/task/calendar_fix_orphaned_events.php
Normal file
73
lib/classes/task/calendar_fix_orphaned_events.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Adhoc task handling fixing of events that have had their userid lost.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2021 onwards Simey Lameze <simey@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core\task;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class handling fixing of events that have had their userid lost.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2021 onwards Simey Lameze <simey@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class calendar_fix_orphaned_events extends adhoc_task {
|
||||
|
||||
/**
|
||||
* Run the task to recover the correct userid from the event.
|
||||
*
|
||||
* If the maximum number of records are updated, the task re-queues itself,
|
||||
* as there may be more events to be fixed.
|
||||
*/
|
||||
public function execute() {
|
||||
|
||||
// Check for problematic upgrade steps and fix orphaned records.
|
||||
if ($this->update_events_wrong_userid_remaining()) {
|
||||
// There are orphaned events to be fixed.
|
||||
// The task will re-queue itself until all orphaned calendar events have been fixed.
|
||||
\core\task\manager::queue_adhoc_task(new calendar_fix_orphaned_events());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the recovery of events that have been set with userid to zero.
|
||||
*
|
||||
* @return bool Whether there are more events to be fixed.
|
||||
*/
|
||||
protected function update_events_wrong_userid_remaining(): bool {
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->libdir . '/db/upgradelib.php');
|
||||
|
||||
// Default the max runtime to 60 seconds, unless overridden in config.php.
|
||||
$maxseconds = $CFG->calendareventsmaxseconds ?? MINSECS;
|
||||
|
||||
// Orphaned events found, get those events so it can be recovered.
|
||||
$eventsinfo = upgrade_calendar_events_status();
|
||||
|
||||
// Fix the orphaned events and returns if there are more events to be fixed.
|
||||
return upgrade_calendar_events_fix_remaining($eventsinfo, true, $maxseconds);
|
||||
}
|
||||
}
|
@ -2598,5 +2598,28 @@ function xmldb_main_upgrade($oldversion) {
|
||||
upgrade_main_savepoint(true, 2021052500.84);
|
||||
}
|
||||
|
||||
if ($oldversion < 2021052500.85) {
|
||||
require_once($CFG->libdir . '/db/upgradelib.php');
|
||||
|
||||
// Check if this site has executed the problematic upgrade steps.
|
||||
$needsfixing = upgrade_calendar_site_status(false);
|
||||
|
||||
// Only queue the task if this site has been affected by the problematic upgrade step.
|
||||
if ($needsfixing) {
|
||||
|
||||
// Create adhoc task to search and recover orphaned calendar events.
|
||||
$record = new \stdClass();
|
||||
$record->classname = '\core\task\calendar_fix_orphaned_events';
|
||||
|
||||
// Next run time based from nextruntime computation in \core\task\manager::queue_adhoc_task().
|
||||
$nextruntime = time() - 1;
|
||||
$record->nextruntime = $nextruntime;
|
||||
$DB->insert_record('task_adhoc', $record);
|
||||
}
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2021052500.85);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -749,3 +749,528 @@ function upgrade_core_licenses() {
|
||||
set_config('sitedefaultlicense', reset($activelicenses));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects if the site may need to get the calendar events fixed or no. With optional output.
|
||||
*
|
||||
* @param bool $output true if the function must output information, false if not.
|
||||
* @return bool true if the site needs to run the fixes, false if not.
|
||||
*/
|
||||
function upgrade_calendar_site_status(bool $output = true): bool {
|
||||
global $DB;
|
||||
|
||||
// List of upgrade steps where the bug happened.
|
||||
$badsteps = [
|
||||
'3.9.5' => '2020061504.08',
|
||||
'3.10.2' => '2020110901.09',
|
||||
'3.11dev' => '2021022600.02',
|
||||
'4.0dev' => '2021052500.65',
|
||||
];
|
||||
|
||||
// List of upgrade steps that ran the fixer.
|
||||
$fixsteps = [
|
||||
'3.9.6+' => '2020061506.05',
|
||||
'3.10.3+' => '2020110903.05',
|
||||
'3.11dev' => '2021042100.02',
|
||||
'4.0dev' => '2021052500.85',
|
||||
];
|
||||
|
||||
$targetsteps = array_merge(array_values($badsteps), array_values( $fixsteps));
|
||||
list($insql, $inparams) = $DB->get_in_or_equal($targetsteps);
|
||||
$foundsteps = $DB->get_fieldset_sql("
|
||||
SELECT DISTINCT version
|
||||
FROM {upgrade_log}
|
||||
WHERE plugin = 'core'
|
||||
AND version " . $insql . "
|
||||
ORDER BY version", $inparams);
|
||||
|
||||
// Analyse the found steps, to decide if the site needs upgrading or no.
|
||||
$badfound = false;
|
||||
$fixfound = false;
|
||||
foreach ($foundsteps as $foundstep) {
|
||||
$badfound = $badfound ?: array_search($foundstep, $badsteps, true);
|
||||
$fixfound = $fixfound ?: array_search($foundstep, $fixsteps, true);
|
||||
}
|
||||
$needsfix = $badfound && !$fixfound;
|
||||
|
||||
// Let's output some textual information if required to.
|
||||
if ($output) {
|
||||
mtrace("");
|
||||
if ($badfound) {
|
||||
mtrace("This site has executed the problematic upgrade step {$badsteps[$badfound]} present in {$badfound}.");
|
||||
} else {
|
||||
mtrace("Problematic upgrade steps were NOT found, site should be safe.");
|
||||
}
|
||||
if ($fixfound) {
|
||||
mtrace("This site has executed the fix upgrade step {$fixsteps[$fixfound]} present in {$fixfound}.");
|
||||
} else {
|
||||
mtrace("Fix upgrade steps were NOT found.");
|
||||
}
|
||||
mtrace("");
|
||||
if ($needsfix) {
|
||||
mtrace("This site NEEDS to run the calendar events fix!");
|
||||
mtrace('');
|
||||
mtrace("You can use this CLI tool or upgrade to a version of Moodle that includes");
|
||||
mtrace("the fix and will be executed as part of the normal upgrade procedure.");
|
||||
mtrace("The following versions or up are known candidates to upgrade to:");
|
||||
foreach ($fixsteps as $key => $value) {
|
||||
mtrace(" - {$key}: {$value}");
|
||||
}
|
||||
mtrace("");
|
||||
}
|
||||
}
|
||||
return $needsfix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the calendar events needing to be fixed. With optional output.
|
||||
*
|
||||
* @param bool $output true if the function must output information, false if not.
|
||||
* @return stdClass[] an array of event types (as keys) with total and bad counters, plus sql to retrieve them.
|
||||
*/
|
||||
function upgrade_calendar_events_status(bool $output = true): array {
|
||||
global $DB;
|
||||
|
||||
// Calculate the list of standard (core) activity plugins.
|
||||
$plugins = core_plugin_manager::standard_plugins_list('mod');
|
||||
$coremodules = "modulename IN ('" . implode("', '", $plugins) . "')";
|
||||
|
||||
// Some query parts go here.
|
||||
$brokenevents = "(userid = 0 AND (eventtype <> 'user' OR priority <> 0))"; // From the original bad upgrade step.
|
||||
$standardevents = "(eventtype IN ('site', 'category', 'course', 'group', 'user') AND subscriptionid IS NULL)";
|
||||
$subscriptionevents = "(subscriptionid IS NOT NULL)";
|
||||
$overrideevents = "({$coremodules} AND priority IS NOT NULL)";
|
||||
$actionevents = "({$coremodules} AND instance > 0 and priority IS NULL)";
|
||||
$otherevents = "(NOT ({$standardevents} OR {$subscriptionevents} OR {$overrideevents} OR {$actionevents}))";
|
||||
|
||||
// Detailed query template.
|
||||
$detailstemplate = "
|
||||
SELECT ##group## AS groupname, COUNT(1) AS count
|
||||
FROM {event}
|
||||
WHERE ##groupconditions##
|
||||
GROUP BY ##group##";
|
||||
|
||||
// Count total and potentially broken events.
|
||||
$total = $DB->count_records_select('event', '');
|
||||
$totalbadsql = $brokenevents;
|
||||
$totalbad = $DB->count_records_select('event', $totalbadsql);
|
||||
|
||||
// Standard events.
|
||||
$standard = $DB->count_records_select('event', $standardevents);
|
||||
$standardbadsql = "{$brokenevents} AND {$standardevents}";
|
||||
$standardbad = $DB->count_records_select('event', $standardbadsql);
|
||||
$standarddetails = $DB->get_records_sql(
|
||||
str_replace(
|
||||
['##group##', '##groupconditions##'],
|
||||
['eventtype', $standardbadsql],
|
||||
$detailstemplate
|
||||
)
|
||||
);
|
||||
array_walk($standarddetails, function (&$rec) {
|
||||
$rec = $rec->groupname . ': ' . $rec->count;
|
||||
});
|
||||
$standarddetails = $standarddetails ? '(' . implode(', ', $standarddetails) . ')' : '- all good!';
|
||||
|
||||
// Subscription events.
|
||||
$subscription = $DB->count_records_select('event', $subscriptionevents);
|
||||
$subscriptionbadsql = "{$brokenevents} AND {$subscriptionevents}";
|
||||
$subscriptionbad = $DB->count_records_select('event', $subscriptionbadsql);
|
||||
$subscriptiondetails = $DB->get_records_sql(
|
||||
str_replace(
|
||||
['##group##', '##groupconditions##'],
|
||||
['eventtype', $subscriptionbadsql],
|
||||
$detailstemplate
|
||||
)
|
||||
);
|
||||
array_walk($subscriptiondetails, function (&$rec) {
|
||||
$rec = $rec->groupname . ': ' . $rec->count;
|
||||
});
|
||||
$subscriptiondetails = $subscriptiondetails ? '(' . implode(', ', $subscriptiondetails) . ')' : '- all good!';
|
||||
|
||||
// Override events.
|
||||
$override = $DB->count_records_select('event', $overrideevents);
|
||||
$overridebadsql = "{$brokenevents} AND {$overrideevents}";
|
||||
$overridebad = $DB->count_records_select('event', $overridebadsql);
|
||||
$overridedetails = $DB->get_records_sql(
|
||||
str_replace(
|
||||
['##group##', '##groupconditions##'],
|
||||
['modulename', $overridebadsql],
|
||||
$detailstemplate
|
||||
)
|
||||
);
|
||||
array_walk($overridedetails, function (&$rec) {
|
||||
$rec = $rec->groupname . ': ' . $rec->count;
|
||||
});
|
||||
$overridedetails = $overridedetails ? '(' . implode(', ', $overridedetails) . ')' : '- all good!';
|
||||
|
||||
// Action events.
|
||||
$action = $DB->count_records_select('event', $actionevents);
|
||||
$actionbadsql = "{$brokenevents} AND {$actionevents}";
|
||||
$actionbad = $DB->count_records_select('event', $actionbadsql);
|
||||
$actiondetails = $DB->get_records_sql(
|
||||
str_replace(
|
||||
['##group##', '##groupconditions##'],
|
||||
['modulename', $actionbadsql],
|
||||
$detailstemplate
|
||||
)
|
||||
);
|
||||
array_walk($actiondetails, function (&$rec) {
|
||||
$rec = $rec->groupname . ': ' . $rec->count;
|
||||
});
|
||||
$actiondetails = $actiondetails ? '(' . implode(', ', $actiondetails) . ')' : '- all good!';
|
||||
|
||||
// Other events.
|
||||
$other = $DB->count_records_select('event', $otherevents);
|
||||
$otherbadsql = "{$brokenevents} AND {$otherevents}";
|
||||
$otherbad = $DB->count_records_select('event', $otherbadsql);
|
||||
$otherdetails = $DB->get_records_sql(
|
||||
str_replace(
|
||||
['##group##', '##groupconditions##'],
|
||||
['COALESCE(component, modulename)', $otherbadsql],
|
||||
$detailstemplate
|
||||
)
|
||||
);
|
||||
array_walk($otherdetails, function (&$rec) {
|
||||
$rec = ($rec->groupname ?: 'unknown') . ': ' . $rec->count;
|
||||
});
|
||||
$otherdetails = $otherdetails ? '(' . implode(', ', $otherdetails) . ')' : '- all good!';
|
||||
|
||||
// Let's output some textual information if required to.
|
||||
if ($output) {
|
||||
mtrace("");
|
||||
mtrace("Totals: {$total} / {$totalbad} (total / wrong)");
|
||||
mtrace(" - standards events: {$standard} / {$standardbad} {$standarddetails}");
|
||||
mtrace(" - subscription events: {$subscription} / {$subscriptionbad} {$subscriptiondetails}");
|
||||
mtrace(" - override events: {$override} / {$overridebad} {$overridedetails}");
|
||||
mtrace(" - action events: {$action} / {$actionbad} {$actiondetails}");
|
||||
mtrace(" - other events: {$other} / {$otherbad} {$otherdetails}");
|
||||
mtrace("");
|
||||
}
|
||||
|
||||
return [
|
||||
'total' => (object)['count' => $total, 'bad' => $totalbad, 'sql' => $totalbadsql],
|
||||
'standard' => (object)['count' => $standard, 'bad' => $standardbad, 'sql' => $standardbadsql],
|
||||
'subscription' => (object)['count' => $subscription, 'bad' => $subscriptionbad, 'sql' => $subscriptionbadsql],
|
||||
'override' => (object)['count' => $override, 'bad' => $overridebad, 'sql' => $overridebadsql],
|
||||
'action' => (object)['count' => $action, 'bad' => $actionbad, 'sql' => $actionbadsql],
|
||||
'other' => (object)['count' => $other, 'bad' => $otherbad, 'sql' => $otherbadsql],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the calendar events needing to be fixed. With optional output.
|
||||
*
|
||||
* @param stdClass[] an array of event types (as keys) with total and bad counters, plus sql to retrieve them.
|
||||
* @param bool $output true if the function must output information, false if not.
|
||||
* @param int $maxseconds Number of seconds the function will run as max, with zero meaning no limit.
|
||||
* @return bool true if the function has not finished fixing everything, false if it has finished.
|
||||
*/
|
||||
function upgrade_calendar_events_fix_remaining(array $info, bool $output = true, int $maxseconds = 0): bool {
|
||||
global $DB;
|
||||
|
||||
upgrade_calendar_events_mtrace('', $output);
|
||||
|
||||
// Initial preparations.
|
||||
$starttime = time();
|
||||
$endtime = $maxseconds ? ($starttime + $maxseconds) : 0;
|
||||
|
||||
// No bad events, or all bad events are "other" events, finished.
|
||||
if ($info['total']->bad == 0 || $info['total']->bad == $info['other']->bad) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Let's fix overriden events first (they are the ones performing worse with the missing userid).
|
||||
if ($info['override']->bad != 0) {
|
||||
if (upgrade_calendar_override_events_fix($info['override'], $output, $endtime)) {
|
||||
return true; // Not finished yet.
|
||||
}
|
||||
}
|
||||
|
||||
// Let's fix the subscription events (like standard ones, but with the event_subscriptions table).
|
||||
if ($info['subscription']->bad != 0) {
|
||||
if (upgrade_calendar_subscription_events_fix($info['subscription'], $output, $endtime)) {
|
||||
return true; // Not finished yet.
|
||||
}
|
||||
}
|
||||
|
||||
// Let's fix the standard events (site, category, course, group).
|
||||
if ($info['standard']->bad != 0) {
|
||||
if (upgrade_calendar_standard_events_fix($info['standard'], $output, $endtime)) {
|
||||
return true; // Not finished yet.
|
||||
}
|
||||
}
|
||||
|
||||
// Let's fix the action events (all them are "general" ones, not user-specific in core).
|
||||
if ($info['action']->bad != 0) {
|
||||
if (upgrade_calendar_action_events_fix($info['action'], $output, $endtime)) {
|
||||
return true; // Not finished yet.
|
||||
}
|
||||
}
|
||||
|
||||
// Have arrived here, finished!
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper over mtrace() to allow a few more things to be specified.
|
||||
*
|
||||
* @param string $string string to output.
|
||||
* @param bool $output true to perform the output, false to avoid it.
|
||||
*/
|
||||
function upgrade_calendar_events_mtrace(string $string, bool $output): void {
|
||||
static $cols = 0;
|
||||
|
||||
// No output, do nothing.
|
||||
if (!$output) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Printing dots... let's output them slightly nicer.
|
||||
if ($string === '.') {
|
||||
$cols++;
|
||||
// Up to 60 cols.
|
||||
if ($cols < 60) {
|
||||
mtrace($string, '');
|
||||
} else {
|
||||
mtrace($string);
|
||||
$cols = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset cols, have ended printing dots.
|
||||
if ($cols) {
|
||||
$cols = 0;
|
||||
mtrace('');
|
||||
}
|
||||
|
||||
// Normal output.
|
||||
mtrace($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a valid editing teacher for a given courseid
|
||||
*
|
||||
* @param int $courseid The course to look for editing teachers.
|
||||
* @return int A user id of an editing teacher or, if missing, the admin userid.
|
||||
*/
|
||||
function upgrade_calendar_events_get_teacherid(int $courseid): int {
|
||||
|
||||
if ($context = context_course::instance($courseid, IGNORE_MISSING)) {
|
||||
if ($havemanage = get_users_by_capability($context, 'moodle/course:manageactivities', 'u.id')) {
|
||||
return array_keys($havemanage)[0];
|
||||
}
|
||||
}
|
||||
return get_admin()->id; // Could not find a teacher, default to admin.
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the calendar standard events needing to be fixed. With optional output.
|
||||
*
|
||||
* @param stdClass $info an object with total and bad counters, plus sql to retrieve them.
|
||||
* @param bool $output true if the function must output information, false if not.
|
||||
* @param int $endtime cutoff time when the process must stop (0 means no cutoff).
|
||||
* @return bool true if the function has not finished fixing everything, false if it has finished.
|
||||
*/
|
||||
function upgrade_calendar_standard_events_fix(stdClass $info, bool $output = true, int $endtime = 0): bool {
|
||||
global $DB;
|
||||
|
||||
$return = false; // Let's assume the function is going to finish by default.
|
||||
$status = "Finished!"; // To decide the message to be presented on return.
|
||||
|
||||
upgrade_calendar_events_mtrace('Processing standard events', $output);
|
||||
|
||||
$rs = $DB->get_recordset_sql("
|
||||
SELECT DISTINCT eventtype, courseid
|
||||
FROM {event}
|
||||
WHERE {$info->sql}");
|
||||
|
||||
foreach ($rs as $record) {
|
||||
switch ($record->eventtype) {
|
||||
case 'site':
|
||||
case 'category':
|
||||
// These are created by admin.
|
||||
$DB->set_field('event', 'userid', get_admin()->id, ['eventtype' => $record->eventtype]);
|
||||
break;
|
||||
case 'course':
|
||||
case 'group':
|
||||
// These are created by course teacher.
|
||||
$DB->set_field('event', 'userid', upgrade_calendar_events_get_teacherid($record->courseid),
|
||||
['eventtype' => $record->eventtype, 'courseid' => $record->courseid]);
|
||||
break;
|
||||
}
|
||||
|
||||
// Cutoff time, let's exit.
|
||||
if ($endtime && $endtime <= time()) {
|
||||
$status = 'Remaining standard events pending';
|
||||
$return = true; // Not finished yet.
|
||||
break;
|
||||
}
|
||||
upgrade_calendar_events_mtrace('.', $output);
|
||||
}
|
||||
$rs->close();
|
||||
upgrade_calendar_events_mtrace($status, $output);
|
||||
upgrade_calendar_events_mtrace('', $output);
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the calendar subscription events needing to be fixed. With optional output.
|
||||
*
|
||||
* @param stdClass $info an object with total and bad counters, plus sql to retrieve them.
|
||||
* @param bool $output true if the function must output information, false if not.
|
||||
* @param int $endtime cutoff time when the process must stop (0 means no cutoff).
|
||||
* @return bool true if the function has not finished fixing everything, false if it has finished.
|
||||
*/
|
||||
function upgrade_calendar_subscription_events_fix(stdClass $info, bool $output = true, int $endtime = 0): bool {
|
||||
global $DB;
|
||||
|
||||
$return = false; // Let's assume the function is going to finish by default.
|
||||
$status = "Finished!"; // To decide the message to be presented on return.
|
||||
|
||||
upgrade_calendar_events_mtrace('Processing subscription events', $output);
|
||||
|
||||
$rs = $DB->get_recordset_sql("
|
||||
SELECT DISTINCT subscriptionid AS id
|
||||
FROM {event}
|
||||
WHERE {$info->sql}");
|
||||
|
||||
foreach ($rs as $subscription) {
|
||||
// Subscriptions can be site or category level, let's put the admin as userid.
|
||||
// (note that "user" subscription weren't deleted so there is nothing to recover with them.
|
||||
$DB->set_field('event_subscriptions', 'userid', get_admin()->id, ['id' => $subscription->id]);
|
||||
$DB->set_field('event', 'userid', get_admin()->id, ['subscriptionid' => $subscription->id]);
|
||||
|
||||
// Cutoff time, let's exit.
|
||||
if ($endtime && $endtime <= time()) {
|
||||
$status = 'Remaining subscription events pending';
|
||||
$return = true; // Not finished yet.
|
||||
break;
|
||||
}
|
||||
upgrade_calendar_events_mtrace('.', $output);
|
||||
}
|
||||
$rs->close();
|
||||
upgrade_calendar_events_mtrace($status, $output);
|
||||
upgrade_calendar_events_mtrace('', $output);
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the calendar action events needing to be fixed. With optional output.
|
||||
*
|
||||
* @param stdClass $info an object with total and bad counters, plus sql to retrieve them.
|
||||
* @param bool $output true if the function must output information, false if not.
|
||||
* @param int $endtime cutoff time when the process must stop (0 means no cutoff).
|
||||
* @return bool true if the function has not finished fixing everything, false if it has finished.
|
||||
*/
|
||||
function upgrade_calendar_action_events_fix(stdClass $info, bool $output = true, int $endtime = 0): bool {
|
||||
global $DB;
|
||||
|
||||
$return = false; // Let's assume the function is going to finish by default.
|
||||
$status = "Finished!"; // To decide the message to be presented on return.
|
||||
|
||||
upgrade_calendar_events_mtrace('Processing action events', $output);
|
||||
|
||||
$rs = $DB->get_recordset_sql("
|
||||
SELECT DISTINCT modulename, instance, courseid
|
||||
FROM {event}
|
||||
WHERE {$info->sql}");
|
||||
|
||||
foreach ($rs as $record) {
|
||||
// These are created by course teacher.
|
||||
$DB->set_field('event', 'userid', upgrade_calendar_events_get_teacherid($record->courseid),
|
||||
['modulename' => $record->modulename, 'instance' => $record->instance, 'courseid' => $record->courseid]);
|
||||
|
||||
// Cutoff time, let's exit.
|
||||
if ($endtime && $endtime <= time()) {
|
||||
$status = 'Remaining action events pending';
|
||||
$return = true; // Not finished yet.
|
||||
break;
|
||||
}
|
||||
upgrade_calendar_events_mtrace('.', $output);
|
||||
}
|
||||
$rs->close();
|
||||
upgrade_calendar_events_mtrace($status, $output);
|
||||
upgrade_calendar_events_mtrace('', $output);
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the calendar override events needing to be fixed. With optional output.
|
||||
*
|
||||
* @param stdClass $info an object with total and bad counters, plus sql to retrieve them.
|
||||
* @param bool $output true if the function must output information, false if not.
|
||||
* @param int $endtime cutoff time when the process must stop (0 means no cutoff).
|
||||
* @return bool true if the function has not finished fixing everything, false if it has finished.
|
||||
*/
|
||||
function upgrade_calendar_override_events_fix(stdClass $info, bool $output = true, int $endtime = 0): bool {
|
||||
global $CFG, $DB;
|
||||
|
||||
include_once($CFG->dirroot. '/course/lib.php');
|
||||
include_once($CFG->dirroot. '/mod/assign/lib.php');
|
||||
include_once($CFG->dirroot. '/mod/assign/locallib.php');
|
||||
include_once($CFG->dirroot. '/mod/lesson/lib.php');
|
||||
include_once($CFG->dirroot. '/mod/lesson/locallib.php');
|
||||
include_once($CFG->dirroot. '/mod/quiz/lib.php');
|
||||
include_once($CFG->dirroot. '/mod/quiz/locallib.php');
|
||||
|
||||
$return = false; // Let's assume the function is going to finish by default.
|
||||
$status = "Finished!"; // To decide the message to be presented on return.
|
||||
|
||||
upgrade_calendar_events_mtrace('Processing override events', $output);
|
||||
|
||||
$rs = $DB->get_recordset_sql("
|
||||
SELECT DISTINCT modulename, instance
|
||||
FROM {event}
|
||||
WHERE {$info->sql}");
|
||||
|
||||
foreach ($rs as $module) {
|
||||
// Remove all the records from the events table for the module.
|
||||
$DB->delete_records('event', ['modulename' => $module->modulename, 'instance' => $module->instance]);
|
||||
|
||||
// Get the activity record.
|
||||
if (!$activityrecord = $DB->get_record($module->modulename, ['id' => $module->instance])) {
|
||||
// Orphaned calendar event (activity doesn't exists), skip.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Let's rebuild it by calling to each module API.
|
||||
switch ($module->modulename) {
|
||||
case 'assign';
|
||||
if (function_exists('assign_prepare_update_events')) {
|
||||
assign_prepare_update_events($activityrecord);
|
||||
}
|
||||
break;
|
||||
case 'lesson':
|
||||
if (function_exists('lesson_update_events')) {
|
||||
lesson_update_events($activityrecord);
|
||||
}
|
||||
break;
|
||||
case 'quiz':
|
||||
if (function_exists('quiz_update_events')) {
|
||||
quiz_update_events($activityrecord);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Sometimes, some (group) overrides are created without userid, when that happens, they deserve
|
||||
// some user (teacher or admin). This doesn't affect to groups calendar events behaviour,
|
||||
// but allows counters to detect already processed group overrides and makes things
|
||||
// consistent.
|
||||
$DB->set_field_select('event', 'userid', upgrade_calendar_events_get_teacherid($activityrecord->course),
|
||||
'modulename = ? AND instance = ? and priority != 0 and userid = 0',
|
||||
['modulename' => $module->modulename, 'instance' => $module->instance]);
|
||||
|
||||
// Cutoff time, let's exit.
|
||||
if ($endtime && $endtime <= time()) {
|
||||
$status = 'Remaining override events pending';
|
||||
$return = true; // Not finished yet.
|
||||
break;
|
||||
}
|
||||
upgrade_calendar_events_mtrace('.', $output);
|
||||
}
|
||||
$rs->close();
|
||||
upgrade_calendar_events_mtrace($status, $output);
|
||||
upgrade_calendar_events_mtrace('', $output);
|
||||
return $return;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
require_once($CFG->libdir.'/upgradelib.php');
|
||||
require_once($CFG->libdir.'/db/upgradelib.php');
|
||||
require_once($CFG->dirroot . '/calendar/tests/helpers.php');
|
||||
|
||||
/**
|
||||
* Tests various classes and functions in upgradelib.php library.
|
||||
@ -1129,4 +1130,364 @@ class core_upgradelib_testcase extends advanced_testcase {
|
||||
$actualshortnames = $DB->get_records_menu('license', null, '', 'id, shortname');
|
||||
$this->assertNotContains($deletedcorelicenseshortname, $actualshortnames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute same problematic query from upgrade step.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function run_upgrade_step_query() {
|
||||
global $DB;
|
||||
|
||||
return $DB->execute("UPDATE {event} SET userid = 0 WHERE eventtype <> 'user' OR priority <> 0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the functionality of upgrade_calendar_events_status() function.
|
||||
*/
|
||||
public function test_upgrade_calendar_events_status() {
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$events = create_standard_events(5);
|
||||
$eventscount = count($events);
|
||||
|
||||
// Run same DB query as the problematic upgrade step.
|
||||
$this->run_upgrade_step_query();
|
||||
|
||||
// Get the events info.
|
||||
$status = upgrade_calendar_events_status(false);
|
||||
|
||||
// Total events.
|
||||
$expected = [
|
||||
'total' => (object)[
|
||||
'count' => $eventscount,
|
||||
'bad' => $eventscount - 5, // Event count excluding user events.
|
||||
],
|
||||
'standard' => (object)[
|
||||
'count' => $eventscount,
|
||||
'bad' => $eventscount - 5, // Event count excluding user events.
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEquals($expected['standard']->count, $status['standard']->count);
|
||||
$this->assertEquals($expected['standard']->bad, $status['standard']->bad);
|
||||
$this->assertEquals($expected['total']->count, $status['total']->count);
|
||||
$this->assertEquals($expected['total']->bad, $status['total']->bad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the functionality of upgrade_calendar_events_get_teacherid() function.
|
||||
*/
|
||||
public function test_upgrade_calendar_events_get_teacherid() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Create a new course and enrol a user as editing teacher.
|
||||
$generator = $this->getDataGenerator();
|
||||
$course = $generator->create_course();
|
||||
$teacher = $generator->create_and_enrol($course, 'editingteacher');
|
||||
|
||||
// There's a teacher enrolled in the course, return its user id.
|
||||
$userid = upgrade_calendar_events_get_teacherid($course->id);
|
||||
|
||||
// It should return the enrolled teacher by default.
|
||||
$this->assertEquals($teacher->id, $userid);
|
||||
|
||||
// Un-enrol teacher from course.
|
||||
$instance = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'manual']);
|
||||
enrol_get_plugin('manual')->unenrol_user($instance, $teacher->id);
|
||||
|
||||
// Since there are no teachers enrolled in the course, fallback to admin user id.
|
||||
$admin = get_admin();
|
||||
$userid = upgrade_calendar_events_get_teacherid($course->id);
|
||||
$this->assertEquals($admin->id, $userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the functionality of upgrade_calendar_standard_events_fix() function.
|
||||
*/
|
||||
public function test_upgrade_calendar_standard_events_fix() {
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$events = create_standard_events(5);
|
||||
$eventscount = count($events);
|
||||
|
||||
// Get the events info.
|
||||
$info = upgrade_calendar_events_status(false);
|
||||
|
||||
// There should be no standard events to be fixed.
|
||||
$this->assertEquals(0, $info['standard']->bad);
|
||||
|
||||
// No events to be fixed, should return false.
|
||||
$this->assertFalse(upgrade_calendar_standard_events_fix($info['standard'], false));
|
||||
|
||||
// Run same problematic DB query.
|
||||
$this->run_upgrade_step_query();
|
||||
|
||||
// Get the events info.
|
||||
$info = upgrade_calendar_events_status(false);
|
||||
|
||||
// There should be 20 events to be fixed (five from each type except user).
|
||||
$this->assertEquals($eventscount - 5, $info['standard']->bad);
|
||||
|
||||
// Test the function runtime, passing -1 as end time.
|
||||
// It should not be able to fix all events so fast, so some events should remain to be fixed in the next run.
|
||||
$result = upgrade_calendar_standard_events_fix($info['standard'], false, -1);
|
||||
$this->assertNotFalse($result);
|
||||
|
||||
// Call the function again, this time it will run until all events have been fixed.
|
||||
$this->assertFalse(upgrade_calendar_standard_events_fix($info['standard'], false));
|
||||
|
||||
// Get the events info again.
|
||||
$info = upgrade_calendar_events_status(false);
|
||||
|
||||
// All standard events should have been recovered.
|
||||
// There should be no standard events flagged to be fixed.
|
||||
$this->assertEquals(0, $info['standard']->bad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the functionality of upgrade_calendar_subscription_events_fix() function.
|
||||
*/
|
||||
public function test_upgrade_calendar_subscription_events_fix() {
|
||||
global $CFG, $DB;
|
||||
|
||||
require_once($CFG->dirroot . '/calendar/lib.php');
|
||||
require_once($CFG->dirroot . '/lib/bennu/bennu.inc.php');
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create event subscription.
|
||||
$subscription = new stdClass;
|
||||
$subscription->name = 'Repeated events';
|
||||
$subscription->importfrom = CALENDAR_IMPORT_FROM_FILE;
|
||||
$subscription->eventtype = 'site';
|
||||
$id = calendar_add_subscription($subscription);
|
||||
|
||||
// Get repeated events ICS file.
|
||||
$calendar = file_get_contents($CFG->dirroot . '/lib/tests/fixtures/repeated_events.ics');
|
||||
$ical = new iCalendar();
|
||||
$ical->unserialize($calendar);
|
||||
|
||||
// Import subscription events.
|
||||
calendar_import_icalendar_events($ical, null, $id);
|
||||
|
||||
// Subscription should have added 18 events.
|
||||
$eventscount = $DB->count_records('event');
|
||||
|
||||
// Get the events info.
|
||||
$info = upgrade_calendar_events_status(false);
|
||||
|
||||
// There should be no subscription events to be fixed at this point.
|
||||
$this->assertEquals(0, $info['subscription']->bad);
|
||||
|
||||
// No events to be fixed, should return false.
|
||||
$this->assertFalse(upgrade_calendar_subscription_events_fix($info['subscription'], false));
|
||||
|
||||
// Run same problematic DB query.
|
||||
$this->run_upgrade_step_query();
|
||||
|
||||
// Get the events info and assert total number of events is correct.
|
||||
$info = upgrade_calendar_events_status(false);
|
||||
$subscriptioninfo = $info['subscription'];
|
||||
|
||||
$this->assertEquals($eventscount, $subscriptioninfo->count);
|
||||
|
||||
// Since we have added our subscription as site, all sub events have been affected.
|
||||
$this->assertEquals($eventscount, $subscriptioninfo->bad);
|
||||
|
||||
// Test the function runtime, passing -1 as end time.
|
||||
// It should not be able to fix all events so fast, so some events should remain to be fixed in the next run.
|
||||
$result = upgrade_calendar_subscription_events_fix($subscriptioninfo, false, -1);
|
||||
$this->assertNotFalse($result);
|
||||
|
||||
// Call the function again, this time it will run until all events have been fixed.
|
||||
$this->assertFalse(upgrade_calendar_subscription_events_fix($subscriptioninfo, false));
|
||||
|
||||
// Get the events info again.
|
||||
$info = upgrade_calendar_events_status(false);
|
||||
|
||||
// All standard events should have been recovered.
|
||||
// There should be no standard events flagged to be fixed.
|
||||
$this->assertEquals(0, $info['subscription']->bad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the functionality of upgrade_calendar_action_events_fix() function.
|
||||
*/
|
||||
public function test_upgrade_calendar_action_events_fix() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create a new course and a choice activity.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$choice = $this->getDataGenerator()->create_module('choice', ['course' => $course->id]);
|
||||
|
||||
// Create some action events.
|
||||
create_action_event(['courseid' => $course->id, 'modulename' => 'choice', 'instance' => $choice->id,
|
||||
'eventtype' => CHOICE_EVENT_TYPE_OPEN]);
|
||||
create_action_event(['courseid' => $course->id, 'modulename' => 'choice', 'instance' => $choice->id,
|
||||
'eventtype' => CHOICE_EVENT_TYPE_CLOSE]);
|
||||
|
||||
$eventscount = $DB->count_records('event');
|
||||
|
||||
// Get the events info.
|
||||
$info = upgrade_calendar_events_status(false);
|
||||
$actioninfo = $info['action'];
|
||||
|
||||
// There should be no standard events to be fixed.
|
||||
$this->assertEquals(0, $actioninfo->bad);
|
||||
|
||||
// No events to be fixed, should return false.
|
||||
$this->assertFalse(upgrade_calendar_action_events_fix($actioninfo, false));
|
||||
|
||||
// Run same problematic DB query.
|
||||
$this->run_upgrade_step_query();
|
||||
|
||||
// Get the events info.
|
||||
$info = upgrade_calendar_events_status(false);
|
||||
$actioninfo = $info['action'];
|
||||
|
||||
// There should be 2 events to be fixed.
|
||||
$this->assertEquals($eventscount, $actioninfo->bad);
|
||||
|
||||
// Test the function runtime, passing -1 as end time.
|
||||
// It should not be able to fix all events so fast, so some events should remain to be fixed in the next run.
|
||||
$this->assertNotFalse(upgrade_calendar_action_events_fix($actioninfo, false, -1));
|
||||
|
||||
// Call the function again, this time it will run until all events have been fixed.
|
||||
$this->assertFalse(upgrade_calendar_action_events_fix($actioninfo, false));
|
||||
|
||||
// Get the events info again.
|
||||
$info = upgrade_calendar_events_status(false);
|
||||
|
||||
// All standard events should have been recovered.
|
||||
// There should be no standard events flagged to be fixed.
|
||||
$this->assertEquals(0, $info['action']->bad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the user override part of upgrade_calendar_override_events_fix() function.
|
||||
*/
|
||||
public function test_upgrade_calendar_user_override_events_fix() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
// Create a new course.
|
||||
$course = $generator->create_course();
|
||||
|
||||
// Create few users and enrol as students.
|
||||
$student1 = $generator->create_and_enrol($course, 'student');
|
||||
$student2 = $generator->create_and_enrol($course, 'student');
|
||||
$student3 = $generator->create_and_enrol($course, 'student');
|
||||
|
||||
// Create some activities and some override events.
|
||||
foreach (['assign', 'lesson', 'quiz'] as $modulename) {
|
||||
$instance = $generator->create_module($modulename, ['course' => $course->id]);
|
||||
create_user_override_event($modulename, $instance->id, $student1->id);
|
||||
create_user_override_event($modulename, $instance->id, $student2->id);
|
||||
create_user_override_event($modulename, $instance->id, $student3->id);
|
||||
}
|
||||
|
||||
// There should be 9 override events to be fixed (three from each module).
|
||||
$eventscount = $DB->count_records('event');
|
||||
$this->assertEquals(9, $eventscount);
|
||||
|
||||
// Get the events info.
|
||||
$info = upgrade_calendar_events_status(false);
|
||||
$overrideinfo = $info['override'];
|
||||
|
||||
// There should be no standard events to be fixed.
|
||||
$this->assertEquals(0, $overrideinfo->bad);
|
||||
|
||||
// No events to be fixed, should return false.
|
||||
$this->assertFalse(upgrade_calendar_override_events_fix($overrideinfo, false));
|
||||
|
||||
// Run same problematic DB query.
|
||||
$this->run_upgrade_step_query();
|
||||
|
||||
// Get the events info.
|
||||
$info = upgrade_calendar_events_status(false);
|
||||
$overrideinfo = $info['override'];
|
||||
|
||||
// There should be 9 events to be fixed (three from each module).
|
||||
$this->assertEquals($eventscount, $overrideinfo->bad);
|
||||
|
||||
// Call the function again, this time it will run until all events have been fixed.
|
||||
$this->assertFalse(upgrade_calendar_override_events_fix($overrideinfo, false));
|
||||
|
||||
// Get the events info again.
|
||||
$info = upgrade_calendar_events_status(false);
|
||||
|
||||
// All standard events should have been recovered.
|
||||
// There should be no standard events flagged to be fixed.
|
||||
$this->assertEquals(0, $info['override']->bad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the group override part of upgrade_calendar_override_events_fix() function.
|
||||
*/
|
||||
public function test_upgrade_calendar_group_override_events_fix() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
// Create a new course and few groups.
|
||||
$course = $generator->create_course();
|
||||
$group1 = $generator->create_group(['courseid' => $course->id]);
|
||||
$group2 = $generator->create_group(['courseid' => $course->id]);
|
||||
$group3 = $generator->create_group(['courseid' => $course->id]);
|
||||
|
||||
// Create some activities and some override events.
|
||||
foreach (['assign', 'lesson', 'quiz'] as $modulename) {
|
||||
$instance = $generator->create_module($modulename, ['course' => $course->id]);
|
||||
create_group_override_event($modulename, $instance->id, $course->id, $group1->id);
|
||||
create_group_override_event($modulename, $instance->id, $course->id, $group2->id);
|
||||
create_group_override_event($modulename, $instance->id, $course->id, $group3->id);
|
||||
}
|
||||
|
||||
// There should be 9 override events to be fixed (three from each module).
|
||||
$eventscount = $DB->count_records('event');
|
||||
$this->assertEquals(9, $eventscount);
|
||||
|
||||
// Get the events info.
|
||||
$info = upgrade_calendar_events_status(false);
|
||||
|
||||
// We classify group overrides as action events since they do not record the userid.
|
||||
$groupoverrideinfo = $info['action'];
|
||||
|
||||
// There should be no events to be fixed.
|
||||
$this->assertEquals(0, $groupoverrideinfo->bad);
|
||||
|
||||
// No events to be fixed, should return false.
|
||||
$this->assertFalse(upgrade_calendar_action_events_fix($groupoverrideinfo, false));
|
||||
|
||||
// Run same problematic DB query.
|
||||
$this->run_upgrade_step_query();
|
||||
|
||||
// Get the events info.
|
||||
$info = upgrade_calendar_events_status(false);
|
||||
$this->assertEquals(9, $info['action']->bad);
|
||||
|
||||
// Call the function again, this time it will run until all events have been fixed.
|
||||
$this->assertFalse(upgrade_calendar_action_events_fix($info['action'], false));
|
||||
|
||||
// Since group override events do not set userid, these events should not be flagged to be fixed.
|
||||
$this->assertEquals(0, $groupoverrideinfo->bad);
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2021052500.84; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2021052500.85; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
$release = '4.0dev (Build: 20210420)'; // Human-friendly version name
|
||||
|
Loading…
x
Reference in New Issue
Block a user