2017-02-03 05:59:51 +00:00
|
|
|
<?php
|
|
|
|
// This file is part of Moodle - http://moodle.org/
|
|
|
|
//
|
|
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This file contains helper classes and functions for testing.
|
|
|
|
*
|
|
|
|
* @package core_calendar
|
|
|
|
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
|
*/
|
|
|
|
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
|
2017-02-21 15:38:10 +08:00
|
|
|
global $CFG;
|
|
|
|
|
|
|
|
require_once($CFG->dirroot . '/calendar/lib.php');
|
2017-02-03 05:59:51 +00:00
|
|
|
|
|
|
|
use core_calendar\local\event\entities\action_event;
|
|
|
|
use core_calendar\local\event\entities\event;
|
|
|
|
use core_calendar\local\event\entities\repeat_event_collection;
|
2017-03-22 23:42:28 +08:00
|
|
|
use core_calendar\local\event\proxies\std_proxy;
|
2017-09-19 11:39:39 +08:00
|
|
|
use core_calendar\local\event\proxies\coursecat_proxy;
|
2017-04-24 14:09:09 +08:00
|
|
|
use core_calendar\local\event\proxies\cm_info_proxy;
|
2017-02-03 05:59:51 +00:00
|
|
|
use core_calendar\local\event\value_objects\action;
|
|
|
|
use core_calendar\local\event\value_objects\event_description;
|
|
|
|
use core_calendar\local\event\value_objects\event_times;
|
2017-03-30 15:33:13 +08:00
|
|
|
use core_calendar\local\event\factories\event_factory_interface;
|
2017-02-03 05:59:51 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a calendar event with the given properties.
|
|
|
|
*
|
|
|
|
* @param array $properties The properties to set on the event
|
2017-03-29 13:33:11 +08:00
|
|
|
* @return \calendar_event
|
2017-02-03 05:59:51 +00:00
|
|
|
*/
|
|
|
|
function create_event($properties) {
|
|
|
|
$record = new \stdClass();
|
|
|
|
$record->name = 'event name';
|
MDL-66612 calendar: Change global events to site events
AMOS BEGIN
CPY [category,moodle],[category,calendar]
CPY [site,notes],[eventtypesite,calendar]
CPY [site,moodle],[site,calendar]
MOV [typesite,moodle],[siteevent,calendar]
AMOS END
2019-09-06 08:46:28 +02:00
|
|
|
$record->eventtype = 'site';
|
2017-02-03 05:59:51 +00:00
|
|
|
$record->repeat = 0;
|
|
|
|
$record->repeats = 0;
|
|
|
|
$record->timestart = time();
|
|
|
|
$record->timeduration = 0;
|
|
|
|
$record->timesort = 0;
|
|
|
|
$record->type = CALENDAR_EVENT_TYPE_STANDARD;
|
|
|
|
$record->courseid = 0;
|
2017-11-15 17:16:21 +01:00
|
|
|
$record->categoryid = 0;
|
2017-02-03 05:59:51 +00:00
|
|
|
|
|
|
|
foreach ($properties as $name => $value) {
|
|
|
|
$record->$name = $value;
|
|
|
|
}
|
|
|
|
|
2017-03-29 13:33:11 +08:00
|
|
|
$event = new \calendar_event($record);
|
2017-02-03 05:59:51 +00:00
|
|
|
return $event->create($record);
|
|
|
|
}
|
|
|
|
|
MDL-71156 core: machinery to recover orphaned calendar events
The machinery to fix orphaned calendar events that were broken by MDL-67494.
The solution consists of:
1) Upgrade step that checks if this site has executed the problematic upgrade steps and
if positive, it will schedule a new run for calendar_fix_orphaned_events adhoc task.
2) Adhoc task that will self-spawn calling the recovery machinery, running until
all the orphaned calendar events are fixed. It also sets the maximum runtime of
60 seconds. It is also possible to override that number by specifing the desired
number setting the ->calendareventsmaxseconds in your config.php
3) CLI script that 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.
4) Unit tests and helper functions to generate calendar events. We have decided to
keep the tests simple, testing only true and false and not using data generators because
the code is purely to recover the calendar events and won't turn into an API or something
and also due to the urgency of this issue.
The helpers have been created in calendar/tests/helpers.php since there are no data generators
for calendar.
2021-04-16 11:42:33 +08:00
|
|
|
/**
|
|
|
|
* 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,
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2017-02-03 05:59:51 +00:00
|
|
|
/**
|
|
|
|
* A test factory that will create action events.
|
2017-03-24 16:58:54 +08:00
|
|
|
*
|
|
|
|
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
|
|
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late
|
2017-02-03 05:59:51 +00:00
|
|
|
*/
|
|
|
|
class action_event_test_factory implements event_factory_interface {
|
|
|
|
|
2017-03-24 16:58:54 +08:00
|
|
|
/**
|
|
|
|
* @var callable $callback.
|
|
|
|
*/
|
2017-02-03 05:59:51 +00:00
|
|
|
private $callback;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A test factory that will create action events. The factory accepts a callback
|
|
|
|
* that will be used to determine if the event should be returned or not.
|
|
|
|
*
|
|
|
|
* The callback will be given the event and should return true if the event
|
|
|
|
* should be returned and false otherwise.
|
2017-03-24 16:58:54 +08:00
|
|
|
*
|
|
|
|
* @param callable $callback The callback.
|
2017-02-03 05:59:51 +00:00
|
|
|
*/
|
|
|
|
public function __construct($callback = null) {
|
|
|
|
$this->callback = $callback;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function create_instance(\stdClass $record) {
|
|
|
|
$module = null;
|
|
|
|
$subscription = null;
|
|
|
|
|
|
|
|
if ($record->instance && $record->modulename) {
|
2017-04-24 14:09:09 +08:00
|
|
|
$module = new cm_info_proxy($record->instance, $record->modulename, $record->courseid);
|
2017-02-03 05:59:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($record->subscriptionid) {
|
|
|
|
$subscription = new std_proxy($record->subscriptionid, function($id) {
|
|
|
|
return (object)['id' => $id];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
$event = new event(
|
|
|
|
$record->id,
|
|
|
|
$record->name,
|
|
|
|
new event_description($record->description, $record->format),
|
2017-09-19 11:39:39 +08:00
|
|
|
new coursecat_proxy($record->categoryid),
|
2017-02-03 05:59:51 +00:00
|
|
|
new std_proxy($record->courseid, function($id) {
|
|
|
|
$course = new \stdClass();
|
|
|
|
$course->id = $id;
|
|
|
|
return $course;
|
|
|
|
}),
|
|
|
|
new std_proxy($record->groupid, function($id) {
|
|
|
|
$group = new \stdClass();
|
|
|
|
$group->id = $id;
|
|
|
|
return $group;
|
|
|
|
}),
|
|
|
|
new std_proxy($record->userid, function($id) {
|
|
|
|
$user = new \stdClass();
|
|
|
|
$user->id = $id;
|
|
|
|
return $user;
|
|
|
|
}),
|
2018-02-12 05:44:06 +00:00
|
|
|
!empty($record->repeatid) ? new repeat_event_collection($record, $this) : null,
|
2017-02-03 05:59:51 +00:00
|
|
|
$module,
|
|
|
|
$record->eventtype,
|
|
|
|
new event_times(
|
|
|
|
(new \DateTimeImmutable())->setTimestamp($record->timestart),
|
|
|
|
(new \DateTimeImmutable())->setTimestamp($record->timestart + $record->timeduration),
|
|
|
|
(new \DateTimeImmutable())->setTimestamp($record->timesort ? $record->timesort : $record->timestart),
|
2020-07-23 14:53:49 +10:00
|
|
|
(new \DateTimeImmutable())->setTimestamp($record->timemodified),
|
2021-02-17 12:41:12 +01:00
|
|
|
(new \DateTimeImmutable())->setTimestamp($record->timesort ? usergetmidnight($record->timesort) : 0)
|
2017-02-03 05:59:51 +00:00
|
|
|
),
|
|
|
|
!empty($record->visible),
|
2018-05-24 09:44:54 +08:00
|
|
|
$subscription,
|
2020-04-29 22:22:10 +02:00
|
|
|
$record->location,
|
|
|
|
!empty($record->component) ? $record->component : null
|
2017-02-03 05:59:51 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
$action = new action(
|
|
|
|
'Test action',
|
|
|
|
new \moodle_url('/'),
|
2017-02-21 15:38:10 +08:00
|
|
|
1,
|
|
|
|
true
|
2017-02-03 05:59:51 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
$actionevent = new action_event($event, $action);
|
|
|
|
|
|
|
|
if ($callback = $this->callback) {
|
|
|
|
return $callback($actionevent) ? $actionevent : false;
|
|
|
|
} else {
|
|
|
|
return $actionevent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|