first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
<?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/>.
/**
* Action factory.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar;
defined('MOODLE_INTERNAL') || die();
use core_calendar\local\event\factories\action_factory_interface;
use core_calendar\local\event\value_objects\action;
/**
* Action factory class.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class action_factory implements action_factory_interface {
public function create_instance($name, \moodle_url $url, $itemcount, $actionable) {
return new action ($name, $url, $itemcount, $actionable);
}
}
+93
View File
@@ -0,0 +1,93 @@
<?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/>.
/**
* The mform for exporting calendar events
*
* @package core_calendar
* @copyright 2014 Brian Barnes
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// Always include formslib.
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); // It must be included from a Moodle page.
}
require_once($CFG->dirroot.'/lib/formslib.php');
/**
* The mform class for creating and editing a calendar
*
* @copyright 2014 Brian Barnes
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_calendar_export_form extends moodleform {
/**
* The export form definition
* @throws coding_exception
*/
public function definition() {
global $CFG;
$mform = $this->_form;
$mform->addElement('html', '<div class="mt-3 mb-xl-6">' . get_string('exporthelp', 'calendar') . '</div>');
$export = array();
$export[] = $mform->createElement('radio', 'exportevents', '', get_string('eventsall', 'calendar'), 'all');
$export[] = $mform->createElement('radio', 'exportevents', '', get_string('eventsrelatedtocategories', 'calendar'), 'categories');
$export[] = $mform->createElement('radio', 'exportevents', '', get_string('eventsrelatedtocourses', 'calendar'), 'courses');
$export[] = $mform->createElement('radio', 'exportevents', '', get_string('eventsrelatedtogroups', 'calendar'), 'groups');
$export[] = $mform->createElement('radio', 'exportevents', '', get_string('eventspersonal', 'calendar'), 'user');
$mform->addGroup($export, 'events', get_string('eventstoexport', 'calendar'), '<br/>');
$mform->addGroupRule('events', get_string('required'), 'required');
$mform->setDefault('events', 'all');
$range = array();
if ($this->_customdata['allowthisweek']) {
$range[] = $mform->createElement('radio', 'timeperiod', '', get_string('weekthis', 'calendar'), 'weeknow');
}
if ($this->_customdata['allownextweek']) {
$range[] = $mform->createElement('radio', 'timeperiod', '', get_string('weeknext', 'calendar'), 'weeknext');
}
$range[] = $mform->createElement('radio', 'timeperiod', '', get_string('monththis', 'calendar'), 'monthnow');
if ($this->_customdata['allownextmonth']) {
$range[] = $mform->createElement('radio', 'timeperiod', '', get_string('monthnext', 'calendar'), 'monthnext');
}
$range[] = $mform->createElement('radio', 'timeperiod', '', get_string('recentupcoming', 'calendar'), 'recentupcoming');
if ($CFG->calendar_customexport) {
$a = new stdClass();
$now = time();
$time = $now - $CFG->calendar_exportlookback * DAYSECS;
$a->timestart = userdate($time, get_string('strftimedatefullshort', 'langconfig'));
$time = $now + $CFG->calendar_exportlookahead * DAYSECS;
$a->timeend = userdate($time, get_string('strftimedatefullshort', 'langconfig'));
$range[] = $mform->createElement('radio', 'timeperiod', '', get_string('customexport', 'calendar', $a), 'custom');
}
$mform->addGroup($range, 'period', get_string('timeperiod', 'calendar'), '<br/>');
$mform->addGroupRule('period', get_string('required'), 'required');
$mform->setDefault('period', 'recentupcoming');
$buttons = array();
$buttons[] = $mform->createElement('submit', 'generateurl', get_string('generateurlbutton', 'calendar'));
$buttons[] = $mform->createElement('submit', 'export', get_string('exportbutton', 'calendar'));
$mform->addGroup($buttons);
}
}
+279
View File
@@ -0,0 +1,279 @@
<?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/>.
namespace core_calendar\external;
use core\external\exporter;
use core_date;
use DateTimeImmutable;
use renderer_base;
use moodle_url;
use core_calendar\local\event\container;
/**
* Class for displaying the day view.
*
* @package core_calendar
* @copyright 2017 Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class calendar_day_exporter extends exporter {
/**
* @var \calendar_information $calendar The calendar to be rendered.
*/
protected $calendar;
/**
* @var moodle_url $url The URL for the day view page.
*/
protected $url;
/**
* Constructor for day exporter.
*
* @param \calendar_information $calendar The calendar being represented.
* @param array $related The related information
*/
public function __construct(\calendar_information $calendar, $related) {
$this->calendar = $calendar;
parent::__construct([], $related);
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'events' => [
'type' => calendar_event_exporter::read_properties_definition(),
'multiple' => true,
],
'defaulteventcontext' => [
'type' => PARAM_INT,
'default' => 0,
],
'filter_selector' => [
'type' => PARAM_RAW,
],
'courseid' => [
'type' => PARAM_INT,
],
'categoryid' => [
'type' => PARAM_INT,
'optional' => true,
'default' => 0,
],
'neweventtimestamp' => [
'type' => PARAM_INT,
],
'date' => [
'type' => date_exporter::read_properties_definition(),
],
'periodname' => [
// Note: We must use RAW here because the calendar type returns the formatted month name based on a
// calendar format.
'type' => PARAM_RAW,
],
'previousperiod' => [
'type' => date_exporter::read_properties_definition(),
],
'previousperiodlink' => [
'type' => PARAM_URL,
],
'previousperiodname' => [
// Note: We must use RAW here because the calendar type returns the formatted month name based on a
// calendar format.
'type' => PARAM_RAW,
],
'nextperiod' => [
'type' => date_exporter::read_properties_definition(),
],
'nextperiodname' => [
// Note: We must use RAW here because the calendar type returns the formatted month name based on a
// calendar format.
'type' => PARAM_RAW,
],
'nextperiodlink' => [
'type' => PARAM_URL,
],
'larrow' => [
// The left arrow defined by the theme.
'type' => PARAM_RAW,
],
'rarrow' => [
// The right arrow defined by the theme.
'type' => PARAM_RAW,
],
];
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
$timestamp = $this->calendar->time;
$cache = $this->related['cache'];
$url = new moodle_url('/calendar/view.php', [
'view' => 'day',
'time' => $timestamp,
]);
if ($this->calendar->course && SITEID !== $this->calendar->course->id) {
$url->param('course', $this->calendar->course->id);
} else if ($this->calendar->categoryid) {
$url->param('category', $this->calendar->categoryid);
}
$this->url = $url;
$return['events'] = array_map(function($event) use ($cache, $output, $url) {
$context = $cache->get_context($event);
$course = $cache->get_course($event);
$moduleinstance = $cache->get_module_instance($event);
$exporter = new calendar_event_exporter($event, [
'context' => $context,
'course' => $course,
'moduleinstance' => $moduleinstance,
'daylink' => $url,
'type' => $this->related['type'],
'today' => $this->calendar->time,
]);
$data = $exporter->export($output);
// We need to override default formatted time because it differs from day view.
// Formatted time for day view adds a link to the day view.
$legacyevent = container::get_event_mapper()->from_event_to_legacy_event($event);
$data->formattedtime = calendar_format_event_time($legacyevent, time(), null);
return $data;
}, $this->related['events']);
if ($context = $this->get_default_add_context()) {
$return['defaulteventcontext'] = $context->id;
}
if ($this->calendar->categoryid) {
$return['categoryid'] = $this->calendar->categoryid;
}
$return['filter_selector'] = $this->get_course_filter_selector($output);
$return['courseid'] = $this->calendar->courseid;
$previousperiod = $this->get_previous_day_data();
$nextperiod = $this->get_next_day_data();
$date = $this->related['type']->timestamp_to_date_array($this->calendar->time);
$nextperiodlink = new moodle_url($this->url);
$nextperiodlink->param('time', $nextperiod[0]);
$previousperiodlink = new moodle_url($this->url);
$previousperiodlink->param('time', $previousperiod[0]);
$days = calendar_get_days();
$return['date'] = (new date_exporter($date))->export($output);
$return['periodname'] = userdate($this->calendar->time, get_string('strftimedaydate'));
$return['previousperiod'] = (new date_exporter($previousperiod))->export($output);
$return['previousperiodname'] = $days[$previousperiod['wday']]['fullname'];
$return['previousperiodlink'] = $previousperiodlink->out(false);
$return['nextperiod'] = (new date_exporter($nextperiod))->export($output);
$return['nextperiodname'] = $days[$nextperiod['wday']]['fullname'];
$return['nextperiodlink'] = $nextperiodlink->out(false);
$return['larrow'] = $output->larrow();
$return['rarrow'] = $output->rarrow();
// Need to account for user's timezone.
$usernow = usergetdate(time());
$today = new DateTimeImmutable(
timezone: core_date::get_user_timezone_object(),
);
// The start time should use the day's date but the current
// time of the day (adjusted for user's timezone).
$neweventtimestamp = $today->setTimestamp($date[0])->setTime(
$usernow['hours'],
$usernow['minutes'],
$usernow['seconds']
);
$return['neweventtimestamp'] = $neweventtimestamp->getTimestamp();
return $return;
}
/**
* Get the default context for use when adding a new event.
*
* @return null|\context
*/
protected function get_default_add_context() {
if (calendar_user_can_add_event($this->calendar->course)) {
return \context_course::instance($this->calendar->course->id);
}
return null;
}
/**
* Get the course filter selector.
*
* @param renderer_base $output
* @return string The html code for the course filter selector.
*/
protected function get_course_filter_selector(renderer_base $output) {
return $output->course_filter_selector($this->url, '', $this->calendar->course->id);
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'events' => '\core_calendar\local\event\entities\event_interface[]',
'cache' => '\core_calendar\external\events_related_objects_cache',
'type' => '\core_calendar\type_base',
];
}
/**
* Get the previous day timestamp.
*
* @return int The previous day timestamp.
*/
protected function get_previous_day_data() {
$type = $this->related['type'];
$time = $type->get_prev_day($this->calendar->time);
return $type->timestamp_to_date_array($time);
}
/**
* Get the next day timestamp.
*
* @return int The next day timestamp.
*/
protected function get_next_day_data() {
$type = $this->related['type'];
$time = $type->get_next_day($this->calendar->time);
return $type->timestamp_to_date_array($time);
}
}
+405
View File
@@ -0,0 +1,405 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains event class for displaying a calendar event.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
use \core_calendar\local\event\container;
use \renderer_base;
require_once($CFG->dirroot . '/course/lib.php');
/**
* Class for displaying a calendar event.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class calendar_event_exporter extends event_exporter_base {
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
$values = parent::define_other_properties();
$values['url'] = ['type' => PARAM_URL];
$values['islastday'] = [
'type' => PARAM_BOOL,
'default' => false,
];
$values['popupname'] = [
'type' => PARAM_RAW,
];
$values['mindaytimestamp'] = [
'type' => PARAM_INT,
'optional' => true
];
$values['mindayerror'] = [
'type' => PARAM_TEXT,
'optional' => true
];
$values['maxdaytimestamp'] = [
'type' => PARAM_INT,
'optional' => true
];
$values['maxdayerror'] = [
'type' => PARAM_TEXT,
'optional' => true
];
$values['draggable'] = [
'type' => PARAM_BOOL,
'default' => false
];
return $values;
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
global $CFG;
$values = parent::get_other_values($output);
$event = $this->event;
$course = $this->related['course'];
$hascourse = !empty($course);
// By default all events that can be edited are
// draggable.
$values['draggable'] = $values['canedit'];
if ($moduleproxy = $event->get_course_module()) {
$modulename = $moduleproxy->get('modname');
$moduleid = $moduleproxy->get('id');
$url = new \moodle_url(sprintf('/mod/%s/view.php', $modulename), ['id' => $moduleid]);
// Build edit event url for action events.
$params = array('update' => $moduleid, 'return' => true, 'sesskey' => sesskey());
$editurl = new \moodle_url('/course/mod.php', $params);
$values['editurl'] = $editurl->out(false);
} else if ($event->get_type() == 'category') {
$url = $event->get_category()->get_proxied_instance()->get_view_link();
} else {
$url = course_get_url($hascourse ? $course : SITEID);
}
$values['url'] = $url->out(false);
$values['islastday'] = false;
$today = $this->related['type']->timestamp_to_date_array($this->related['today']);
if ($hascourse) {
$values['popupname'] = \core_external\util::format_string(
$this->event->get_name(),
\context_course::instance($course->id),
true
);
} else {
$values['popupname'] = \core_external\util::format_string($this->event->get_name(), \context_system::instance(), true);
}
$times = $this->event->get_times();
if ($duration = $times->get_duration()) {
$enddate = $this->related['type']->timestamp_to_date_array($times->get_end_time()->getTimestamp());
$values['islastday'] = true;
$values['islastday'] = $values['islastday'] && $enddate['year'] == $today['year'];
$values['islastday'] = $values['islastday'] && $enddate['mon'] == $today['mon'];
$values['islastday'] = $values['islastday'] && $enddate['mday'] == $today['mday'];
}
$subscription = $this->event->get_subscription();
if ($subscription && !empty($subscription->get('id')) && $CFG->calendar_showicalsource) {
$a = (object) [
'name' => $values['popupname'],
'source' => $subscription->get('name'),
];
$values['popupname'] = get_string('namewithsource', 'calendar', $a);
} else {
if ($values['islastday']) {
$startdate = $this->related['type']->timestamp_to_date_array($times->get_start_time()->getTimestamp());
$samedate = true;
$samedate = $samedate && $startdate['mon'] == $enddate['mon'];
$samedate = $samedate && $startdate['year'] == $enddate['year'];
$samedate = $samedate && $startdate['mday'] == $enddate['mday'];
if (!$samedate) {
$values['popupname'] = get_string('eventendtimewrapped', 'calendar', $values['popupname']);
}
}
}
// Include category name into the event name, if applicable.
$proxy = $this->event->get_category();
if ($proxy && $proxy->get('id')) {
$category = $proxy->get_proxied_instance();
$eventnameparams = (object) [
'name' => $values['popupname'],
'category' => $category->get_formatted_name(),
];
$values['popupname'] = get_string('eventnameandcategory', 'calendar', $eventnameparams);
}
// Include course's shortname into the event name, if applicable.
if ($hascourse && $course->id !== SITEID) {
$eventnameparams = (object) [
'name' => $values['popupname'],
'course' => $values['course']->shortname,
];
$values['popupname'] = get_string('eventnameandcourse', 'calendar', $eventnameparams);
}
if ($event->get_course_module()) {
$values = array_merge($values, $this->get_module_timestamp_limits($event));
} else if ($hascourse && $course->id != SITEID && empty($event->get_group())) {
// This is a course event.
$values = array_merge($values, $this->get_course_timestamp_limits($event));
}
return $values;
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
$related = parent::define_related();
$related['daylink'] = \moodle_url::class;
$related['type'] = '\core_calendar\type_base';
$related['today'] = 'int';
$related['moduleinstance'] = 'stdClass?';
return $related;
}
/**
* Return the normalised event type.
* Activity events are normalised to be course events.
*
* @return string
*/
public function get_calendar_event_type() {
if ($this->event->get_course_module()) {
return 'course';
}
return $this->event->get_type();
}
/**
* Return the set of minimum and maximum date timestamp values
* for the given event.
*
* @param \core_calendar\local\event\entities\event_interface $event
* @return array
*/
protected function get_course_timestamp_limits($event) {
$values = [];
$mapper = container::get_event_mapper();
$starttime = $event->get_times()->get_start_time();
list($min, $max) = component_callback(
'core_course',
'core_calendar_get_valid_event_timestart_range',
[$mapper->from_event_to_legacy_event($event), $event->get_course()->get_proxied_instance()],
[false, false]
);
// The callback will return false for either of the
// min or max cutoffs to indicate that there are no
// valid timestart values. In which case the event is
// not draggable.
if ($min === false || $max === false) {
return ['draggable' => false];
}
if ($min) {
$values = array_merge($values, $this->get_timestamp_min_limit($starttime, $min));
}
if ($max) {
$values = array_merge($values, $this->get_timestamp_max_limit($starttime, $max));
}
return $values;
}
/**
* Return the set of minimum and maximum date timestamp values
* for the given event.
*
* @param \core_calendar\local\event\entities\event_interface $event
* @return array
*/
protected function get_module_timestamp_limits($event) {
$values = [];
$mapper = container::get_event_mapper();
$starttime = $event->get_times()->get_start_time();
$modname = $event->get_course_module()->get('modname');
$moduleinstance = $this->related['moduleinstance'];
list($min, $max) = component_callback(
'mod_' . $modname,
'core_calendar_get_valid_event_timestart_range',
[$mapper->from_event_to_legacy_event($event), $moduleinstance],
[false, false]
);
// The callback will return false for either of the
// min or max cutoffs to indicate that there are no
// valid timestart values. In which case the event is
// not draggable.
if ($min === false || $max === false) {
return ['draggable' => false];
}
if ($min) {
$values = array_merge($values, $this->get_timestamp_min_limit($starttime, $min));
}
if ($max) {
$values = array_merge($values, $this->get_timestamp_max_limit($starttime, $max));
}
return $values;
}
/**
* Get the correct minimum midnight day limit based on the event start time
* and the minimum timestamp limit of what the event belongs to.
*
* @param DateTimeInterface $starttime The event start time
* @param array $min The module's minimum limit for the event
* @return array Returns an array with mindaytimestamp and mindayerror keys.
*/
protected function get_timestamp_min_limit(\DateTimeInterface $starttime, $min) {
// We need to check that the minimum valid time is earlier in the
// day than the current event time so that if the user drags and drops
// the event to this day (which changes the date but not the time) it
// will result in a valid time start for the event.
//
// For example:
// An event that starts on 2017-01-10 08:00 with a minimum cutoff
// of 2017-01-05 09:00 means that 2017-01-05 is not a valid start day
// for the drag and drop because it would result in the event start time
// being set to 2017-01-05 08:00, which is invalid. Instead the minimum
// valid start day would be 2017-01-06.
$values = [];
$timestamp = $min[0];
$errorstring = $min[1];
$mindate = (new \DateTimeImmutable())->setTimestamp($timestamp);
$minstart = $mindate->setTime(
$starttime->format('H'),
$starttime->format('i'),
$starttime->format('s')
);
$midnight = usergetmidnight($timestamp);
if ($mindate <= $minstart) {
$values['mindaytimestamp'] = $midnight;
} else {
$tomorrow = (new \DateTime())->setTimestamp($midnight)->modify('+1 day');
$values['mindaytimestamp'] = $tomorrow->getTimestamp();
}
// Get the human readable error message to display if the min day
// timestamp is violated.
$values['mindayerror'] = $errorstring;
return $values;
}
/**
* Get the correct maximum midnight day limit based on the event start time
* and the maximum timestamp limit of what the event belongs to.
*
* @param DateTimeInterface $starttime The event start time
* @param array $max The module's maximum limit for the event
* @return array Returns an array with maxdaytimestamp and maxdayerror keys.
*/
protected function get_timestamp_max_limit(\DateTimeInterface $starttime, $max) {
// We're doing a similar calculation here as we are for the minimum
// day timestamp. See the explanation above.
$values = [];
$timestamp = $max[0];
$errorstring = $max[1];
$maxdate = (new \DateTimeImmutable())->setTimestamp($timestamp);
$maxstart = $maxdate->setTime(
$starttime->format('H'),
$starttime->format('i'),
$starttime->format('s')
);
$midnight = usergetmidnight($timestamp);
if ($maxdate >= $maxstart) {
$values['maxdaytimestamp'] = $midnight;
} else {
$yesterday = (new \DateTime())->setTimestamp($midnight)->modify('-1 day');
$values['maxdaytimestamp'] = $yesterday->getTimestamp();
}
// Get the human readable error message to display if the max day
// timestamp is violated.
$values['maxdayerror'] = $errorstring;
return $values;
}
/**
* Get the correct minimum midnight day limit based on the event start time
* and the module's minimum timestamp limit.
*
* @deprecated since Moodle 3.6. Please use get_timestamp_min_limit().
* @todo final deprecation. To be removed in Moodle 3.10
* @param \DateTimeInterface $starttime The event start time
* @param array $min The module's minimum limit for the event
* @return array Returns an array with mindaytimestamp and mindayerror keys.
*/
protected function get_module_timestamp_min_limit(\DateTimeInterface $starttime, $min) {
debugging('get_module_timestamp_min_limit() has been deprecated. Please call get_timestamp_min_limit() instead.',
DEBUG_DEVELOPER);
return $this->get_timestamp_min_limit($starttime, $min);
}
/**
* Get the correct maximum midnight day limit based on the event start time
* and the module's maximum timestamp limit.
*
* @deprecated since Moodle 3.6. Please use get_timestamp_max_limit().
* @todo final deprecation. To be removed in Moodle 3.10
* @param \DateTimeInterface $starttime The event start time
* @param array $max The module's maximum limit for the event
* @return array Returns an array with maxdaytimestamp and maxdayerror keys.
*/
protected function get_module_timestamp_max_limit(\DateTimeInterface $starttime, $max) {
debugging('get_module_timestamp_max_limit() has been deprecated. Please call get_timestamp_max_limit() instead.',
DEBUG_DEVELOPER);
return $this->get_timestamp_max_limit($starttime, $max);
}
}
+188
View File
@@ -0,0 +1,188 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains event class for displaying the upcoming view.
*
* @package core_calendar
* @copyright 2017 Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use renderer_base;
use moodle_url;
use \core_calendar\local\event\container;
/**
* Class for displaying the day view.
*
* @package core_calendar
* @copyright 2017 Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class calendar_upcoming_exporter extends exporter {
/**
* @var \calendar_information $calendar The calendar to be rendered.
*/
protected $calendar;
/**
* @var moodle_url $url The URL for the upcoming view page.
*/
protected $url;
/**
* Constructor for upcoming exporter.
*
* @param \calendar_information $calendar The calendar being represented.
* @param array $related The related information
*/
public function __construct(\calendar_information $calendar, $related) {
$this->calendar = $calendar;
parent::__construct([], $related);
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'events' => [
'type' => calendar_event_exporter::read_properties_definition(),
'multiple' => true,
],
'defaulteventcontext' => [
'type' => PARAM_INT,
'default' => 0,
],
'filter_selector' => [
'type' => PARAM_RAW,
],
'courseid' => [
'type' => PARAM_INT,
],
'categoryid' => [
'type' => PARAM_INT,
'optional' => true,
'default' => 0,
],
'isloggedin' => [
'type' => PARAM_BOOL,
],
'date' => [
'type' => date_exporter::read_properties_definition(),
],
];
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
$timestamp = $this->calendar->time;
$cache = $this->related['cache'];
$url = new moodle_url('/calendar/view.php', [
'view' => 'upcoming',
'time' => $timestamp,
'course' => $this->calendar->course->id,
]);
$this->url = $url;
$return['isloggedin'] = isloggedin();
$return['events'] = array_map(function($event) use ($cache, $output, $url) {
$context = $cache->get_context($event);
$course = $cache->get_course($event);
$moduleinstance = $cache->get_module_instance($event);
$exporter = new calendar_event_exporter($event, [
'context' => $context,
'course' => $course,
'moduleinstance' => $moduleinstance,
'daylink' => $url,
'type' => $this->related['type'],
'today' => $this->calendar->time,
]);
$data = $exporter->export($output);
// We need to override default formatted time because it differs from day view.
// Formatted time for upcoming view adds a link to the day view.
$legacyevent = container::get_event_mapper()->from_event_to_legacy_event($event);
$data->formattedtime = calendar_format_event_time($legacyevent, time(), null);
return $data;
}, $this->related['events']);
if ($context = $this->get_default_add_context()) {
$return['defaulteventcontext'] = $context->id;
}
$return['filter_selector'] = $this->get_course_filter_selector($output);
$return['courseid'] = $this->calendar->courseid;
$date = $this->related['type']->timestamp_to_date_array($this->calendar->time);
$return['date'] = (new date_exporter($date))->export($output);
if ($this->calendar->categoryid) {
$return['categoryid'] = $this->calendar->categoryid;
}
return $return;
}
/**
* Get the default context for use when adding a new event.
*
* @return null|\context
*/
protected function get_default_add_context() {
if (calendar_user_can_add_event($this->calendar->course)) {
return \context_course::instance($this->calendar->course->id);
}
return null;
}
/**
* Get the course filter selector.
*
* @param renderer_base $output
* @return string The html code for the course filter selector.
*/
protected function get_course_filter_selector(renderer_base $output) {
return $output->course_filter_selector($this->url, '', $this->calendar->course->id);
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'events' => '\core_calendar\local\event\entities\event_interface[]',
'cache' => '\core_calendar\external\events_related_objects_cache',
'type' => '\core_calendar\type_base',
];
}
}
+90
View File
@@ -0,0 +1,90 @@
<?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/>.
/**
* Class for normalising the date data.
*
* @package core_calendar
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
/**
* Class for normalising the date data.
*
* @package core_calendar
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class date_exporter extends exporter {
/**
* Constructor for date_exporter.
*
* @param array $data
* @param array $related The related information
*/
public function __construct($data, $related = []) {
$data['timestamp'] = $data[0];
unset($data[0]);
parent::__construct($data, $related);
}
protected static function define_properties() {
return [
'seconds' => [
'type' => PARAM_INT,
],
'minutes' => [
'type' => PARAM_INT,
],
'hours' => [
'type' => PARAM_INT,
],
'mday' => [
'type' => PARAM_INT,
],
'wday' => [
'type' => PARAM_INT,
],
'mon' => [
'type' => PARAM_INT,
],
'year' => [
'type' => PARAM_INT,
],
'yday' => [
'type' => PARAM_INT,
],
'weekday' => [
'type' => PARAM_RAW,
],
'month' => [
'type' => PARAM_RAW,
],
'timestamp' => [
'type' => PARAM_INT,
],
];
}
}
+277
View File
@@ -0,0 +1,277 @@
<?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/>.
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/calendar/lib.php');
use core\external\exporter;
use core_date;
use DateTimeImmutable;
use renderer_base;
use moodle_url;
/**
* Class for displaying the day view.
*
* @package core_calendar
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class day_exporter extends exporter {
/**
* @var \calendar_information $calendar The calendar being displayed.
*/
protected $calendar;
/**
* @var moodle_url
*/
protected $url;
/**
* Constructor.
*
* @param \calendar_information $calendar The calendar information for the period being displayed
* @param mixed $data Either an stdClass or an array of values.
* @param array $related Related objects.
*/
public function __construct(\calendar_information $calendar, $data, $related) {
$this->calendar = $calendar;
$url = new moodle_url('/calendar/view.php', [
'view' => 'day',
'time' => $calendar->time,
]);
if ($this->calendar->course && SITEID !== $this->calendar->course->id) {
$url->param('course', $this->calendar->course->id);
} else if ($this->calendar->categoryid) {
$url->param('category', $this->calendar->categoryid);
}
$this->url = $url;
parent::__construct($data, $related);
}
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
// These are the default properties as returned by getuserdate()
// but without the formatted month and week names.
return [
'seconds' => [
'type' => PARAM_INT,
],
'minutes' => [
'type' => PARAM_INT,
],
'hours' => [
'type' => PARAM_INT,
],
'mday' => [
'type' => PARAM_INT,
],
'wday' => [
'type' => PARAM_INT,
],
'year' => [
'type' => PARAM_INT,
],
'yday' => [
'type' => PARAM_INT,
],
];
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'timestamp' => [
'type' => PARAM_INT,
],
'neweventtimestamp' => [
'type' => PARAM_INT,
],
'viewdaylink' => [
'type' => PARAM_URL,
'optional' => true,
],
'viewdaylinktitle' => [
'type' => PARAM_RAW,
'optional' => true,
],
'events' => [
'type' => calendar_event_exporter::read_properties_definition(),
'multiple' => true,
],
'hasevents' => [
'type' => PARAM_BOOL,
'default' => false,
],
'calendareventtypes' => [
'type' => PARAM_RAW,
'multiple' => true,
],
'previousperiod' => [
'type' => PARAM_INT,
],
'nextperiod' => [
'type' => PARAM_INT,
],
'haslastdayofevent' => [
'type' => PARAM_BOOL,
'default' => false,
],
];
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
$daytimestamp = $this->calendar->time;
$timestamp = $this->data[0];
// Need to account for user's timezone.
$usernow = usergetdate(time());
$today = new DateTimeImmutable(
timezone: core_date::get_user_timezone_object(),
);
// The start time should use the day's date but the current
// time of the day (adjusted for user's timezone).
$neweventstarttime = $today->setTimestamp($timestamp)->setTime(
$usernow['hours'],
$usernow['minutes'],
$usernow['seconds']
);
$return = [
'timestamp' => $timestamp,
'neweventtimestamp' => $neweventstarttime->getTimestamp(),
'previousperiod' => $this->get_previous_day_timestamp($daytimestamp),
'nextperiod' => $this->get_next_day_timestamp($daytimestamp),
'viewdaylink' => $this->url->out(false),
];
if ($viewdaylinktitle = $this->get_view_link_title()) {
$return['viewdaylinktitle'] = $viewdaylinktitle;
}
$cache = $this->related['cache'];
$eventexporters = array_map(function($event) use ($cache, $output) {
$context = $cache->get_context($event);
$course = $cache->get_course($event);
$moduleinstance = $cache->get_module_instance($event);
$exporter = new calendar_event_exporter($event, [
'context' => $context,
'course' => $course,
'moduleinstance' => $moduleinstance,
'daylink' => $this->url,
'type' => $this->related['type'],
'today' => $this->data[0],
]);
return $exporter;
}, $this->related['events']);
$return['events'] = array_map(function($exporter) use ($output) {
return $exporter->export($output);
}, $eventexporters);
$return['hasevents'] = !empty($return['events']);
$return['calendareventtypes'] = array_map(function($exporter) {
return $exporter->get_calendar_event_type();
}, $eventexporters);
$return['calendareventtypes'] = array_values(array_unique($return['calendareventtypes']));
$return['haslastdayofevent'] = false;
foreach ($return['events'] as $event) {
if ($event->islastday) {
$return['haslastdayofevent'] = true;
break;
}
}
return $return;
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'events' => '\core_calendar\local\event\entities\event_interface[]',
'cache' => '\core_calendar\external\events_related_objects_cache',
'type' => '\core_calendar\type_base',
];
}
/**
* Get the previous day timestamp.
*
* @param int $daytimestamp The current day timestamp.
* @return int The previous day timestamp.
*/
protected function get_previous_day_timestamp($daytimestamp) {
return $this->related['type']->get_prev_day($daytimestamp);
}
/**
* Get the next day timestamp.
*
* @param int $daytimestamp The current day timestamp.
* @return int The next day timestamp.
*/
protected function get_next_day_timestamp($daytimestamp) {
return $this->related['type']->get_next_day($daytimestamp);
}
/**
* Get the title for view link.
*
* @return string
*/
protected function get_view_link_title() {
$title = null;
$userdate = userdate($this->data[0], get_string('strftimedayshort'));
if ($this->data['istoday']) {
$title = get_string('todayplustitle', 'calendar', $userdate);
} else if (count($this->related['events'])) {
$title = get_string('eventsfor', 'calendar', $userdate);
}
return $title;
}
}
+87
View File
@@ -0,0 +1,87 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains event class for displaying the day name.
*
* @package core_calendar
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
/**
* Class for displaying the day view.
*
* @package core_calendar
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class day_name_exporter extends exporter {
/**
* @var int $dayno The day number.
*/
protected $dayno;
/**
* @var string $shortname The formatted short name of the day.
*/
protected $shortname;
/**
* @var string $fullname The formatted full name of the day.
*/
protected $fullname;
/**
* Constructor.
*
* @param int $dayno The day number.
* @param array $names The list of names.
*/
public function __construct($dayno, $names) {
$data = $names + ['dayno' => $dayno];
parent::__construct($data, []);
}
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return [
'dayno' => [
'type' => PARAM_INT,
],
'shortname' => [
// Note: The calendar type class has already formatted the names.
'type' => PARAM_RAW,
],
'fullname' => [
// Note: The calendar type class has already formatted the names.
'type' => PARAM_RAW,
],
];
}
}
+129
View File
@@ -0,0 +1,129 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains event class for displaying a calendar event's action.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use core_calendar\local\event\entities\action_interface;
use core_calendar\local\event\container;
use renderer_base;
/**
* Class for displaying a calendar event's action.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class event_action_exporter extends exporter {
/**
* Constructor.
*
* @param action_interface $action The action object.
* @param array $related Related data.
*/
public function __construct(action_interface $action, $related = []) {
$data = new \stdClass();
$data->name = $action->get_name();
$data->url = $action->get_url()->out(false);
$data->itemcount = $action->get_item_count();
$data->actionable = $action->is_actionable();
parent::__construct($data, $related);
}
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return [
'name' => ['type' => PARAM_TEXT],
'url' => ['type' => PARAM_URL],
'itemcount' => ['type' => PARAM_INT],
'actionable' => ['type' => PARAM_BOOL]
];
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'showitemcount' => ['type' => PARAM_BOOL, 'default' => false]
];
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
$event = $this->related['event'];
if (!$event->get_component()) {
return ['showitemcount' => false];
}
$showitemcountcallback = 'core_calendar_event_action_shows_item_count';
$mapper = container::get_event_mapper();
$calevent = $mapper->from_event_to_legacy_event($event);
$params = [$calevent, $this->data->itemcount];
$showitemcount = component_callback($event->get_component(), $showitemcountcallback, $params, false);
// Prepare other values data.
$data = [
'showitemcount' => $showitemcount
];
return $data;
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'context' => 'context',
'event' => '\\core_calendar\\local\\event\\entities\\event_interface'
];
}
/**
* Magic method returning parameters for formatting 'name' property
*
* @return bool[]
*/
protected function get_format_parameters_for_name() {
return ['escape' => false];
}
}
+93
View File
@@ -0,0 +1,93 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains event class for displaying a calendar event.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . "/calendar/lib.php");
use \core_calendar\local\event\container;
use \renderer_base;
/**
* Class for displaying a calendar event.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class event_exporter extends event_exporter_base {
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
$values = parent::define_other_properties();
$values['url'] = ['type' => PARAM_URL];
return $values;
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
$values = parent::get_other_values($output);
global $CFG;
require_once($CFG->dirroot.'/course/lib.php');
$event = $this->event;
$context = $this->related['context'];
if ($moduleproxy = $event->get_course_module()) {
$modulename = $moduleproxy->get('modname');
$moduleid = $moduleproxy->get('id');
$url = new \moodle_url(sprintf('/mod/%s/view.php', $modulename), ['id' => $moduleid]);
// Build edit event url for action events.
$params = array('update' => $moduleid, 'return' => true, 'sesskey' => sesskey());
$editurl = new \moodle_url('/course/mod.php', $params);
$values['editurl'] = $editurl->out(false);
} else if ($event->get_type() == 'category') {
$url = $event->get_category()->get_proxied_instance()->get_view_link();
} else if ($event->get_type() == 'course') {
$url = \course_get_url($this->related['course'] ?: SITEID);
} else {
$url = \course_get_url($this->related['course'] ?: SITEID);
}
$values['url'] = $url->out(false);
// Override default formatted time to make sure the date portion of the time is always rendered.
$legacyevent = container::get_event_mapper()->from_event_to_legacy_event($event);
$values['formattedtime'] = calendar_format_event_time($legacyevent, time(), null, false);
return $values;
}
}
+415
View File
@@ -0,0 +1,415 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains event class for displaying a calendar event.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . "/calendar/lib.php");
require_once($CFG->libdir . "/filelib.php");
use \core\external\exporter;
use \core_calendar\local\event\container;
use \core_calendar\local\event\entities\event_interface;
use \core_calendar\local\event\entities\action_event_interface;
use \core_course\external\course_summary_exporter;
use \core\external\coursecat_summary_exporter;
use \renderer_base;
use moodle_url;
/**
* Class for displaying a calendar event.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class event_exporter_base extends exporter {
/**
* @var event_interface $event
*/
protected $event;
/**
* Constructor.
*
* @param event_interface $event
* @param array $related The related data.
*/
public function __construct(event_interface $event, $related = []) {
$this->event = $event;
$starttimestamp = $event->get_times()->get_start_time()->getTimestamp();
$endtimestamp = $event->get_times()->get_end_time()->getTimestamp();
$groupid = $event->get_group() ? $event->get_group()->get('id') : null;
$userid = $event->get_user() ? $event->get_user()->get('id') : null;
$categoryid = $event->get_category() ? $event->get_category()->get('id') : null;
$data = new \stdClass();
$data->id = $event->get_id();
$data->name = $event->get_name();
$data->description = file_rewrite_pluginfile_urls(
$event->get_description()->get_value(),
'pluginfile.php',
$related['context']->id,
'calendar',
'event_description',
$event->get_id()
);
$data->descriptionformat = $event->get_description()->get_format();
$data->location = \core_external\util::format_text($event->get_location(), FORMAT_PLAIN, $related['context']->id)[0];
$data->groupid = $groupid;
$data->userid = $userid;
$data->categoryid = $categoryid;
$data->eventtype = $event->get_type();
$data->timestart = $starttimestamp;
$data->timeduration = $endtimestamp - $starttimestamp;
$data->timesort = $event->get_times()->get_sort_time()->getTimestamp();
$data->timeusermidnight = $event->get_times()->get_usermidnight_time()->getTimestamp();
$data->visible = $event->is_visible() ? 1 : 0;
$data->timemodified = $event->get_times()->get_modified_time()->getTimestamp();
$data->component = $event->get_component();
$data->overdue = $data->timesort < time();
if ($repeats = $event->get_repeats()) {
$data->repeatid = $repeats->get_id();
$data->eventcount = $repeats->get_num() + 1;
}
if ($cm = $event->get_course_module()) {
$data->modulename = $cm->get('modname');
$data->instance = $cm->get('id');
$data->activityname = $cm->get('name');
$component = 'mod_' . $data->modulename;
if (!component_callback_exists($component, 'core_calendar_get_event_action_string')) {
$modulename = get_string('modulename', $data->modulename);
$data->activitystr = get_string('requiresaction', 'calendar', $modulename);
} else {
$data->activitystr = component_callback(
$component,
'core_calendar_get_event_action_string',
[$event->get_type()]
);
}
}
parent::__construct($data, $related);
}
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return [
'id' => ['type' => PARAM_INT],
'name' => ['type' => PARAM_TEXT],
'description' => [
'type' => PARAM_RAW,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'descriptionformat' => [
'type' => PARAM_INT,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'location' => [
'type' => PARAM_RAW,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'categoryid' => [
'type' => PARAM_INT,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'groupid' => [
'type' => PARAM_INT,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'userid' => [
'type' => PARAM_INT,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'repeatid' => [
'type' => PARAM_INT,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'eventcount' => [
'type' => PARAM_INT,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'component' => [
'type' => PARAM_COMPONENT,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'modulename' => [
'type' => PARAM_TEXT,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'activityname' => [
'type' => PARAM_TEXT,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'activitystr' => [
'type' => PARAM_TEXT,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'instance' => [
'type' => PARAM_INT,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'eventtype' => ['type' => PARAM_TEXT],
'timestart' => ['type' => PARAM_INT],
'timeduration' => ['type' => PARAM_INT],
'timesort' => ['type' => PARAM_INT],
'timeusermidnight' => ['type' => PARAM_INT],
'visible' => ['type' => PARAM_INT],
'timemodified' => ['type' => PARAM_INT],
'overdue' => [
'type' => PARAM_BOOL,
'optional' => true,
'default' => false,
'null' => NULL_ALLOWED
],
];
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'icon' => [
'type' => event_icon_exporter::read_properties_definition(),
],
'category' => [
'type' => coursecat_summary_exporter::read_properties_definition(),
'optional' => true,
],
'course' => [
'type' => course_summary_exporter::read_properties_definition(),
'optional' => true,
],
'subscription' => [
'type' => event_subscription_exporter::read_properties_definition(),
'optional' => true,
],
'canedit' => [
'type' => PARAM_BOOL
],
'candelete' => [
'type' => PARAM_BOOL
],
'deleteurl' => [
'type' => PARAM_URL
],
'editurl' => [
'type' => PARAM_URL
],
'viewurl' => [
'type' => PARAM_URL
],
'formattedtime' => [
'type' => PARAM_RAW,
],
'formattedlocation' => [
'type' => PARAM_RAW,
],
'isactionevent' => [
'type' => PARAM_BOOL
],
'iscourseevent' => [
'type' => PARAM_BOOL
],
'iscategoryevent' => [
'type' => PARAM_BOOL
],
'groupname' => [
'type' => PARAM_RAW,
'optional' => true,
'default' => null,
'null' => NULL_ALLOWED
],
'normalisedeventtype' => [
'type' => PARAM_TEXT
],
'normalisedeventtypetext' => [
'type' => PARAM_TEXT
],
'action' => [
'type' => event_action_exporter::read_properties_definition(),
'optional' => true,
],
'purpose' => [
'type' => PARAM_TEXT
],
'branded' => [
'type' => PARAM_BOOL,
'optional' => true,
],
];
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
$values = [];
$event = $this->event;
$legacyevent = container::get_event_mapper()->from_event_to_legacy_event($event);
$context = $this->related['context'];
$course = $this->related['course'];
$values['isactionevent'] = false;
$values['iscourseevent'] = false;
$values['iscategoryevent'] = false;
$values['normalisedeventtype'] = $event->get_type();
if ($moduleproxy = $event->get_course_module()) {
// We need a separate property to flag if an event is action event.
// That's required because canedit return true but action action events cannot be edited on the calendar UI.
// But they are considered editable because you can drag and drop the event on the month view.
$values['isactionevent'] = true;
// Activity events are normalised to "look" like course events.
$values['normalisedeventtype'] = 'course';
} else if ($event->get_type() == 'course') {
$values['iscourseevent'] = true;
} else if ($event->get_type() == 'category') {
$values['iscategoryevent'] = true;
}
$timesort = $event->get_times()->get_sort_time()->getTimestamp();
$iconexporter = new event_icon_exporter($event, ['context' => $context]);
$identifier = 'type' . $values['normalisedeventtype'];
$stringexists = get_string_manager()->string_exists($identifier, 'calendar');
if (!$stringexists) {
// Property normalisedeventtype is used to build the name of the CSS class for the events.
$values['normalisedeventtype'] = 'other';
}
$values['normalisedeventtypetext'] = $stringexists ? get_string($identifier, 'calendar') : '';
$purpose = 'none';
$isbranded = false;
if ($moduleproxy) {
$purpose = plugin_supports('mod', $moduleproxy->get('modname'), FEATURE_MOD_PURPOSE, 'none');
$isbranded = component_callback('mod_' . $moduleproxy->get('modname'), 'is_branded') !== null ? : false;
}
$values['purpose'] = $purpose;
$values['branded'] = $isbranded;
$values['icon'] = $iconexporter->export($output);
$subscriptionexporter = new event_subscription_exporter($event);
$values['subscription'] = $subscriptionexporter->export($output);
$proxy = $this->event->get_category();
if ($proxy && $proxy->get('id')) {
$category = $proxy->get_proxied_instance();
$categorysummaryexporter = new coursecat_summary_exporter($category, ['context' => $context]);
$values['category'] = $categorysummaryexporter->export($output);
}
if ($course && $course->id != SITEID) {
$coursesummaryexporter = new course_summary_exporter($course, ['context' => $context]);
$values['course'] = $coursesummaryexporter->export($output);
}
$courseid = (!$course) ? SITEID : $course->id;
$values['canedit'] = calendar_edit_event_allowed($legacyevent, true);
$values['candelete'] = calendar_delete_event_allowed($legacyevent);
$deleteurl = new moodle_url('/calendar/delete.php', ['id' => $event->get_id(), 'course' => $courseid]);
$values['deleteurl'] = $deleteurl->out(false);
$editurl = new moodle_url('/calendar/event.php', ['action' => 'edit', 'id' => $event->get_id(),
'course' => $courseid]);
$values['editurl'] = $editurl->out(false);
$viewurl = new moodle_url('/calendar/view.php', ['view' => 'day', 'course' => $courseid,
'time' => $timesort]);
$viewurl->set_anchor('event_' . $event->get_id());
$values['viewurl'] = $viewurl->out(false);
$values['formattedtime'] = calendar_format_event_time($legacyevent, time(), null, false,
$timesort);
$values['formattedlocation'] = calendar_format_event_location($legacyevent);
if ($group = $event->get_group()) {
$values['groupname'] = format_string($group->get('name'), true,
['context' => \context_course::instance($event->get_course()->get('id'))]);
}
if ($event instanceof action_event_interface) {
// Export event action if applicable.
$actionrelated = [
'context' => $this->related['context'],
'event' => $event
];
$actionexporter = new event_action_exporter($event->get_action(), $actionrelated);
$values['action'] = $actionexporter->export($output);
}
return $values;
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'context' => 'context',
'course' => 'stdClass?',
];
}
}
+157
View File
@@ -0,0 +1,157 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains event class for displaying a calendar event's icon.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
use \core\external\exporter;
use \core_calendar\local\event\entities\event_interface;
/**
* Class for displaying a calendar event's icon.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class event_icon_exporter extends exporter {
/**
* Constructor.
*
* @param event_interface $event
* @param array $related The related data.
*/
public function __construct(event_interface $event, $related = []) {
global $PAGE;
$coursemodule = $event->get_course_module();
$category = $event->get_category();
$categoryid = $category ? $category->get('id') : null;
$course = $event->get_course();
$courseid = $course ? $course->get('id') : null;
$group = $event->get_group();
$groupid = $group ? $group->get('id') : null;
$user = $event->get_user();
$userid = $user ? $user->get('id') : null;
$isactivityevent = !empty($coursemodule);
$issiteevent = ($course && $courseid == SITEID);
$iscategoryevent = ($category && !empty($categoryid));
$iscourseevent = ($course && !empty($courseid) && $courseid != SITEID && empty($groupid));
$isgroupevent = ($group && !empty($groupid));
$isuserevent = ($user && !empty($userid));
$iconurl = '';
$iconclass = '';
if ($isactivityevent) {
$key = 'monologo';
$component = $coursemodule->get('modname');
$iconurl = get_fast_modinfo($courseid)->get_cm($coursemodule->get('id'))->get_icon_url();
$iconclass = $iconurl->get_param('filtericon') ? '' : 'nofilter';
$iconurl = $iconurl->out(false);
if (get_string_manager()->string_exists($event->get_type(), $component)) {
$alttext = get_string($event->get_type(), $component);
} else {
$alttext = get_string('activityevent', 'calendar');
}
} else if ($event->get_component()) {
// Guess the icon and the title for the component event. By default display calendar icon and the
// plugin name as the alttext.
if ($PAGE->theme->resolve_image_location($event->get_type(), $event->get_component())) {
$key = $event->get_type();
$component = $event->get_component();
} else {
$key = 'i/otherevent';
$component = 'core';
}
if (get_string_manager()->string_exists($event->get_type(), $event->get_component())) {
$alttext = get_string($event->get_type(), $event->get_component());
} else {
$alttext = get_string('pluginname', $event->get_component());
}
} else if ($issiteevent) {
$key = 'i/siteevent';
$component = 'core';
$alttext = get_string('typesite', 'calendar');
} else if ($iscategoryevent) {
$key = 'i/categoryevent';
$component = 'core';
$alttext = get_string('typecategory', 'calendar');
} else if ($iscourseevent) {
$key = 'i/courseevent';
$component = 'core';
$alttext = get_string('typecourse', 'calendar');
} else if ($isgroupevent) {
$key = 'i/groupevent';
$component = 'core';
$alttext = get_string('typegroup', 'calendar');
} else if ($isuserevent) {
$key = 'i/userevent';
$component = 'core';
$alttext = get_string('typeuser', 'calendar');
} else {
// Default to site event icon?
$key = 'i/siteevent';
$component = 'core';
$alttext = get_string('typesite', 'calendar');
}
$data = new \stdClass();
$data->key = $key;
$data->component = $component;
$data->alttext = $alttext;
$data->iconurl = $iconurl;
$data->iconclass = $iconclass;
parent::__construct($data, $related);
}
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return [
'key' => ['type' => PARAM_TEXT],
'component' => ['type' => PARAM_TEXT],
'alttext' => ['type' => PARAM_TEXT],
'iconurl' => ['type' => PARAM_TEXT],
'iconclass' => ['type' => PARAM_TEXT],
];
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'context' => 'context',
];
}
}
@@ -0,0 +1,85 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains event class for displaying a calendar event's subscription.
*
* @package core_calendar
* @copyright 2017 Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
use \core\external\exporter;
use \core_calendar\local\event\entities\event_interface;
/**
* Class for displaying a calendar event's subscription.
*
* @package core_calendar
* @copyright 2017 Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class event_subscription_exporter extends exporter {
/**
* Constructor.
*
* @param event_interface $event
*/
public function __construct(event_interface $event) {
global $CFG;
$data = new \stdClass();
$data->displayeventsource = false;
if ($event->get_subscription()) {
$subscription = calendar_get_subscription($event->get_subscription()->get('id'));
if (!empty($subscription) && $CFG->calendar_showicalsource) {
$data->displayeventsource = true;
if (!empty($subscription->url)) {
$data->subscriptionurl = $subscription->url;
}
$data->subscriptionname = $subscription->name;
}
}
parent::__construct($data);
}
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return [
'displayeventsource' => [
'type' => PARAM_BOOL
],
'subscriptionname' => [
'type' => PARAM_RAW,
'optional' => true
],
'subscriptionurl' => [
'type' => PARAM_URL,
'optional' => true
],
];
}
}
+121
View File
@@ -0,0 +1,121 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains event class for displaying a list of calendar events.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
use \core\external\exporter;
use \renderer_base;
/**
* Class for displaying a list of calendar events.
*
* This class uses the events relateds cache in order to get the related
* data for exporting an event without having to naively hit the database
* for each event.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class events_exporter extends exporter {
/**
* @var array $events An array of event_interface objects.
*/
protected $events;
/**
* Constructor.
*
* @param array $events An array of event_interface objects
* @param array $related An array of related objects
*/
public function __construct(array $events, $related = []) {
$this->events = $events;
parent::__construct([], $related);
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'events' => [
'type' => event_exporter::read_properties_definition(),
'multiple' => true,
],
'firstid' => [
'type' => PARAM_INT,
'null' => NULL_ALLOWED,
'default' => null,
],
'lastid' => [
'type' => PARAM_INT,
'null' => NULL_ALLOWED,
'default' => null,
],
];
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
$return = [];
$cache = $this->related['cache'];
$return['events'] = array_map(function($event) use ($cache, $output) {
$context = $cache->get_context($event);
$course = $cache->get_course($event);
$exporter = new event_exporter($event, ['context' => $context, 'course' => $course]);
return $exporter->export($output);
}, $this->events);
if ($count = count($return['events'])) {
$return['firstid'] = $return['events'][0]->id;
$return['lastid'] = $return['events'][$count - 1]->id;
}
return $return;
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'cache' => 'core_calendar\external\events_related_objects_cache',
];
}
}
@@ -0,0 +1,106 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains event class for displaying a list of calendar events grouped by course id.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
use \core\external\exporter;
use \renderer_base;
/**
* Class for displaying a list of calendar events grouped by course id.
*
* This class uses the events relateds cache in order to get the related
* data for exporting an event without having to naively hit the database
* for each event.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class events_grouped_by_course_exporter extends exporter {
/**
* @var array $events An array of event_interface objects
* grouped and index by course id.
*/
protected $eventsbycourse;
/**
* Constructor.
*
* @param array $eventsbycourse An array of event_interface objects
* @param array $related An array of related objects
*/
public function __construct(array $eventsbycourse, $related = []) {
$this->eventsbycourse = $eventsbycourse;
parent::__construct([], $related);
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'groupedbycourse' => [
'type' => events_same_course_exporter::read_properties_definition(),
'multiple' => true,
'default' => [],
],
];
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
$return = [];
$cache = $this->related['cache'];
foreach ($this->eventsbycourse as $courseid => $events) {
$eventsexporter = new events_same_course_exporter(
$courseid, $events, ['cache' => $cache]);
$return['groupedbycourse'][] = $eventsexporter->export($output);
}
return $return;
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'cache' => 'core_calendar\external\events_related_objects_cache',
];
}
}
@@ -0,0 +1,288 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains event class for providing the related objects when exporting a list of calendar events.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
use \core_calendar\local\event\entities\event_interface;
/**
* Class to providing the related objects when exporting a list of calendar events.
*
* This class is only meant for use with exporters. It attempts to bulk load
* the related objects for a list of events and cache them to avoid having
* to query the database when exporting each individual event.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class events_related_objects_cache {
/**
* @var array $events The events for which we need related objects.
*/
protected $events;
/**
* @var array $courses The related courses.
*/
protected $courses = null;
/**
* @var array $groups The related groups.
*/
protected $groups = null;
/**
* @var array $coursemodules The related course modules.
*/
protected $coursemodules = [];
/**
* @var array $moduleinstances The related module instances.
*/
protected $moduleinstances = null;
/**
* Constructor.
*
* @param array $events Array of event_interface events
* @param array $courses Array of courses to populate the cache with
*/
public function __construct(array $events, array $courses = null) {
$this->events = $events;
if (!is_null($courses)) {
$this->courses = [];
foreach ($courses as $course) {
$this->courses[$course->id] = $course;
}
}
}
/**
* Get the related course object for a given event.
*
* @param event_interface $event The event object.
* @return \stdClass|null
*/
public function get_course(event_interface $event) {
if (is_null($this->courses)) {
$this->load_courses();
}
if ($course = $event->get_course()) {
$courseid = $course->get('id');
return isset($this->courses[$courseid]) ? $this->courses[$courseid] : null;
} else {
return null;
}
}
/**
* Get the related context for a given event.
*
* @param event_interface $event The event object.
* @return \context|null
*/
public function get_context(event_interface $event) {
global $USER;
$categoryid = $event->get_category() ? $event->get_category()->get('id') : null;
$courseid = $event->get_course() ? $event->get_course()->get('id') : null;
$groupid = $event->get_group() ? $event->get_group()->get('id') : null;
$userid = $event->get_user() ? $event->get_user()->get('id') : null;
$moduleid = $event->get_course_module() ? $event->get_course_module()->get('id') : null;
if (!empty($categoryid)) {
return \context_coursecat::instance($categoryid);
} else if (!empty($courseid)) {
return \context_course::instance($event->get_course()->get('id'));
} else if (!empty($groupid)) {
$group = $this->get_group($event);
return \context_course::instance($group->courseid);
} else if (!empty($userid) && $userid == $USER->id) {
return \context_user::instance($userid);
} else if (!empty($userid) && $userid != $USER->id && $moduleid && $moduleid > 0) {
$cm = $this->get_course_module($event);
return \context_course::instance($cm->course);
} else {
return \context_user::instance($userid);
}
}
/**
* Get the related group object for a given event.
*
* @param event_interface $event The event object.
* @return \stdClass|null
*/
public function get_group(event_interface $event) {
if (is_null($this->groups)) {
$this->load_groups();
}
if ($group = $event->get_group()) {
$groupid = $group->get('id');
return isset($this->groups[$groupid]) ? $this->groups[$groupid] : null;
} else {
return null;
}
}
/**
* Get the related course module for a given event.
*
* @param event_interface $event The event object.
* @return \stdClass|null
*/
public function get_course_module(event_interface $event) {
if (!$event->get_course_module()) {
return null;
}
$id = $event->get_course_module()->get('id');
$name = $event->get_course_module()->get('modname');
$key = $name . '_' . $id;
if (!isset($this->coursemodules[$key])) {
$this->coursemodules[$key] = get_coursemodule_from_instance($name, $id, 0, false, MUST_EXIST);
}
return $this->coursemodules[$key];
}
/**
* Get the related module instance for a given event.
*
* @param event_interface $event The event object.
* @return \stdClass|null
*/
public function get_module_instance(event_interface $event) {
if (!$event->get_course_module()) {
return null;
}
if (is_null($this->moduleinstances)) {
$this->load_module_instances();
}
$id = $event->get_course_module()->get('instance');
$name = $event->get_course_module()->get('modname');
if (isset($this->moduleinstances[$name])) {
if (isset($this->moduleinstances[$name][$id])) {
return $this->moduleinstances[$name][$id];
}
}
return null;
}
/**
* Load the list of all of the distinct courses required for the
* list of provided events and save the result in memory.
*/
protected function load_courses() {
global $DB;
$courseids = [];
foreach ($this->events as $event) {
if ($course = $event->get_course()) {
$id = $course->get('id');
$courseids[$id] = true;
}
}
if (empty($courseids)) {
$this->courses = [];
return;
}
list($idsql, $params) = $DB->get_in_or_equal(array_keys($courseids));
$sql = "SELECT * FROM {course} WHERE id {$idsql}";
$this->courses = $DB->get_records_sql($sql, $params);
}
/**
* Load the list of all of the distinct groups required for the
* list of provided events and save the result in memory.
*/
protected function load_groups() {
global $DB;
$groupids = [];
foreach ($this->events as $event) {
if ($group = $event->get_group()) {
$id = $group->get('id');
$groupids[$id] = true;
}
}
if (empty($groupids)) {
$this->groups = [];
return;
}
list($idsql, $params) = $DB->get_in_or_equal(array_keys($groupids));
$sql = "SELECT * FROM {groups} WHERE id {$idsql}";
$this->groups = $DB->get_records_sql($sql, $params);
}
/**
* Load the list of all of the distinct module instances required for the
* list of provided events and save the result in memory.
*/
protected function load_module_instances() {
global $DB;
$this->moduleinstances = [];
$modulestoload = [];
foreach ($this->events as $event) {
if ($module = $event->get_course_module()) {
$id = $module->get('instance');
$name = $module->get('modname');
$ids = isset($modulestoload[$name]) ? $modulestoload[$name] : [];
$ids[$id] = true;
$modulestoload[$name] = $ids;
}
}
if (empty($modulestoload)) {
return;
}
foreach ($modulestoload as $modulename => $ids) {
list($idsql, $params) = $DB->get_in_or_equal(array_keys($ids));
$sql = "SELECT * FROM {" . $modulename . "} WHERE id {$idsql}";
$this->moduleinstances[$modulename] = $DB->get_records_sql($sql, $params);
}
}
}
@@ -0,0 +1,83 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains event class for displaying a list of calendar events for a single course.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
use \renderer_base;
/**
* Class for displaying a list of calendar events for a single course.
*
* This class uses the events relateds cache in order to get the related
* data for exporting an event without having to naively hit the database
* for each event.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class events_same_course_exporter extends events_exporter {
/**
* @var array $courseid The id of the course for these events.
*/
protected $courseid;
/**
* Constructor.
*
* @param int $courseid The course id for these events
* @param array $events An array of event_interface objects
* @param array $related An array of related objects
*/
public function __construct($courseid, array $events, $related = []) {
parent::__construct($events, $related);
$this->courseid = $courseid;
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
$properties = parent::define_other_properties();
$properties['courseid'] = ['type' => PARAM_INT];
return $properties;
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
$values = parent::get_other_values($output);
$values['courseid'] = $this->courseid;
return $values;
}
}
+97
View File
@@ -0,0 +1,97 @@
<?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 is the external method for exporting a calendar token.
*
* @package core_calendar
* @since Moodle 3.10
* @copyright 2020 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external\export;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/calendar/lib.php');
use context_system;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use moodle_exception;
/**
* This is the external method for exporting a calendar token.
*
* @copyright 2020 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class token extends external_api {
/**
* Returns description of method parameters.
*
* @return external_function_parameters.
* @since Moodle 3.10
*/
public static function execute_parameters() {
return new external_function_parameters([]);
}
/**
* Return the auth token required for exporting a calendar.
*
* @return array The access information
* @throws moodle_exception
* @since Moodle 3.10
*/
public static function execute() {
global $CFG, $USER;
$context = context_system::instance();
self::validate_context($context);
if (empty($CFG->enablecalendarexport)) {
throw new moodle_exception('Calendar export is disabled in this site.');
}
return [
'token' => calendar_get_export_token($USER),
'warnings' => [],
];
}
/**
* Returns description of method result value.
*
* @return \core_external\external_description.
* @since Moodle 3.10
*/
public static function execute_returns() {
return new external_single_structure(
[
'token' => new external_value(PARAM_RAW, 'The calendar permanent access token for calendar export.'),
'warnings' => new external_warnings(),
]
);
}
}
+153
View File
@@ -0,0 +1,153 @@
<?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/>.
namespace core_calendar\external;
use core\external\exporter;
use renderer_base;
use stdClass;
use moodle_url;
/**
* Class for exporting calendar footer view options data.
*
* @package core_calendar
* @copyright 2017 Simey Lameze
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class footer_options_exporter extends exporter {
/**
* @var \calendar_information $calendar The calendar to be rendered.
*/
protected $calendar;
/**
* @var int $userid The user id.
*/
protected $userid;
/**
* @var string $token The user sha1 token.
*/
protected $token;
/**
* @var bool $showfullcalendarlink Whether the full calendar link should be displayed or not.
*/
protected $showfullcalendarlink;
/**
* Constructor for month_exporter.
*
* @param \calendar_information $calendar The calendar being represented
* @param int $userid The user id
* @param string $token The user sha1 token.
* @param array $options Display options for the footer. If an option is not set, a default value will be provided.
* It consists of:
* - showfullcalendarlink - bool - Whether to show the full calendar link or not. Defaults to false.
*/
public function __construct(\calendar_information $calendar, $userid, $token, array $options = []) {
$this->calendar = $calendar;
$this->userid = $userid;
$this->token = $token;
$this->showfullcalendarlink = $options['showfullcalendarlink'] ?? false;
}
/**
* Get manage subscription link.
*
* @return string|null The manage subscription hyperlink.
*/
protected function get_manage_subscriptions_link(): ?string {
if (calendar_user_can_add_event($this->calendar->course)) {
$managesubscriptionurl = new moodle_url('/calendar/managesubscriptions.php');
return $managesubscriptionurl->out(true);
}
return null;
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
global $CFG;
$values = new stdClass();
$values->footerlinks = [];
if ($this->showfullcalendarlink) {
if ($this->calendar->courseid !== SITEID) {
$linkname = get_string('coursecalendarlink', 'calendar');
} else {
$linkname = get_string('fullcalendar', 'calendar');
}
$values->footerlinks[] = (object)[
'url' => $this->get_calendar_url(),
'linkname' => $linkname,
];
}
if (!empty($CFG->enablecalendarexport) && $managesubscriptionlink = $this->get_manage_subscriptions_link()) {
$values->footerlinks[] = (object)[
'url' => $managesubscriptionlink,
'linkname' => get_string('managesubscriptions', 'calendar'),
];
}
return (array) $values;
}
/**
* Return the list of additional properties.
*
* @return array
*/
public static function define_other_properties() {
return [
'footerlinks' => [
'type' => [
'url' => [
'type' => PARAM_URL,
],
'linkname' => [
'type' => PARAM_TEXT,
],
],
'multiple' => true,
'optional' => true,
],
];
}
/**
* Build the calendar URL.
*
* @return string The calendar URL.
*/
public function get_calendar_url() {
$url = new moodle_url('/calendar/view.php', [
'view' => 'month',
'time' => $this->calendar->time,
'course' => $this->calendar->courseid,
]);
return $url->out(false);
}
}
+471
View File
@@ -0,0 +1,471 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains event class for displaying the month view.
*
* @package core_calendar
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use renderer_base;
use moodle_url;
/**
* Class for displaying the month view.
*
* @package core_calendar
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class month_exporter extends exporter {
/** @var int Number of calendar instances displayed. */
protected static $calendarinstances = 0;
/** @var int This calendar instance's ID. */
protected $calendarinstanceid = 0;
/**
* @var \calendar_information $calendar The calendar to be rendered.
*/
protected $calendar;
/**
* @var int $firstdayofweek The first day of the week.
*/
protected $firstdayofweek;
/**
* @var moodle_url $url The URL for the events page.
*/
protected $url;
/**
* @var bool $includenavigation Whether navigation should be included on the output.
*/
protected $includenavigation = true;
/**
* @var bool $initialeventsloaded Whether the events have been loaded for this month.
*/
protected $initialeventsloaded = true;
/**
* @var bool $showcoursefilter Whether to render the course filter selector as well.
*/
protected $showcoursefilter = false;
/**
* Constructor for month_exporter.
*
* @param \calendar_information $calendar The calendar being represented
* @param \core_calendar\type_base $type The calendar type (e.g. Gregorian)
* @param array $related The related information
*/
public function __construct(\calendar_information $calendar, \core_calendar\type_base $type, $related) {
// Increment the calendar instances count on initialisation.
self::$calendarinstances++;
// Assign this instance an ID based on the latest calendar instances count.
$this->calendarinstanceid = self::$calendarinstances;
$this->calendar = $calendar;
$this->firstdayofweek = $type->get_starting_weekday();
$this->url = new moodle_url('/calendar/view.php', [
'view' => 'month',
'time' => $calendar->time,
]);
if ($this->calendar->course && SITEID !== $this->calendar->course->id) {
$this->url->param('course', $this->calendar->course->id);
} else if ($this->calendar->categoryid) {
$this->url->param('category', $this->calendar->categoryid);
}
$related['type'] = $type;
$data = [
'url' => $this->url->out(false),
];
parent::__construct($data, $related);
}
protected static function define_properties() {
return [
'url' => [
'type' => PARAM_URL,
],
];
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'courseid' => [
'type' => PARAM_INT,
],
'categoryid' => [
'type' => PARAM_INT,
'optional' => true,
'default' => 0,
],
'filter_selector' => [
'type' => PARAM_RAW,
'optional' => true,
],
'weeks' => [
'type' => week_exporter::read_properties_definition(),
'multiple' => true,
],
'daynames' => [
'type' => day_name_exporter::read_properties_definition(),
'multiple' => true,
],
'view' => [
'type' => PARAM_ALPHA,
],
'date' => [
'type' => date_exporter::read_properties_definition(),
],
'periodname' => [
// Note: We must use RAW here because the calendar type returns the formatted month name based on a
// calendar format.
'type' => PARAM_RAW,
],
'includenavigation' => [
'type' => PARAM_BOOL,
'default' => true,
],
// Tracks whether the first set of events have been loaded and provided
// to the exporter.
'initialeventsloaded' => [
'type' => PARAM_BOOL,
'default' => true,
],
'previousperiod' => [
'type' => date_exporter::read_properties_definition(),
],
'previousperiodlink' => [
'type' => PARAM_URL,
],
'previousperiodname' => [
// Note: We must use RAW here because the calendar type returns the formatted month name based on a
// calendar format.
'type' => PARAM_RAW,
],
'nextperiod' => [
'type' => date_exporter::read_properties_definition(),
],
'nextperiodname' => [
// Note: We must use RAW here because the calendar type returns the formatted month name based on a
// calendar format.
'type' => PARAM_RAW,
],
'nextperiodlink' => [
'type' => PARAM_URL,
],
'larrow' => [
// The left arrow defined by the theme.
'type' => PARAM_RAW,
],
'rarrow' => [
// The right arrow defined by the theme.
'type' => PARAM_RAW,
],
'defaulteventcontext' => [
'type' => PARAM_INT,
'default' => 0,
],
'calendarinstanceid' => [
'type' => PARAM_INT,
'default' => 0,
],
'viewingmonth' => [
'type' => PARAM_BOOL,
'default' => true,
],
'showviewselector' => [
'type' => PARAM_BOOL,
'default' => true,
],
'viewinginblock' => [
'type' => PARAM_BOOL,
'default' => false,
],
];
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
$previousperiod = $this->get_previous_month_data();
$nextperiod = $this->get_next_month_data();
$date = $this->related['type']->timestamp_to_date_array($this->calendar->time);
$nextperiodlink = new moodle_url($this->url);
$nextperiodlink->param('time', $nextperiod[0]);
$previousperiodlink = new moodle_url($this->url);
$previousperiodlink->param('time', $previousperiod[0]);
$viewmode = $this->calendar->get_viewmode() ?? 'month';
$return = [
'courseid' => $this->calendar->courseid,
'weeks' => $this->get_weeks($output),
'daynames' => $this->get_day_names($output),
'view' => $viewmode,
'date' => (new date_exporter($date))->export($output),
'periodname' => userdate($this->calendar->time, get_string('strftimemonthyear')),
'previousperiod' => (new date_exporter($previousperiod))->export($output),
'previousperiodname' => userdate($previousperiod[0], get_string('strftimemonth')),
'previousperiodlink' => $previousperiodlink->out(false),
'nextperiod' => (new date_exporter($nextperiod))->export($output),
'nextperiodname' => userdate($nextperiod[0], get_string('strftimemonth')),
'nextperiodlink' => $nextperiodlink->out(false),
'larrow' => $output->larrow(),
'rarrow' => $output->rarrow(),
'includenavigation' => $this->includenavigation,
'initialeventsloaded' => $this->initialeventsloaded,
'calendarinstanceid' => $this->calendarinstanceid,
'showviewselector' => $viewmode === 'month',
'viewinginblock' => $viewmode === 'monthblock',
];
if ($this->showcoursefilter) {
$return['filter_selector'] = $this->get_course_filter_selector($output);
}
if ($context = $this->get_default_add_context()) {
$return['defaulteventcontext'] = $context->id;
}
if ($this->calendar->categoryid) {
$return['categoryid'] = $this->calendar->categoryid;
}
return $return;
}
/**
* Get the course filter selector.
*
* @param renderer_base $output
* @return string The html code for the course filter selector.
*/
protected function get_course_filter_selector(renderer_base $output) {
$content = '';
$content .= $output->course_filter_selector($this->url, '', $this->calendar->course->id, $this->calendarinstanceid);
return $content;
}
/**
* Get the list of day names for display, re-ordered from the first day
* of the week.
*
* @param renderer_base $output
* @return day_name_exporter[]
*/
protected function get_day_names(renderer_base $output) {
$weekdays = $this->related['type']->get_weekdays();
$daysinweek = count($weekdays);
$daynames = [];
for ($i = 0; $i < $daysinweek; $i++) {
// Bump the currentdayno and ensure it loops.
$dayno = ($i + $this->firstdayofweek + $daysinweek) % $daysinweek;
$dayname = new day_name_exporter($dayno, $weekdays[$dayno]);
$daynames[] = $dayname->export($output);
}
return $daynames;
}
/**
* Get the list of week days, ordered into weeks and padded according
* to the value of the first day of the week.
*
* @param renderer_base $output
* @return array The list of weeks.
*/
protected function get_weeks(renderer_base $output) {
$weeks = [];
$alldays = $this->get_days();
$daysinweek = count($this->related['type']->get_weekdays());
// Calculate which day number is the first, and last day of the week.
$firstdayofweek = $this->firstdayofweek;
// The first week is special as it may have padding at the beginning.
$day = reset($alldays);
$firstdayno = $day['wday'];
$prepadding = ($firstdayno + $daysinweek - $firstdayofweek) % $daysinweek;
$daysinfirstweek = $daysinweek - $prepadding;
$days = array_slice($alldays, 0, $daysinfirstweek);
$week = new week_exporter($this->calendar, $days, $prepadding, ($daysinweek - count($days) - $prepadding), $this->related);
$weeks[] = $week->export($output);
// Now chunk up the remaining day. and turn them into weeks.
$daychunks = array_chunk(array_slice($alldays, $daysinfirstweek), $daysinweek);
foreach ($daychunks as $days) {
$week = new week_exporter($this->calendar, $days, 0, ($daysinweek - count($days)), $this->related);
$weeks[] = $week->export($output);
}
return $weeks;
}
/**
* Get the list of days with the matching date array.
*
* @return array
*/
protected function get_days() {
$date = $this->related['type']->timestamp_to_date_array($this->calendar->time);
$monthdays = $this->related['type']->get_num_days_in_month($date['year'], $date['mon']);
$days = [];
for ($dayno = 1; $dayno <= $monthdays; $dayno++) {
// Get the gregorian representation of the day.
$timestamp = $this->related['type']->convert_to_timestamp($date['year'], $date['mon'], $dayno);
$days[] = $this->related['type']->timestamp_to_date_array($timestamp);
}
return $days;
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'events' => '\core_calendar\local\event\entities\event_interface[]',
'cache' => '\core_calendar\external\events_related_objects_cache',
'type' => '\core_calendar\type_base',
];
}
/**
* Get the current month timestamp.
*
* @return int The month timestamp.
*/
protected function get_month_data() {
$date = $this->related['type']->timestamp_to_date_array($this->calendar->time);
$monthtime = $this->related['type']->convert_to_gregorian($date['year'], $date['month'], 1);
return make_timestamp($monthtime['year'], $monthtime['month']);
}
/**
* Get the previous month timestamp.
*
* @return int The previous month timestamp.
*/
protected function get_previous_month_data() {
$type = $this->related['type'];
$date = $type->timestamp_to_date_array($this->calendar->time);
list($date['mon'], $date['year']) = $type->get_prev_month($date['year'], $date['mon']);
$time = $type->convert_to_timestamp($date['year'], $date['mon'], 1);
return $type->timestamp_to_date_array($time);
}
/**
* Get the next month timestamp.
*
* @return int The next month timestamp.
*/
protected function get_next_month_data() {
$type = $this->related['type'];
$date = $type->timestamp_to_date_array($this->calendar->time);
list($date['mon'], $date['year']) = $type->get_next_month($date['year'], $date['mon']);
$time = $type->convert_to_timestamp($date['year'], $date['mon'], 1);
return $type->timestamp_to_date_array($time);
}
/**
* Set whether the navigation should be shown.
*
* @param bool $include
* @return $this
*/
public function set_includenavigation($include) {
$this->includenavigation = $include;
return $this;
}
/**
* Set whether the initial events have already been loaded and
* provided to the exporter.
*
* @param bool $loaded
* @return $this
*/
public function set_initialeventsloaded(bool $loaded) {
$this->initialeventsloaded = $loaded;
return $this;
}
/**
* Set whether the course filter selector should be shown.
*
* @param bool $show
* @return $this
*/
public function set_showcoursefilter(bool $show) {
$this->showcoursefilter = $show;
return $this;
}
/**
* Get the default context for use when adding a new event.
*
* @return null|\context
*/
protected function get_default_add_context() {
if (calendar_user_can_add_event($this->calendar->course)) {
return \context_course::instance($this->calendar->course->id);
}
return null;
}
}
+104
View File
@@ -0,0 +1,104 @@
<?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/>.
/**
* Calendar external API for deleting the subscription.
*
* @package core_calendar
* @category external
* @copyright 2021 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external\subscription;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/calendar/lib.php');
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
/**
* Calendar external API for deleting the subscription.
*
* @package core_calendar
* @category external
* @copyright 2021 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class delete extends external_api {
/**
* Describes the parameters for deleting the subscription.
*
* @return external_function_parameters
* @since Moodle 4.0
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'subscriptionid' => new external_value(PARAM_INT, 'The id of the subscription', VALUE_REQUIRED)
]);
}
/**
* External function to delete the calendar subscription.
*
* @param int $subscriptionid Subscription id.
* @return array
*/
public static function execute(int $subscriptionid): array {
[
'subscriptionid' => $subscriptionid
] = self::validate_parameters(self::execute_parameters(), [
'subscriptionid' => $subscriptionid
]);
$status = false;
$warnings = [];
if (calendar_can_edit_subscription($subscriptionid)) {
// Fetch the subscription from the database making sure it exists.
$sub = calendar_get_subscription($subscriptionid);
calendar_delete_subscription($subscriptionid);
$status = true;
} else {
$warnings = [
'item' => $subscriptionid,
'warningcode' => 'errordeletingsubscription',
'message' => get_string('nopermissions', 'error')
];
}
return [
'status' => $status,
'warnings' => $warnings
];
}
/**
* Describes the data returned from the external function.
*
* @return external_single_structure
* @since Moodle 4.0
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'status' => new external_value(PARAM_BOOL, 'status: true if success'),
'warnings' => new external_warnings()
]);
}
}
+168
View File
@@ -0,0 +1,168 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains event class for displaying the day on month view.
*
* @package core_calendar
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
use renderer_base;
/**
* Class for displaying the day on month view.
*
* @package core_calendar
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class week_day_exporter extends day_exporter {
/**
* Constructor.
*
* @param \calendar_information $calendar The calendar information for the period being displayed
* @param mixed $data Either an stdClass or an array of values.
* @param array $related Related objects.
*/
public function __construct(\calendar_information $calendar, $data, $related) {
parent::__construct($calendar, $data, $related);
// Fix the url for today to be based on the today timestamp
// rather than the calendar_information time set in the parent
// constructor.
$this->url->param('time', $this->data[0]);
}
/**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
$return = parent::define_properties();
$return = array_merge($return, [
// These are additional params.
'istoday' => [
'type' => PARAM_BOOL,
'default' => false,
],
'isweekend' => [
'type' => PARAM_BOOL,
'default' => false,
],
]);
return $return;
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
$return = parent::define_other_properties();
$return = array_merge($return, [
'popovertitle' => [
'type' => PARAM_RAW,
'default' => '',
],
'daytitle' => [
'type' => PARAM_RAW,
]
]);
return $return;
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
$return = parent::get_other_values($output);
if ($popovertitle = $this->get_popover_title()) {
$return['popovertitle'] = $popovertitle;
}
$return['daytitle'] = $this->get_day_title();
return $return;
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'events' => '\core_calendar\local\event\entities\event_interface[]',
'cache' => '\core_calendar\external\events_related_objects_cache',
'type' => '\core_calendar\type_base',
];
}
/**
* Get the title for this popover.
*
* @return string
*/
protected function get_popover_title() {
$title = null;
$userdate = userdate($this->data[0], get_string('strftimedayshort'));
if (count($this->related['events'])) {
$title = get_string('eventsfor', 'calendar', $userdate);
} else if ($this->data['istoday']) {
$title = $userdate;
}
if ($this->data['istoday']) {
$title = get_string('todayplustitle', 'calendar', $userdate);
}
return $title;
}
/**
* Get the title for this day.
*
* @return string
*/
protected function get_day_title(): string {
$userdate = userdate($this->data[0], get_string('strftimedayshort'));
$numevents = count($this->related['events']);
if ($numevents == 1) {
$title = get_string('dayeventsone', 'calendar', $userdate);
} else if ($numevents) {
$title = get_string('dayeventsmany', 'calendar', ['num' => $numevents, 'day' => $userdate]);
} else {
$title = get_string('dayeventsnone', 'calendar', $userdate);
}
return $title;
}
}
+182
View File
@@ -0,0 +1,182 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains event class for displaying the week view.
*
* @package core_calendar
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\external;
defined('MOODLE_INTERNAL') || die();
use core\external\exporter;
use renderer_base;
/**
* Class for displaying the week view.
*
* @package core_calendar
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class week_exporter extends exporter {
/**
* @var array $days An array of day_exporter objects.
*/
protected $days = [];
/**
* @var int $prepadding The number of pre-padding days at the start of the week.
*/
protected $prepadding = 0;
/**
* @var int $postpadding The number of post-padding days at the start of the week.
*/
protected $postpadding = 0;
/**
* @var \calendar_information $calendar The calendar being displayed.
*/
protected $calendar;
/**
* Constructor.
*
* @param \calendar_information $calendar The calendar information for the period being displayed
* @param mixed $days An array of day_exporter objects.
* @param int $prepadding The number of pre-padding days at the start of the week.
* @param int $postpadding The number of post-padding days at the start of the week.
* @param array $related Related objects.
*/
public function __construct(\calendar_information $calendar, $days, $prepadding, $postpadding, $related) {
$this->days = $days;
$this->prepadding = $prepadding;
$this->postpadding = $postpadding;
$this->calendar = $calendar;
parent::__construct([], $related);
}
/**
* Return the list of additional properties.
*
* @return array
*/
protected static function define_other_properties() {
return [
'prepadding' => [
'type' => PARAM_INT,
'multiple' => true,
],
'postpadding' => [
'type' => PARAM_INT,
'multiple' => true,
],
'days' => [
'type' => week_day_exporter::read_properties_definition(),
'multiple' => true,
],
];
}
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
global $CFG;
$return = [
'prepadding' => [],
'postpadding' => [],
'days' => [],
];
for ($i = 0; $i < $this->prepadding; $i++) {
$return['prepadding'][] = $i;
}
for ($i = 0; $i < $this->postpadding; $i++) {
$return['postpadding'][] = $i;
}
$return['days'] = [];
$today = $this->related['type']->timestamp_to_date_array(time());
$weekend = CALENDAR_DEFAULT_WEEKEND;
if (isset($CFG->calendar_weekend)) {
$weekend = intval($CFG->calendar_weekend);
}
$numberofdaysinweek = $this->related['type']->get_num_weekdays();
foreach ($this->days as $daydata) {
$events = [];
foreach ($this->related['events'] as $event) {
$times = $event->get_times();
$starttime = $times->get_start_time()->getTimestamp();
$startdate = $this->related['type']->timestamp_to_date_array($starttime);
$endtime = $times->get_end_time()->getTimestamp();
$enddate = $this->related['type']->timestamp_to_date_array($endtime);
if ((($startdate['year'] * 366) + $startdate['yday']) > ($daydata['year'] * 366) + $daydata['yday']) {
// Starts after today.
continue;
}
if ((($enddate['year'] * 366) + $enddate['yday']) < ($daydata['year'] * 366) + $daydata['yday']) {
// Ends before today.
continue;
}
$events[] = $event;
}
$istoday = true;
$istoday = $istoday && $today['year'] == $daydata['year'];
$istoday = $istoday && $today['yday'] == $daydata['yday'];
$daydata['istoday'] = $istoday;
$daydata['isweekend'] = !!($weekend & (1 << ($daydata['wday'] % $numberofdaysinweek)));
$day = new week_day_exporter($this->calendar, $daydata, [
'events' => $events,
'cache' => $this->related['cache'],
'type' => $this->related['type'],
]);
$return['days'][] = $day->export($output);
}
return $return;
}
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return [
'events' => '\core_calendar\local\event\entities\event_interface[]',
'cache' => '\core_calendar\external\events_related_objects_cache',
'type' => '\core_calendar\type_base',
];
}
}
+338
View File
@@ -0,0 +1,338 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains class containing the internal calendar API.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local;
defined('MOODLE_INTERNAL') || die();
use core_calendar\local\event\container;
use core_calendar\local\event\entities\event_interface;
use core_calendar\local\event\exceptions\limit_invalid_parameter_exception;
/**
* Class containing the local calendar API.
*
* This should not be used outside of core_calendar.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class api {
/**
* Get all events restricted by various parameters, taking in to account user and group overrides.
*
* @param int|null $timestartfrom Events with timestart from this value (inclusive).
* @param int|null $timestartto Events with timestart until this value (inclusive).
* @param int|null $timesortfrom Events with timesort from this value (inclusive).
* @param int|null $timesortto Events with timesort until this value (inclusive).
* @param int|null $timestartaftereventid Restrict the events in the timestart range to ones after this ID.
* @param int|null $timesortaftereventid Restrict the events in the timesort range to ones after this ID.
* @param int $limitnum Return at most this number of events.
* @param int|null $type Return only events of this type.
* @param array|null $usersfilter Return only events for these users.
* @param array|null $groupsfilter Return only events for these groups.
* @param array|null $coursesfilter Return only events for these courses.
* @param bool $withduration If true return only events starting within specified
* timestart otherwise return in progress events as well.
* @param bool $ignorehidden If true don't return hidden events.
* @return \core_calendar\local\event\entities\event_interface[] Array of event_interfaces.
*/
public static function get_events(
$timestartfrom = null,
$timestartto = null,
$timesortfrom = null,
$timesortto = null,
$timestartaftereventid = null,
$timesortaftereventid = null,
$limitnum = 20,
$type = null,
array $usersfilter = null,
array $groupsfilter = null,
array $coursesfilter = null,
array $categoriesfilter = null,
$withduration = true,
$ignorehidden = true,
callable $filter = null
) {
global $USER;
$vault = \core_calendar\local\event\container::get_event_vault();
$timestartafterevent = null;
$timesortafterevent = null;
if ($timestartaftereventid && $event = $vault->get_event_by_id($timestartaftereventid)) {
$timestartafterevent = $event;
}
if ($timesortaftereventid && $event = $vault->get_event_by_id($timesortaftereventid)) {
$timesortafterevent = $event;
}
return $vault->get_events(
$timestartfrom,
$timestartto,
$timesortfrom,
$timesortto,
$timestartafterevent,
$timesortafterevent,
$limitnum,
$type,
$usersfilter,
$groupsfilter,
$coursesfilter,
$categoriesfilter,
$withduration,
$ignorehidden,
$filter
);
}
/**
* Get a list of action events for the logged in user by the given
* timesort values.
*
* @param int|null $timesortfrom The start timesort value (inclusive)
* @param int|null $timesortto The end timesort value (inclusive)
* @param int|null $aftereventid Only return events after this one
* @param int $limitnum Limit results to this amount (between 1 and 50)
* @param bool $lmittononsuspendedevents Limit course events to courses the user is active in (not suspended).
* @param \stdClass|null $user The user id or false for $USER
* @param string|null $searchvalue The value a user wishes to search against
* @return array A list of action_event_interface objects
* @throws \moodle_exception
*/
public static function get_action_events_by_timesort(
$timesortfrom = null,
$timesortto = null,
$aftereventid = null,
$limitnum = 20,
$limittononsuspendedevents = false,
?\stdClass $user = null,
?string $searchvalue = null
) {
global $USER;
if (!$user) {
$user = $USER;
}
if (is_null($timesortfrom) && is_null($timesortto)) {
throw new \moodle_exception("Must provide a timesort to and/or from value");
}
if ($limitnum < 1 || $limitnum > 50) {
throw new \moodle_exception("Limit must be between 1 and 50 (inclusive)");
}
\core_calendar\local\event\container::set_requesting_user($user->id);
$vault = \core_calendar\local\event\container::get_event_vault();
$afterevent = null;
if ($aftereventid && $event = $vault->get_event_by_id($aftereventid)) {
$afterevent = $event;
}
return $vault->get_action_events_by_timesort($user, $timesortfrom, $timesortto, $afterevent, $limitnum,
$limittononsuspendedevents, $searchvalue);
}
/**
* Get a list of action events for the logged in user by the given
* course and timesort values.
*
* @param \stdClass $course The course the events must belong to
* @param int|null $timesortfrom The start timesort value (inclusive)
* @param int|null $timesortto The end timesort value (inclusive)
* @param int|null $aftereventid Only return events after this one
* @param int $limitnum Limit results to this amount (between 1 and 50)
* @param string|null $searchvalue The value a user wishes to search against
* @return array A list of action_event_interface objects
* @throws limit_invalid_parameter_exception
*/
public static function get_action_events_by_course(
$course,
$timesortfrom = null,
$timesortto = null,
$aftereventid = null,
$limitnum = 20,
?string $searchvalue = null
) {
global $USER;
if ($limitnum < 1 || $limitnum > 50) {
throw new limit_invalid_parameter_exception(
"Limit must be between 1 and 50 (inclusive)");
}
$vault = \core_calendar\local\event\container::get_event_vault();
$afterevent = null;
if ($aftereventid && $event = $vault->get_event_by_id($aftereventid)) {
$afterevent = $event;
}
return $vault->get_action_events_by_course(
$USER, $course, $timesortfrom, $timesortto, $afterevent, $limitnum, $searchvalue);
}
/**
* Get a list of action events for the logged in user by the given
* courses and timesort values.
*
* The limit number applies per course, not for the result set as a whole.
* E.g. Requesting 3 courses with a limit of 10 will result in up to 30
* events being returned (up to 10 per course).
*
* @param array $courses The courses the events must belong to
* @param int|null $timesortfrom The start timesort value (inclusive)
* @param int|null $timesortto The end timesort value (inclusive)
* @param int $limitnum Limit results per course to this amount (between 1 and 50)
* @param string|null $searchvalue The value a user wishes to search against
* @return array A list of action_event_interface objects indexed by course id
*/
public static function get_action_events_by_courses(
$courses = [],
$timesortfrom = null,
$timesortto = null,
$limitnum = 20,
?string $searchvalue = null
) {
$return = [];
foreach ($courses as $course) {
$return[$course->id] = self::get_action_events_by_course(
$course,
$timesortfrom,
$timesortto,
null,
$limitnum,
$searchvalue
);
}
return $return;
}
/**
* Change the start day for an event. Only the date will be
* modified, the time of day for the event will be left as is.
*
* @param event_interface $event The existing event to modify
* @param DateTimeInterface $startdate The new date to use for the start day
* @return event_interface The new event with updated start date
*/
public static function update_event_start_day(
event_interface $event,
\DateTimeInterface $startdate
) {
global $DB;
$mapper = container::get_event_mapper();
$legacyevent = $mapper->from_event_to_legacy_event($event);
$hascoursemodule = !empty($event->get_course_module());
$moduleinstance = null;
$starttime = $event->get_times()->get_start_time()->setDate(
$startdate->format('Y'),
$startdate->format('n'),
$startdate->format('j')
);
$starttimestamp = $starttime->getTimestamp();
if ($hascoursemodule) {
$moduleinstance = $DB->get_record(
$event->get_course_module()->get('modname'),
['id' => $event->get_course_module()->get('instance')],
'*',
MUST_EXIST
);
// If there is a timestart range callback implemented then we can
// use the values returned from the valid timestart range to apply
// some default validation on the event's timestart value to ensure
// that it falls within the specified range.
list($min, $max) = component_callback(
'mod_' . $event->get_course_module()->get('modname'),
'core_calendar_get_valid_event_timestart_range',
[$legacyevent, $moduleinstance],
[false, false]
);
} else if ($legacyevent->courseid != 0 && $legacyevent->courseid != SITEID && $legacyevent->groupid == 0) {
// This is a course event.
list($min, $max) = component_callback(
'core_course',
'core_calendar_get_valid_event_timestart_range',
[$legacyevent, $event->get_course()->get_proxied_instance()],
[0, 0]
);
} else {
$min = $max = 0;
}
// If the callback returns false for either value it means that
// there is no valid time start range.
if ($min === false || $max === false) {
throw new \moodle_exception('The start day of this event can not be modified');
}
if ($min && $starttimestamp < $min[0]) {
throw new \moodle_exception($min[1]);
}
if ($max && $starttimestamp > $max[0]) {
throw new \moodle_exception($max[1]);
}
// This function does our capability checks.
$legacyevent->update((object) ['timestart' => $starttime->getTimestamp()]);
// Check that the user is allowed to manually edit calendar events before
// calling the event updated callback. The manual flag causes the code to
// check the user has the capabilities to modify the modules.
//
// We don't want to call the event update callback if the user isn't allowed
// to modify course modules because depending on the callback it can make
// some changes that would be considered security issues, such as updating the
// due date for an assignment.
if ($hascoursemodule && calendar_edit_event_allowed($legacyevent, true)) {
// If this event is from an activity then we need to call
// the activity callback to let it know that the event it
// created has been modified so it needs to update accordingly.
component_callback(
'mod_' . $event->get_course_module()->get('modname'),
'core_calendar_event_timestart_updated',
[$legacyevent, $moduleinstance]
);
// Rebuild the course cache to make sure the updated dates are reflected.
$courseid = $event->get_course()->get('id');
$cmid = $event->get_course_module()->get('id');
\course_modinfo::purge_course_module_cache($courseid, $cmid);
rebuild_course_cache($courseid, true, true);
}
return $mapper->from_legacy_event_to_event($legacyevent);
}
}
+361
View File
@@ -0,0 +1,361 @@
<?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/>.
/**
* Core container for calendar events.
*
* The purpose of this class is simply to wire together the various
* implementations of calendar event components to produce a solution
* to the problems Moodle core wants to solve.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event;
defined('MOODLE_INTERNAL') || die();
use core_calendar\action_factory;
use core_calendar\local\event\data_access\event_vault;
use core_calendar\local\event\entities\action_event;
use core_calendar\local\event\entities\action_event_interface;
use core_calendar\local\event\entities\event_interface;
use core_calendar\local\event\factories\event_factory;
use core_calendar\local\event\mappers\event_mapper;
use core_calendar\local\event\strategies\raw_event_retrieval_strategy;
/**
* Core container.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class container {
/**
* @var event_factory $eventfactory Event factory.
*/
protected static $eventfactory;
/**
* @var event_mapper $eventmapper Event mapper.
*/
protected static $eventmapper;
/**
* @var action_factory $actionfactory Action factory.
*/
protected static $actionfactory;
/**
* @var event_vault $eventvault Event vault.
*/
protected static $eventvault;
/**
* @var raw_event_retrieval_strategy $eventretrievalstrategy Event retrieval strategy.
*/
protected static $eventretrievalstrategy;
/**
* @var \stdClass[] An array of cached courses to use with the event factory.
*/
protected static $coursecache = array();
/**
* @var \stdClass[] An array of cached modules to use with the event factory.
*/
protected static $modulecache = array();
/**
* @var int The requesting user. All capability checks are done against this user.
*/
protected static $requestinguserid;
/**
* Initialises the dependency graph if it hasn't yet been.
*/
private static function init() {
if (empty(self::$eventfactory)) {
self::$actionfactory = new action_factory();
self::$eventmapper = new event_mapper(
// The event mapper we return from here needs to know how to
// make events, so it needs an event factory. However we can't
// give it the same one as we store and return in the container
// as that one uses all our plumbing to control event visibility.
//
// So we make a new even factory that doesn't do anyting other than
// return the instance.
new event_factory(
// Never apply actions, simply return.
function(event_interface $event) {
return $event;
},
// Never hide an event.
function() {
return true;
},
// Never bail out early when instantiating an event.
function() {
return false;
},
self::$coursecache,
self::$modulecache
)
);
self::$eventfactory = new event_factory(
[self::class, 'apply_component_provide_event_action'],
[self::class, 'apply_component_is_event_visible'],
function ($dbrow) {
$requestinguserid = self::get_requesting_user();
if (!empty($dbrow->categoryid)) {
// This is a category event. Check that the category is visible to this user.
$category = \core_course_category::get($dbrow->categoryid, IGNORE_MISSING, true, $requestinguserid);
if (empty($category) || !$category->is_uservisible($requestinguserid)) {
return true;
}
}
// For non-module events we assume that all checks were done in core_calendar_is_event_visible callback.
// For module events we also check that the course module and course itself are visible to the user.
if (empty($dbrow->modulename)) {
return false;
}
$instances = get_fast_modinfo($dbrow->courseid, $requestinguserid)->instances;
// If modinfo doesn't know about the module, we should ignore it.
if (!isset($instances[$dbrow->modulename]) || !isset($instances[$dbrow->modulename][$dbrow->instance])) {
return true;
}
$cm = $instances[$dbrow->modulename][$dbrow->instance];
// If the module is not visible to the current user, we should ignore it.
// We have to check enrolment here as well because the uservisible check
// looks for the "view" capability however some activities (such as Lesson)
// have that capability set on the "Authenticated User" role rather than
// on "Student" role, which means uservisible returns true even when the user
// is no longer enrolled in the course.
// So, with the following we are checking -
// 1) Only process modules if $cm->uservisible is true.
// 2) Only process modules for courses a user has the capability to view OR they are enrolled in.
// 3) Only process modules for courses that are visible OR if the course is not visible, the user
// has the capability to view hidden courses.
if (!$cm->uservisible) {
return true;
}
$coursecontext = \context_course::instance($dbrow->courseid);
if (!$cm->get_course()->visible &&
!has_capability('moodle/course:viewhiddencourses', $coursecontext, $requestinguserid)) {
return true;
}
if (!has_capability('moodle/course:view', $coursecontext, $requestinguserid) &&
!is_enrolled($coursecontext, $requestinguserid)) {
return true;
}
// Ok, now check if we are looking at a completion event.
if ($dbrow->eventtype === \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED) {
// Need to have completion enabled before displaying these events.
$course = new \stdClass();
$course->id = $dbrow->courseid;
$completion = new \completion_info($course);
if ($completion->is_enabled($cm)) {
// Check if the event is completed, then in this case we do not need to complete it.
// Make sure we're using a cm_info object.
$completiondata = $completion->get_data($cm);
return intval($completiondata->completionstate) === COMPLETION_COMPLETE;
}
return true;
}
return false;
},
self::$coursecache,
self::$modulecache
);
}
if (empty(self::$eventvault)) {
self::$eventretrievalstrategy = new raw_event_retrieval_strategy();
self::$eventvault = new event_vault(self::$eventfactory, self::$eventretrievalstrategy);
}
}
/**
* Reset all static caches, called between tests.
*/
public static function reset_caches() {
self::$requestinguserid = null;
self::$eventfactory = null;
self::$eventmapper = null;
self::$eventvault = null;
self::$actionfactory = null;
self::$eventretrievalstrategy = null;
self::$coursecache = [];
self::$modulecache = [];
}
/**
* Gets the event factory.
*
* @return event_factory
*/
public static function get_event_factory() {
self::init();
return self::$eventfactory;
}
/**
* Gets the event mapper.
*
* @return event_mapper
*/
public static function get_event_mapper() {
self::init();
return self::$eventmapper;
}
/**
* Return an event vault.
*
* @return event_vault
*/
public static function get_event_vault() {
self::init();
return self::$eventvault;
}
/**
* Sets the requesting user so that all capability checks are done against this user.
* Setting the requesting user (hence calling this function) is optional and if you do not so,
* $USER will be used as the requesting user. However, if you wish to set the requesting user yourself,
* you should call this function before any other function of the container class is called.
*
* @param int $userid The user id.
* @throws \coding_exception
*/
public static function set_requesting_user($userid) {
self::$requestinguserid = $userid;
}
/**
* Returns the requesting user id.
* It usually is the current user unless it has been set explicitly using set_requesting_user.
*
* @return int
*/
public static function get_requesting_user() {
global $USER;
return empty(self::$requestinguserid) ? $USER->id : self::$requestinguserid;
}
/**
* Calls callback 'core_calendar_provide_event_action' from the component responsible for the event
*
* If no callback is present or callback returns null, there is no action on the event
* and it will not be displayed on the dashboard.
*
* @param event_interface $event
* @return action_event|event_interface
*/
public static function apply_component_provide_event_action(event_interface $event) {
// Callbacks will get supplied a "legacy" version
// of the event class.
$mapper = self::$eventmapper;
$action = null;
if ($event->get_component()) {
$requestinguserid = self::get_requesting_user();
$legacyevent = $mapper->from_event_to_legacy_event($event);
// We know for a fact that the the requesting user might be different from the logged in user,
// but the event mapper is not aware of that.
if (empty($event->user) && !empty($legacyevent->userid)) {
$legacyevent->userid = $requestinguserid;
}
// Any other event will not be displayed on the dashboard.
$action = component_callback(
$event->get_component(),
'core_calendar_provide_event_action',
[
$legacyevent,
self::$actionfactory,
$requestinguserid
]
);
}
// If we get an action back, return an action event, otherwise
// continue piping through the original event.
//
// If a module does not implement the callback, component_callback
// returns null.
return $action ? new action_event($event, $action) : $event;
}
/**
* Calls callback 'core_calendar_is_event_visible' from the component responsible for the event
*
* The visibility callback is optional, if not present it is assumed as visible.
* If it is an actionable event but the get_item_count() returns 0 the visibility
* is set to false.
*
* @param event_interface $event
* @return bool
*/
public static function apply_component_is_event_visible(event_interface $event) {
$mapper = self::$eventmapper;
$eventvisible = null;
if ($event->get_component()) {
$requestinguserid = self::get_requesting_user();
$legacyevent = $mapper->from_event_to_legacy_event($event);
// We know for a fact that the the requesting user might be different from the logged in user,
// but the event mapper is not aware of that.
if (empty($event->user) && !empty($legacyevent->userid)) {
$legacyevent->userid = $requestinguserid;
}
$eventvisible = component_callback(
$event->get_component(),
'core_calendar_is_event_visible',
[
$legacyevent,
$requestinguserid
]
);
}
// Do not display the event if there is nothing to action.
if ($event instanceof action_event_interface && $event->get_action()->get_item_count() === 0) {
return false;
}
// Module does not implement the callback, event should be visible.
if (is_null($eventvisible)) {
return true;
}
return $eventvisible ? true : false;
}
}
@@ -0,0 +1,456 @@
<?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/>.
/**
* Event vault class
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\data_access;
defined('MOODLE_INTERNAL') || die();
use core_calendar\local\event\entities\action_event_interface;
use core_calendar\local\event\entities\event_interface;
use core_calendar\local\event\exceptions\limit_invalid_parameter_exception;
use core_calendar\local\event\factories\action_factory_interface;
use core_calendar\local\event\factories\event_factory_interface;
use core_calendar\local\event\strategies\raw_event_retrieval_strategy_interface;
/**
* Event vault class.
*
* This class will handle interacting with the database layer to retrieve
* the records. This is required to house the complex logic required for
* pagination because it's not a one-to-one mapping between database records
* and users.
*
* This is a repository. It's called a vault to reduce confusion because
* Moodle has already taken the name repository. Vault is cooler anyway.
*
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class event_vault implements event_vault_interface {
/**
* @var event_factory_interface $factory Factory for creating events.
*/
protected $factory;
/**
* @var raw_event_retrieval_strategy_interface $retrievalstrategy Strategy for getting events from the DB.
*/
protected $retrievalstrategy;
/**
* Create an event vault.
*
* @param event_factory_interface $factory An event factory
* @param raw_event_retrieval_strategy_interface $retrievalstrategy
*/
public function __construct(
event_factory_interface $factory,
raw_event_retrieval_strategy_interface $retrievalstrategy
) {
$this->factory = $factory;
$this->retrievalstrategy = $retrievalstrategy;
}
public function get_event_by_id($id) {
global $DB;
if ($record = $DB->get_record('event', ['id' => $id])) {
return $this->transform_from_database_record($record);
} else {
return false;
}
}
public function get_events(
$timestartfrom = null,
$timestartto = null,
$timesortfrom = null,
$timesortto = null,
event_interface $timestartafterevent = null,
event_interface $timesortafterevent = null,
$limitnum = 20,
$type = null,
array $usersfilter = null,
array $groupsfilter = null,
array $coursesfilter = null,
array $categoriesfilter = null,
$withduration = true,
$ignorehidden = true,
callable $filter = null,
?string $searchvalue = null
) {
$fromquery = function($field, $timefrom, $lastseenmethod, $afterevent, $withduration) {
if (!$timefrom) {
return false;
}
return $this->timefield_pagination_from(
$field,
$timefrom,
$afterevent ? $afterevent->get_times()->{$lastseenmethod}()->getTimestamp() : null,
$afterevent ? $afterevent->get_id() : null,
$withduration
);
};
$toquery = function($field, $timeto, $lastseenmethod, $afterevent) {
if (!$timeto) {
return false;
}
return $this->timefield_pagination_to(
$field,
$timeto,
$afterevent ? $afterevent->get_times()->{$lastseenmethod}()->getTimestamp() : null,
$afterevent ? $afterevent->get_id() : null
);
};
$timesortfromquery = $fromquery('timesort', $timesortfrom, 'get_sort_time', $timesortafterevent, $withduration);
$timesorttoquery = $toquery('timesort', $timesortto, 'get_sort_time', $timesortafterevent);
$timestartfromquery = $fromquery('timestart', $timestartfrom, 'get_start_time', $timestartafterevent, $withduration);
$timestarttoquery = $toquery('timestart', $timestartto, 'get_start_time', $timestartafterevent);
if (($timesortto && !$timesorttoquery) || ($timestartto && !$timestarttoquery)) {
return [];
}
$searchquery = $this->generate_search_subquery($searchvalue);
$params = array_merge(
$type ? ['type' => $type] : [],
$timesortfromquery ? $timesortfromquery['params'] : [],
$timesorttoquery ? $timesorttoquery['params'] : [],
$timestartfromquery ? $timestartfromquery['params'] : [],
$timestarttoquery ? $timestarttoquery['params'] : [],
$searchquery ? $searchquery['params'] : [],
);
$where = array_merge(
$type ? ['type = :type'] : [],
$timesortfromquery ? $timesortfromquery['where'] : [],
$timesorttoquery ? $timesorttoquery['where'] : [],
$timestartfromquery ? $timestartfromquery['where'] : [],
$timestarttoquery ? $timestarttoquery['where'] : [],
$searchquery ? [$searchquery['where']] : [],
);
$offset = 0;
$events = [];
while ($records = array_values($this->retrievalstrategy->get_raw_events(
$usersfilter,
$groupsfilter,
$coursesfilter,
$categoriesfilter,
$where,
$params,
"COALESCE(e.timesort, e.timestart) ASC, e.id ASC",
$offset,
$limitnum,
$ignorehidden
))) {
foreach ($records as $record) {
if ($event = $this->transform_from_database_record($record)) {
$filtertest = $filter ? $filter($event) : true;
if ($event && $filtertest) {
$events[] = $event;
}
if (count($events) == $limitnum) {
// We've got all of the events so break both loops.
break 2;
}
}
}
if (!$limitnum) {
break;
} else {
$offset += $limitnum;
}
}
return $events;
}
public function get_action_events_by_timesort(
\stdClass $user,
$timesortfrom = null,
$timesortto = null,
event_interface $afterevent = null,
$limitnum = 20,
$limittononsuspendedevents = false,
?string $searchvalue = null
) {
$courseids = array_map(function($course) {
return $course->id;
}, enrol_get_all_users_courses($user->id, $limittononsuspendedevents));
$groupids = array_reduce($courseids, function($carry, $courseid) use ($user) {
$groupings = groups_get_user_groups($courseid, $user->id);
// Grouping 0 is all groups.
return array_merge($carry, $groupings[0]);
}, []);
// Always include the site events.
$courseids = $courseids ? array_merge($courseids, [SITEID]) : $courseids;
return $this->get_events(
null,
null,
$timesortfrom,
$timesortto,
null,
$afterevent,
$limitnum,
CALENDAR_EVENT_TYPE_ACTION,
[$user->id],
$groupids ? $groupids : null,
$courseids ? $courseids : null,
null, // All categories.
true,
true,
function ($event) {
return $event instanceof action_event_interface;
},
$searchvalue
);
}
public function get_action_events_by_course(
\stdClass $user,
\stdClass $course,
$timesortfrom = null,
$timesortto = null,
event_interface $afterevent = null,
$limitnum = 20,
?string $searchvalue = null
) {
$groupings = groups_get_user_groups($course->id, $user->id);
return array_values(
$this->get_events(
null,
null,
$timesortfrom,
$timesortto,
null,
$afterevent,
$limitnum,
CALENDAR_EVENT_TYPE_ACTION,
[$user->id],
$groupings[0] ? $groupings[0] : null,
[$course->id],
[],
true,
true,
function ($event) use ($course) {
return $event instanceof action_event_interface && $event->get_course()->get('id') == $course->id;
},
$searchvalue
)
);
}
/**
* Generates SQL subquery and parameters for 'from' pagination.
*
* @param string $field
* @param int $timefrom
* @param int|null $lastseentime
* @param int|null $lastseenid
* @param bool $withduration
* @return array
*/
protected function timefield_pagination_from(
$field,
$timefrom,
$lastseentime = null,
$lastseenid = null,
$withduration = true
) {
$where = '';
$params = [];
if ($lastseentime && $lastseentime >= $timefrom) {
$where = '((timesort = :timefrom1 AND e.id > :timefromid) OR timesort > :timefrom2)';
if ($field === 'timestart') {
$where = '((timestart = :timefrom1 AND e.id > :timefromid) OR timestart > :timefrom2' .
($withduration ? ' OR timestart + timeduration > :timefrom3' : '') . ')';
}
$params['timefromid'] = $lastseenid;
$params['timefrom1'] = $lastseentime;
$params['timefrom2'] = $lastseentime;
$params['timefrom3'] = $lastseentime;
} else {
$where = 'timesort >= :timefrom';
if ($field === 'timestart') {
$where = '(timestart >= :timefrom' .
($withduration ? ' OR timestart + timeduration > :timefrom2' : '') . ')';
}
$params['timefrom'] = $timefrom;
$params['timefrom2'] = $timefrom;
}
return ['where' => [$where], 'params' => $params];
}
/**
* Generates SQL subquery and parameters for 'to' pagination.
*
* @param string $field
* @param int $timeto
* @param int|null $lastseentime
* @param int|null $lastseenid
* @return array|bool
*/
protected function timefield_pagination_to(
$field,
$timeto,
$lastseentime = null,
$lastseenid = null
) {
$where = [];
$params = [];
if ($lastseentime && $lastseentime > $timeto) {
// The last seen event from this set is after the time sort range which
// means all events in this range have been seen, so we can just return
// early here.
return false;
} else if ($lastseentime && $lastseentime == $timeto) {
$where[] = '((timesort = :timeto1 AND e.id > :timetoid) OR timesort < :timeto2)';
if ($field === 'timestart') {
$where[] = '((timestart = :timeto1 AND e.id > :timetoid) OR timestart < :timeto2)';
}
$params['timetoid'] = $lastseenid;
$params['timeto1'] = $timeto;
$params['timeto2'] = $timeto;
} else {
$where[] = ($field === 'timestart' ? 'timestart' : 'timesort') . ' <= :timeto';
$params['timeto'] = $timeto;
}
return ['where' => $where, 'params' => $params];
}
/**
* Create an event from a database record.
*
* @param \stdClass $record The database record
* @return event_interface|null
*/
protected function transform_from_database_record(\stdClass $record) {
return $this->factory->create_instance($record);
}
/**
* Fetches records from DB.
*
* @param int $userid
* @param array|null $whereconditions
* @param array $whereparams
* @param string $ordersql
* @param int $offset
* @param int $limitnum
* @return array
*/
protected function get_from_db(
$userid,
$whereconditions,
$whereparams,
$ordersql,
$offset,
$limitnum
) {
return array_values(
$this->retrievalstrategy->get_raw_events(
[$userid],
null,
null,
null,
$whereconditions,
$whereparams,
$ordersql,
$offset,
$limitnum
)
);
}
/**
* Generates SQL subquery and parameters for event searching.
*
* @param string|null $searchvalue Search value.
* @return array|null
*/
protected function generate_search_subquery(?string $searchvalue): ?array {
global $CFG, $DB;
if (!$searchvalue) {
return null;
}
$parts = preg_split('/\s+/', $searchvalue);
$wherecoursenameconditions = [];
$whereactivitynameconditions = [];
foreach ($parts as $index => $part) {
// Course name searching.
$wherecoursenameconditions[] = $DB->sql_like('c.fullname', ':cfullname' . $index, false);
$params['cfullname'. $index] = '%' . $DB->sql_like_escape($part) . '%';
// Activity name searching.
$whereactivitynameconditions[] = $DB->sql_like('e.name', ':eventname' . $index, false);
$params['eventname'. $index] = '%' . $DB->sql_like_escape($part) . '%';
}
// Activity type searching.
$whereconditions[] = $DB->sql_like('e.modulename', ':modulename', false);
$params['modulename'] = '%' . $DB->sql_like_escape($searchvalue) . '%';
// Activity type searching (localised type name).
require_once($CFG->dirroot . '/course/lib.php');
// Search in modules' singular and plural names.
$modules = array_keys(array_merge(
preg_grep('/' . $searchvalue . '/i', get_module_types_names()) ?: [],
preg_grep('/' . $searchvalue . '/i', get_module_types_names(true)) ?: [],
));
if ($modules) {
[$insql, $inparams] = $DB->get_in_or_equal($modules, SQL_PARAMS_NAMED, 'exactmodulename');
$whereconditions[] = 'e.modulename ' . $insql;
$params += $inparams;
}
$whereclause = '(';
$whereclause .= implode(' OR ', $whereconditions);
$whereclause .= ' OR (' . implode(' AND ', $wherecoursenameconditions) . ')';
$whereclause .= ' OR (' . implode(' AND ', $whereactivitynameconditions) . ')';
$whereclause .= ')';
return ['where' => $whereclause, 'params' => $params];
}
}
@@ -0,0 +1,137 @@
<?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/>.
/**
* Event vault interface
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\data_access;
defined('MOODLE_INTERNAL') || die();
use core_calendar\local\event\entities\event_interface;
/**
* Interface for an event vault class
*
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface event_vault_interface {
/**
* Retrieve an event for the given id.
*
* @param int $id The event id
* @return event_interface|false
*/
public function get_event_by_id($id);
/**
* Get all events restricted by various parameters, taking in to account user and group overrides.
*
* @param int|null $timestartfrom Events with timestart from this value (inclusive).
* @param int|null $timestartto Events with timestart until this value (inclusive).
* @param int|null $timesortfrom Events with timesort from this value (inclusive).
* @param int|null $timesortto Events with timesort until this value (inclusive).
* @param event_interface|null $timestartafterevent Restrict the events in the timestart range to ones after this one.
* @param event_interface|null $timesortafterevent Restrict the events in the timesort range to ones after this one.
* @param int $limitnum Return at most this number of events.
* @param int|null $type Return only events of this type.
* @param array|null $usersfilter Return only events for these users.
* @param array|null $groupsfilter Return only events for these groups.
* @param array|null $coursesfilter Return only events for these courses.
* @param bool $withduration If true return only events starting within specified
* timestart otherwise return in progress events as well.
* @param bool $ignorehidden If true don't return hidden events.
* @param callable|null $filter Additional logic to filter out unwanted events.
* Must return true to keep the event, false to discard it.
* @param string|null $searchvalue The value a user wishes to search against
* @return event_interface[] Array of event_interfaces.
*/
public function get_events(
$timestartfrom = null,
$timestartto = null,
$timesortfrom = null,
$timesortto = null,
event_interface $timestartafterevent = null,
event_interface $timesortafterevent = null,
$limitnum = 20,
$type = null,
array $usersfilter = null,
array $groupsfilter = null,
array $coursesfilter = null,
array $categoriesfilter = null,
$withduration = true,
$ignorehidden = true,
callable $filter = null,
?string $searchvalue = null
);
/**
* Retrieve an array of events for the given user and time constraints.
*
* If using this function for pagination then you can provide the last event that you've seen
* ($afterevent) and it will be used to appropriately offset the result set so that you don't
* receive the same events again.
* @param \stdClass $user The user for whom the events belong
* @param int $timesortfrom Events with timesort from this value (inclusive)
* @param int $timesortto Events with timesort until this value (inclusive)
* @param event_interface $afterevent Only return events after this one
* @param int $limitnum Return at most this number of events
* @param bool $lmittononsuspendedevents Limit course events to courses the user is active in (not suspended).
* @param string|null $searchvalue The value a user wishes to search against
* @return event_interface
*/
public function get_action_events_by_timesort(
\stdClass $user,
$timesortfrom,
$timesortto,
event_interface $afterevent,
$limitnum,
$limittononsuspendedevents,
?string $searchvalue = null
);
/**
* Retrieve an array of events for the given user filtered by the course and time constraints.
*
* If using this function for pagination then you can provide the last event that you've seen
* ($afterevent) and it will be used to appropriately offset the result set so that you don't
* receive the same events again.
*
* @param \stdClass $user The user for whom the events belong
* @param \stdClass $course The course to filter by
* @param int $timesortfrom Events with timesort from this value (inclusive)
* @param int $timesortto Events with timesort until this value (inclusive)
* @param event_interface $afterevent Only return events after this one
* @param int $limitnum Return at most this number of events
* @param string|null $searchvalue The value a user wishes to search against
* @return action_event_interface
*/
public function get_action_events_by_course(
\stdClass $user,
\stdClass $course,
$timesortfrom,
$timesortto,
event_interface $afterevent,
$limitnum,
?string $searchvalue = null
);
}
@@ -0,0 +1,136 @@
<?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/>.
/**
* Calendar action event class.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\entities;
defined('MOODLE_INTERNAL') || die();
use core_calendar\local\event\factories\action_factory_interface;
/**
* Class representing an actionable event.
*
* An actionable event can be thought of as an embellished event. That is,
* it does everything a regular event does, but has some extra information
* attached to it. For example, the URL a user needs to visit to complete
* an action, the number of actionable items, etc.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class action_event implements action_event_interface {
/**
* @var event_interface $event The event to delegate to.
*/
protected $event;
/**
* @var action_interface $action The action associated with this event.
*/
protected $action;
/**
* @var proxy_interface $category Category for this event.
*/
protected $category;
/**
* Constructor.
*
* @param event_interface $event The event to delegate to.
* @param action_interface $action The action associated with this event.
*/
public function __construct(event_interface $event, action_interface $action) {
$this->event = $event;
$this->action = $action;
}
public function get_id() {
return $this->event->get_id();
}
public function get_name() {
return $this->event->get_name();
}
public function get_description() {
return $this->event->get_description();
}
public function get_location() {
return $this->event->get_location();
}
public function get_category() {
return $this->event->get_category();
}
public function get_course() {
return $this->event->get_course();
}
public function get_course_module() {
return $this->event->get_course_module();
}
public function get_group() {
return $this->event->get_group();
}
public function get_user() {
return $this->event->get_user();
}
public function get_type() {
return $this->event->get_type();
}
public function get_times() {
return $this->event->get_times();
}
public function get_repeats() {
return $this->event->get_repeats();
}
public function get_subscription() {
return $this->event->get_subscription();
}
public function is_visible() {
return $this->event->is_visible();
}
public function get_action() {
return $this->action;
}
/**
* Event component
* @return string
*/
public function get_component() {
return $this->event->get_component();
}
}
@@ -0,0 +1,42 @@
<?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/>.
/**
* Calendar action event interface.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\entities;
defined('MOODLE_INTERNAL') || die();
/**
* Interface for an action event class.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface action_event_interface extends event_interface {
/**
* Get the action event's action.
*
* @return action_interface
*/
public function get_action();
}
@@ -0,0 +1,63 @@
<?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/>.
/**
* Action interface.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\entities;
defined('MOODLE_INTERNAL') || die();
/**
* Interface for a action class.
*
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface action_interface {
/**
* Get the name of the action.
*
* @return string
*/
public function get_name();
/**
* Get the URL of the action.
*
* @return \moodle_url
*/
public function get_url();
/**
* Get the number of items that need actioning.
*
* @return int
*/
public function get_item_count();
/**
* Get the actions actionability.
*
* @return bool
*/
public function is_actionable();
}
@@ -0,0 +1,231 @@
<?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/>.
/**
* Calendar event class.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\entities;
defined('MOODLE_INTERNAL') || die();
use core_calendar\local\event\proxies\proxy_interface;
use core_calendar\local\event\value_objects\description_interface;
use core_calendar\local\event\value_objects\times_interface;
/**
* Class representing a calendar event.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class event implements event_interface {
/**
* @var int $id The event's id in the database.
*/
protected $id;
/**
* @var string $name The name of this event.
*/
protected $name;
/**
* @var description_interface $description Description for this event.
*/
protected $description;
/**
* @var string $location Location of this event.
*/
protected $location;
/**
* @var proxy_interface $category Category for this event.
*/
protected $category;
/**
* @var proxy_interface $course Course for this event.
*/
protected $course;
/**
* @var proxy_interface $group Group for this event.
*/
protected $group;
/**
* @var proxy_interface $user User for this event.
*/
protected $user;
/**
* @var event_collection_interface $repeats Collection of repeat events.
*/
protected $repeats;
/**
* @var proxy_interface $coursemodule The course module that created this event.
*/
protected $coursemodule;
/**
* @var string type The type of this event.
*/
protected $type;
/**
* @var times_interface $times The times for this event.
*/
protected $times;
/**
* @var bool $visible The visibility of this event.
*/
protected $visible;
/**
* @var string $component
*/
protected $component;
/**
* @var proxy_interface $subscription Subscription for this event.
*/
protected $subscription;
/**
* Constructor.
*
* @param int $id The event's ID in the database.
* @param string $name The event's name.
* @param description_interface $description The event's description.
* @param proxy_interface|null $category The category associated with the event.
* @param proxy_interface|null $course The course associated with the event.
* @param proxy_interface|null $group The group associated with the event.
* @param proxy_interface|null $user The user associated with the event.
* @param event_collection_interface|null $repeats Collection of repeat events.
* @param proxy_interface|null $coursemodule The course module that created the event.
* @param string $type The event's type.
* @param times_interface $times The times associated with the event.
* @param bool $visible The event's visibility. True for visible, false for invisible.
* @param proxy_interface|null $subscription The event's subscription.
* @param string $location The event's location.
* @param string $component The event's component.
*/
public function __construct(
$id,
$name,
description_interface $description,
?proxy_interface $category,
?proxy_interface $course,
?proxy_interface $group,
?proxy_interface $user,
?event_collection_interface $repeats,
?proxy_interface $coursemodule,
$type,
times_interface $times,
$visible,
proxy_interface $subscription = null,
$location = null,
$component = null
) {
$this->id = $id;
$this->name = $name;
$this->description = $description;
$this->location = $location;
$this->category = $category;
$this->course = $course;
$this->group = $group;
$this->user = $user;
$this->repeats = $repeats;
$this->coursemodule = $coursemodule;
$this->type = $type;
$this->times = $times;
$this->visible = $visible;
$this->subscription = $subscription;
$this->component = $component;
}
public function get_id() {
return $this->id;
}
public function get_name() {
return $this->name;
}
public function get_description() {
return $this->description;
}
public function get_location() {
return $this->location;
}
public function get_category() {
return $this->category;
}
public function get_course() {
return $this->course;
}
public function get_course_module() {
return $this->coursemodule;
}
public function get_group() {
return $this->group;
}
public function get_user() {
return $this->user;
}
public function get_type() {
return $this->type;
}
public function get_times() {
return $this->times;
}
public function get_repeats() {
return $this->repeats;
}
public function get_subscription() {
return $this->subscription;
}
public function is_visible() {
return $this->visible;
}
/**
* Resolved event component (frankenstyle name of activity module or the component)
* @return string|null
*/
public function get_component() {
return $this->get_course_module() ? 'mod_' . $this->get_course_module()->get('modname') : $this->component;
}
}
@@ -0,0 +1,49 @@
<?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/>.
/**
* Interface for an event collection class.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\entities;
defined('MOODLE_INTERNAL') || die();
/**
* Interface for an event collection class.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface event_collection_interface extends \IteratorAggregate {
/**
* Get the event collection's ID.
*
* @return int
*/
public function get_id();
/**
* Get the total number of repeats in the collection.
*
* @return int
*/
public function get_num();
}
@@ -0,0 +1,142 @@
<?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/>.
/**
* Calendar event interface.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\entities;
use core_calendar\local\event\proxies\proxy_interface;
defined('MOODLE_INTERNAL') || die();
/**
* Interface for an event class.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface event_interface {
/**
* Get the event's ID.
*
* @return integer
*/
public function get_id();
/**
* Get the event's name.
*
* @return string
*/
public function get_name();
/**
* Get the event's description.
*
* @return description_interface
*/
public function get_description();
/**
* Get the event's location.
*
* @return location_interface
*/
public function get_location();
/**
* Get the category object associated with the event.
*
* @return proxy_interface
*/
public function get_category();
/**
* Get the course object associated with the event.
*
* @return proxy_interface
*/
public function get_course();
/**
* Get the course module object that created the event.
*
* @return proxy_interface
*/
public function get_course_module();
/**
* Get the group object associated with the event.
*
* @return proxy_interface
*/
public function get_group();
/**
* Get the user object associated with the event.
*
* @return proxy_interface
*/
public function get_user();
/**
* Get the event's type.
*
* @return string
*/
public function get_type();
/**
* Get the times associated with the event.
*
* @return times_interface
*/
public function get_times();
/**
* Get repeats of this event or null if the event has no
* repeats.
*
* @return event_collection_interface|null
*/
public function get_repeats();
/**
* Get the event's subscription.
*
* @return proxy_interface
*/
public function get_subscription();
/**
* Get the event's visibility.
*
* @return bool true if the event is visible, false otherwise
*/
public function is_visible();
/**
* Resolved event component (frankenstyle name of activity module or the component)
* @return string|null
*/
public function get_component();
}
@@ -0,0 +1,151 @@
<?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/>.
/**
* Event collection class.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\entities;
defined('MOODLE_INTERNAL') || die();
use core_calendar\local\event\factories\event_factory_interface;
/**
* Class representing a collection of repeat events.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class repeat_event_collection implements event_collection_interface {
/**
* @var int DB_QUERY_LIMIT How many records to pull from the DB at once.
*/
const DB_QUERY_LIMIT = 100;
/**
* @var int $parentid The ID of the event which the events in this collection are repeats of.
*/
protected $parentid;
/**
* @var \stdClass $parentrecord The parent event record from the database.
*/
protected $parentrecord;
/**
* @var event_factory_interface $factory Event factory.
*/
protected $factory;
/**
* @var int $num Total number of events that could be retrieved by this collection.
*/
protected $num;
/**
* Constructor.
*
* @param stdClass $dbrow The event dbrow that is being repeated.
* @param event_factory_interface $factory Event factory.
*/
public function __construct($dbrow, event_factory_interface $factory) {
$eventid = $dbrow->id;
$repeatid = $dbrow->repeatid;
if (empty($repeatid)) {
$this->parentrecord = $dbrow;
$this->parentid = $eventid;
} else {
$this->parentid = $repeatid;
}
if ($eventid === $repeatid) {
// This means the record we've been given is the parent
// record.
$this->parentrecord = $dbrow;
}
$this->factory = $factory;
}
public function get_id() {
return $this->parentid;
}
public function get_num() {
global $DB;
// Subtract one because the original event has repeatid = its own id.
return $this->num = max(
isset($this->num) ? $this->num : ($DB->count_records('event', ['repeatid' => $this->parentid]) - 1),
0
);
}
public function getIterator(): \Traversable {
$parentrecord = $this->get_parent_record();
foreach ($this->load_event_records() as $eventrecords) {
foreach ($eventrecords as $eventrecord) {
// In the case of the repeat event having unset information, fallback on the parent.
yield $this->factory->create_instance((object)array_merge((array)$parentrecord, (array)$eventrecord));
}
}
}
/**
* Return the parent DB record.
*
* @return \stdClass
*/
protected function get_parent_record() {
global $DB;
if (!isset($this->parentrecord)) {
$this->parentrecord = $DB->get_record('event', ['id' => $this->parentid]);
}
return $this->parentrecord;
}
/**
* Generate more event records.
*
* @param int $start Start offset.
* @return \stdClass[]
*/
protected function load_event_records($start = 0) {
global $DB;
while ($records = $DB->get_records_select(
'event',
'id <> :parentid AND repeatid = :repeatid',
[
'parentid' => $this->parentid,
'repeatid' => $this->parentid,
],
'id ASC',
'*',
$start,
self::DB_QUERY_LIMIT
)) {
yield $records;
$start += self::DB_QUERY_LIMIT;
}
}
}
@@ -0,0 +1,36 @@
<?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/>.
/**
* Invalid callback exception.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\exceptions;
defined('MOODLE_INTERNAL') || die();
/**
* Invalid callback exception.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class invalid_callback_exception extends \moodle_exception {
}
@@ -0,0 +1,36 @@
<?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/>.
/**
* General invalid parameter exception.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\exceptions;
defined('MOODLE_INTERNAL') || die();
/**
* General invalid parameter exception.
*
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class invalid_parameter_exception extends \moodle_exception {
}
@@ -0,0 +1,36 @@
<?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/>.
/**
* Invalid limit parameter exception.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\exceptions;
defined('MOODLE_INTERNAL') || die();
/**
* Invalid limit parameter exception.
*
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class limit_invalid_parameter_exception extends invalid_parameter_exception {
}
@@ -0,0 +1,36 @@
<?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/>.
/**
* Member does not exist exception.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\exceptions;
defined('MOODLE_INTERNAL') || die();
/**
* Member does not exist exception.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class member_does_not_exist_exception extends \moodle_exception {
}
@@ -0,0 +1,36 @@
<?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/>.
/**
* Invalid timesort parameter exception.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\exceptions;
defined('MOODLE_INTERNAL') || die();
/**
* Invalid timesort parameter exception.
*
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class timesort_invalid_parameter_exception extends invalid_parameter_exception {
}
@@ -0,0 +1,40 @@
<?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/>.
/**
* Action factory interface.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\factories;
defined('MOODLE_INTERNAL') || die();
interface action_factory_interface {
/**
* Creates an instance of an action.
*
* @param string $name The action's name.
* @param \moodle_url $url The action's URL.
* @param int $itemcount The number of items needing action.
* @param bool $actionable The action's actionability.
* @return \core_calendar\local\event\entities\action_interface The action.
*/
public function create_instance($name, \moodle_url $url, $itemcount, $actionable);
}
@@ -0,0 +1,207 @@
<?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/>.
/**
* Abstract event factory.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\factories;
defined('MOODLE_INTERNAL') || die();
use core_calendar\local\event\entities\event;
use core_calendar\local\event\entities\repeat_event_collection;
use core_calendar\local\event\exceptions\invalid_callback_exception;
use core_calendar\local\event\proxies\cm_info_proxy;
use core_calendar\local\event\proxies\coursecat_proxy;
use core_calendar\local\event\proxies\std_proxy;
use core_calendar\local\event\value_objects\event_description;
use core_calendar\local\event\value_objects\event_times;
use core_calendar\local\event\entities\event_interface;
/**
* Abstract factory for creating calendar events.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class event_abstract_factory implements event_factory_interface {
/**
* @var callable $actioncallbackapplier Function to apply component action callbacks.
*/
protected $actioncallbackapplier;
/**
* @var callable $visibilitycallbackapplier Function to apply component visibility callbacks.
*/
protected $visibilitycallbackapplier;
/**
* @var array Course cache for use with get_course_cached.
*/
protected $coursecachereference;
/**
* @var array Module cache reference for use with get_module_cached.
*/
protected $modulecachereference;
/**
* @var callable Bail out check for create_instance.
*/
protected $bailoutcheck;
/**
* Applies component actions to the event.
*
* @param event_interface $event The event to be updated.
* @return event_interface The potentially modified event.
*/
abstract protected function apply_component_action(event_interface $event);
/**
* Exposes the event (or not).
*
* @param event_interface $event The event to potentially expose.
* @return event_interface|null The exposed event or null.
*/
abstract protected function expose_event(event_interface $event);
/**
* Constructor.
*
* @param callable $actioncallbackapplier Function to apply component action callbacks.
* @param callable $visibilitycallbackapplier Function to apply component visibility callbacks.
* @param callable $bailoutcheck Function to test if we can return null early.
* @param array $coursecachereference Cache to use with get_course_cached.
* @param array $modulecachereference Cache to use with get_module_cached.
*/
public function __construct(
callable $actioncallbackapplier,
callable $visibilitycallbackapplier,
callable $bailoutcheck,
array &$coursecachereference,
array &$modulecachereference
) {
$this->actioncallbackapplier = $actioncallbackapplier;
$this->visibilitycallbackapplier = $visibilitycallbackapplier;
$this->bailoutcheck = $bailoutcheck;
$this->coursecachereference = &$coursecachereference;
$this->modulecachereference = &$modulecachereference;
}
public function create_instance(\stdClass $dbrow) {
if ($dbrow->modulename && $dbrow->instance && $dbrow->courseid == 0) {
// Some events (for example user overrides) may contain module instance but not course id. Find course id.
$cm = calendar_get_module_cached($this->modulecachereference, $dbrow->modulename, $dbrow->instance);
$dbrow->courseid = $cm->course;
}
$bailcheck = $this->bailoutcheck;
$bail = $bailcheck($dbrow);
if (!is_bool($bail)) {
throw new invalid_callback_exception(
'Bail check must return true or false'
);
}
if ($bail) {
return null;
}
$category = null;
$course = null;
$group = null;
$user = null;
$module = null;
$subscription = null;
$component = null;
if ($dbrow->modulename && $dbrow->instance) {
$module = new cm_info_proxy($dbrow->modulename, $dbrow->instance, $dbrow->courseid);
}
if ($dbrow->categoryid) {
$category = new coursecat_proxy($dbrow->categoryid);
}
$course = new std_proxy($dbrow->courseid, function($id) {
return calendar_get_course_cached($this->coursecachereference, $id);
});
if ($dbrow->groupid) {
$group = new std_proxy($dbrow->groupid, function($id) {
return calendar_get_group_cached($id);
});
}
if ($dbrow->userid) {
$user = new std_proxy($dbrow->userid, function($id) {
global $DB;
return $DB->get_record('user', ['id' => $id]);
});
}
if ($dbrow->subscriptionid) {
$subscription = new std_proxy($dbrow->subscriptionid, function($id) {
return calendar_get_subscription($id);
});
}
if (!empty($dbrow->repeatid)) {
$repeatcollection = new repeat_event_collection($dbrow, $this);
} else {
$repeatcollection = null;
}
if (!empty($dbrow->component)) {
$component = $dbrow->component;
}
$event = new event(
$dbrow->id,
$dbrow->name,
new event_description($dbrow->description, $dbrow->format),
$category,
$course,
$group,
$user,
$repeatcollection,
$module,
$dbrow->eventtype,
new event_times(
(new \DateTimeImmutable())->setTimestamp($dbrow->timestart),
(new \DateTimeImmutable())->setTimestamp($dbrow->timestart + $dbrow->timeduration),
(new \DateTimeImmutable())->setTimestamp($dbrow->timesort ? $dbrow->timesort : $dbrow->timestart),
(new \DateTimeImmutable())->setTimestamp($dbrow->timemodified),
(new \DateTimeImmutable())->setTimestamp($dbrow->timesort ? usergetmidnight($dbrow->timesort) : 0)
),
!empty($dbrow->visible),
$subscription,
$dbrow->location,
$component
);
$isactionevent = !empty($dbrow->type) && $dbrow->type == CALENDAR_EVENT_TYPE_ACTION;
return $isactionevent ? $this->expose_event($this->apply_component_action($event)) : $event;
}
}
@@ -0,0 +1,62 @@
<?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/>.
/**
* Event factory class.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\factories;
defined('MOODLE_INTERNAL') || die();
use core_calendar\local\event\exceptions\invalid_callback_exception;
use core_calendar\local\event\entities\event_interface;
/**
* Event factory class.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class event_factory extends event_abstract_factory {
protected function apply_component_action(event_interface $event) {
$callbackapplier = $this->actioncallbackapplier;
$callbackresult = $callbackapplier($event);
if (!$callbackresult instanceof event_interface) {
throw new invalid_callback_exception(
'Event factory action callback applier must return an instance of event_interface');
}
return $callbackresult;
}
protected function expose_event(event_interface $event) {
$callbackapplier = $this->visibilitycallbackapplier;
$callbackresult = $callbackapplier($event);
if (!is_bool($callbackresult)) {
throw new invalid_callback_exception('Event factory visibility callback applier must return true or false');
}
return $callbackresult === true ? $event : null;
}
}
@@ -0,0 +1,43 @@
<?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/>.
/**
* Event factory interface.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\factories;
defined('MOODLE_INTERNAL') || die();
/**
* Interface for an event factory class.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface event_factory_interface {
/**
* Creates an instance of an event.
*
* @param \stdClass $dbrow The event row from the database.
* @return \core_calendar\local\event\entities\event_interface
*/
public function create_instance(\stdClass $dbrow);
}
@@ -0,0 +1,248 @@
<?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/>.
/**
* The mform for creating a calendar event. Based on the old event form.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\forms;
use context_system;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/lib/formslib.php');
/**
* The mform class for creating a calendar event.
*
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class create extends \moodleform {
use eventtype;
/**
* Build the editor options using the given context.
*
* @param \context $context A Moodle context
* @return array
*/
public static function build_editor_options(\context $context) {
global $CFG;
return [
'context' => $context,
'maxfiles' => EDITOR_UNLIMITED_FILES,
'maxbytes' => $CFG->maxbytes,
'noclean' => true,
'autosave' => false
];
}
/**
* The form definition
*/
public function definition() {
global $PAGE;
$mform = $this->_form;
$starttime = isset($this->_customdata['starttime']) ? $this->_customdata['starttime'] : 0;
$editoroptions = !(empty($this->_customdata['editoroptions'])) ? $this->_customdata['editoroptions'] : null;
$courseid = !(empty($this->_customdata['courseid'])) ? $this->_customdata['courseid'] : null;
$eventtypes = $this->_customdata['eventtypes'];
if (in_array(true, $eventtypes, true) === false) {
throw new \moodle_exception('nopermissiontoupdatecalendar');
}
$mform->setDisableShortforms();
$mform->disable_form_change_checker();
// Empty string so that the element doesn't get rendered.
$mform->addElement('header', 'general', '');
$this->add_default_hidden_elements($mform);
// Event name field.
$mform->addElement('text', 'name', get_string('eventname', 'calendar'), 'size="50"');
$mform->addRule('name', get_string('required'), 'required', null, 'client');
$mform->setType('name', PARAM_TEXT);
// Event time start field.
$mform->addElement('date_time_selector', 'timestart', get_string('date'), ['defaulttime' => $starttime]);
// Add the select elements for the available event types.
$this->add_event_type_elements($mform, $eventtypes);
// Start of advanced elements.
// Advanced elements are not visible to the user by default.
// They are displayed through the user of a show more / less button.
$mform->addElement('editor', 'description', get_string('eventdescription', 'calendar'), ['rows' => 3], $editoroptions);
$mform->setType('description', PARAM_RAW);
$mform->setAdvanced('description');
$mform->addElement('text', 'location', get_string('location', 'moodle'), 'size="50"');
$mform->setType('location', PARAM_RAW_TRIMMED);
$mform->setAdvanced('location');
// Add the variety of elements allowed for selecting event duration.
$this->add_event_duration_elements($mform);
// Add the form elements for repeating events.
$this->add_event_repeat_elements($mform);
// Add the javascript required to enhance this mform.
$PAGE->requires->js_call_amd('core_calendar/event_form', 'init', [$mform->getAttribute('id')]);
}
/**
* A bit of custom validation for this form
*
* @param array $data An assoc array of field=>value
* @param array $files An array of files
* @return array
*/
public function validation($data, $files) {
global $DB;
$errors = parent::validation($data, $files);
$eventtype = isset($data['eventtype']) ? $data['eventtype'] : null;
$coursekey = ($eventtype == 'group') ? 'groupcourseid' : 'courseid';
$courseid = (!empty($data[$coursekey])) ? $data[$coursekey] : null;
$categoryid = (!empty($data['categoryid'])) ? $data['categoryid'] : null;
$eventtypes = $this->_customdata['eventtypes'];
if (empty($eventtype) || !isset($eventtypes[$eventtype]) || $eventtypes[$eventtype] == false) {
$errors['eventtype'] = get_string('invalideventtype', 'calendar');
}
if ($courseid && $courseid > 0) {
if ($course = $DB->get_record('course', ['id' => $courseid])) {
if ($data['timestart'] < $course->startdate) {
$errors['timestart'] = get_string('errorbeforecoursestart', 'calendar');
}
} else {
$errors[$coursekey] = get_string('invalidcourse', 'error');
}
}
if ($eventtype == 'course' && empty($courseid)) {
$errors['courseid'] = get_string('selectacourse');
}
if ($eventtype == 'category' && empty($categoryid)) {
$errors['categoryid'] = get_string('selectacategory');
}
if ($eventtype == 'group' && (!empty($courseid) && empty($data['groupid']))) {
$errors['groupcourseid'] = get_string('nogroups', 'core_group');
}
if ($eventtype == 'group' && empty($courseid)) {
$errors['groupcourseid'] = get_string('selectacourse');
}
if ($data['duration'] == 1 && $data['timestart'] > $data['timedurationuntil']) {
$errors['durationgroup'] = get_string('invalidtimedurationuntil', 'calendar');
} else if ($data['duration'] == 2 && (trim($data['timedurationminutes']) == '' || $data['timedurationminutes'] < 1)) {
$errors['durationgroup'] = get_string('invalidtimedurationminutes', 'calendar');
}
return $errors;
}
/**
* Add the list of hidden elements that should appear in this form each
* time. These elements will never be visible to the user.
*
* @param MoodleQuickForm $mform
*/
protected function add_default_hidden_elements($mform) {
global $USER;
// Add some hidden fields.
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->setDefault('id', 0);
$mform->addElement('hidden', 'userid');
$mform->setType('userid', PARAM_INT);
$mform->setDefault('userid', $USER->id);
$mform->addElement('hidden', 'modulename');
$mform->setType('modulename', PARAM_INT);
$mform->setDefault('modulename', '');
$mform->addElement('hidden', 'instance');
$mform->setType('instance', PARAM_INT);
$mform->setDefault('instance', 0);
$mform->addElement('hidden', 'visible');
$mform->setType('visible', PARAM_INT);
$mform->setDefault('visible', 1);
}
/**
* Add the various elements to express the duration options available
* for an event.
*
* @param MoodleQuickForm $mform
*/
protected function add_event_duration_elements($mform) {
$group = [];
$group[] = $mform->createElement('radio', 'duration', null, get_string('durationnone', 'calendar'), 0);
$group[] = $mform->createElement('radio', 'duration', null, get_string('durationuntil', 'calendar'), 1);
$group[] = $mform->createElement('date_time_selector', 'timedurationuntil', '');
$group[] = $mform->createElement('radio', 'duration', null, get_string('durationminutes', 'calendar'), 2);
$group[] = $mform->createElement('text', 'timedurationminutes', get_string('durationminutes', 'calendar'));
$mform->addGroup($group, 'durationgroup', get_string('eventduration', 'calendar'), '<br />', false);
$mform->setAdvanced('durationgroup');
$mform->disabledIf('timedurationuntil', 'duration', 'noteq', 1);
$mform->disabledIf('timedurationuntil[day]', 'duration', 'noteq', 1);
$mform->disabledIf('timedurationuntil[month]', 'duration', 'noteq', 1);
$mform->disabledIf('timedurationuntil[year]', 'duration', 'noteq', 1);
$mform->disabledIf('timedurationuntil[hour]', 'duration', 'noteq', 1);
$mform->disabledIf('timedurationuntil[minute]', 'duration', 'noteq', 1);
$mform->setType('timedurationminutes', PARAM_INT);
$mform->disabledIf('timedurationminutes', 'duration', 'noteq', 2);
$mform->setDefault('duration', 0);
}
/**
* Add the repeat elements for the form when creating a new event.
*
* @param MoodleQuickForm $mform
*/
protected function add_event_repeat_elements($mform) {
$mform->addElement('checkbox', 'repeat', get_string('repeatevent', 'calendar'), null);
$mform->addElement('text', 'repeats', get_string('repeatweeksl', 'calendar'), 'maxlength="10" size="10"');
$mform->setType('repeats', PARAM_INT);
$mform->setDefault('repeats', 1);
$mform->disabledIf('repeats', 'repeat', 'notchecked');
$mform->setAdvanced('repeat');
$mform->setAdvanced('repeats');
}
}
@@ -0,0 +1,122 @@
<?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/>.
/**
* The trait for adding eventtype fields to a form.
*
* @package core_calendar
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\forms;
defined('MOODLE_INTERNAL') || die();
/**
* The trait for adding eventtype fields to a form.
*
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
trait eventtype {
/**
* Add the appropriate elements for the available event types.
*
* If the only event type available is 'user' then we add a hidden
* element because there is nothing for the user to choose.
*
* If more than one type is available then we add the elements as
* follows:
* - Always add the event type selector
* - Elements per type:
* - course: add an additional select element with each
* course as an option.
* - group: add a select element for the course (different
* from the above course select) and a select
* element for the group.
*
* @param MoodleQuickForm $mform
* @param array $eventtypes The available event types for the user
*/
protected function add_event_type_elements($mform, $eventtypes) {
global $CFG, $DB;
$options = [];
if (!empty($eventtypes['user'])) {
$options['user'] = get_string('user', 'calendar');
}
if (!empty($eventtypes['group'])) {
$options['group'] = get_string('group', 'calendar');
}
if (!empty($eventtypes['course'])) {
$options['course'] = get_string('course', 'calendar');
}
if (!empty($eventtypes['category'])) {
$options['category'] = get_string('category', 'calendar');
}
if (!empty($eventtypes['site'])) {
$options['site'] = get_string('site', 'calendar');
}
// If we only have one event type and it's 'user' event then don't bother
// rendering the select boxes because there is no choice for the user to
// make.
if (!empty($eventtypes['user']) && count($options) == 1) {
$mform->addElement('hidden', 'eventtype');
$mform->setType('eventtype', PARAM_TEXT);
$mform->hardFreeze('eventtype');
$mform->setConstant('eventtype', 'user');
return;
} else {
$mform->addElement('select', 'eventtype', get_string('eventkind', 'calendar'), $options);
}
if (!empty($eventtypes['category'])) {
$categoryoptions = [];
foreach (\core_course_category::make_categories_list('moodle/category:manage') as $id => $category) {
$categoryoptions[$id] = $category;
}
$mform->addElement('autocomplete', 'categoryid', get_string('category'), $categoryoptions);
$mform->hideIf('categoryid', 'eventtype', 'noteq', 'category');
}
$showall = is_siteadmin() && !empty($CFG->calendar_adminseesall);
if (!empty($eventtypes['course'])) {
$mform->addElement('course', 'courseid', get_string('course'), ['limittoenrolled' => !$showall]);
$mform->hideIf('courseid', 'eventtype', 'noteq', 'course');
}
if (!empty($eventtypes['group'])) {
$groups = !(empty($this->_customdata['groups'])) ? $this->_customdata['groups'] : null;
// Get the list of courses without groups to filter on the course selector.
$sql = "SELECT c.id
FROM {course} c
WHERE c.id NOT IN (
SELECT DISTINCT courseid FROM {groups}
)";
$coursesnogroup = $DB->get_records_sql($sql);
$mform->addElement('course', 'groupcourseid', get_string('course'), ['limittoenrolled' => !$showall,
'exclude' => array_keys($coursesnogroup)]);
$mform->hideIf('groupcourseid', 'eventtype', 'noteq', 'group');
$mform->addElement('select', 'groupid', get_string('group'), $groups);
$mform->hideIf('groupid', 'eventtype', 'noteq', 'group');
// We handle the group select hide/show actions on the event_form module.
}
}
}
@@ -0,0 +1,173 @@
<?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/>.
/**
* Allows the user to manage calendar subscriptions.
*
* @copyright 2012 Jonathan Harker
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package calendar
*/
namespace core_calendar\local\event\forms;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/formslib.php');
/**
* Form for adding a subscription to a Moodle course calendar.
* @copyright 2012 Jonathan Harker
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class managesubscriptions extends \moodleform {
use eventtype;
/**
* Defines the form used to add calendar subscriptions.
*/
public function definition() {
global $PAGE;
$mform = $this->_form;
$eventtypes = calendar_get_allowed_event_types();
if (in_array(true, $eventtypes, true) === false) {
throw new \moodle_exception('nopermissiontoupdatecalendar');
}
// Name.
$mform->addElement('text', 'name', get_string('subscriptionname', 'calendar'), array('maxsize' => '255', 'size' => '40'));
$mform->addRule('name', get_string('required'), 'required', null, 'client');
$mform->setType('name', PARAM_TEXT);
// Import from (url | importfile).
$choices = array(CALENDAR_IMPORT_FROM_FILE => get_string('importfromfile', 'calendar'),
CALENDAR_IMPORT_FROM_URL => get_string('importfromurl', 'calendar'));
$mform->addElement('select', 'importfrom', get_string('importcalendarfrom', 'calendar'), $choices);
$mform->setDefault('importfrom', CALENDAR_IMPORT_FROM_URL);
// URL.
$mform->addElement('text', 'url', get_string('importfromurl', 'calendar'), array('maxsize' => '255', 'size' => '50'));
// Cannot set as PARAM_URL since we need to allow webcal:// protocol.
$mform->setType('url', PARAM_RAW);
$mform->setForceLtr('url');
// Poll interval
$choices = calendar_get_pollinterval_choices();
$mform->addElement('select', 'pollinterval', get_string('pollinterval', 'calendar'), $choices);
$mform->setDefault('pollinterval', 604800);
$mform->addHelpButton('pollinterval', 'pollinterval', 'calendar');
$mform->setType('pollinterval', PARAM_INT);
// Import file
$mform->addElement('filepicker', 'importfile', get_string('importfromfile', 'calendar'), null, array('accepted_types' => '.ics'));
// Disable appropriate elements depending on import from value.
$mform->hideIf('pollinterval', 'importfrom', 'eq', CALENDAR_IMPORT_FROM_FILE);
$mform->hideIf('url', 'importfrom', 'eq', CALENDAR_IMPORT_FROM_FILE);
$mform->hideIf('importfile', 'importfrom', 'eq', CALENDAR_IMPORT_FROM_URL);
// Add the select elements for the available event types.
$this->add_event_type_elements($mform, $eventtypes);
// Eventtype: 0 = user, 1 = site, anything else = course ID.
$mform->addElement('submit', 'add', get_string('importcalendar', 'calendar'));
// Add the javascript required to enhance this mform.
$PAGE->requires->js_call_amd('core_calendar/event_form', 'init', [$mform->getAttribute('id')]);
}
/**
* Validates the returned data.
*
* @param array $data
* @param array $files
* @return array
*/
public function validation($data, $files) {
global $USER;
$errors = parent::validation($data, $files);
$eventtype = isset($data['eventtype']) ? $data['eventtype'] : null;
$coursekey = ($eventtype == 'group') ? 'groupcourseid' : 'courseid';
$courseid = (!empty($data[$coursekey])) ? $data[$coursekey] : null;
$eventtypes = calendar_get_allowed_event_types($courseid);
if (empty($eventtype) || !isset($eventtypes[$eventtype]) || $eventtypes[$eventtype] == false) {
$errors['eventtype'] = get_string('invalideventtype', 'calendar');
}
if ($data['importfrom'] == CALENDAR_IMPORT_FROM_FILE) {
if (empty($data['importfile'])) {
$errors['importfile'] = get_string('errorrequiredurlorfile', 'calendar');
} else {
// Make sure the file area is not empty and contains only one file.
$draftitemid = $data['importfile'];
$fs = get_file_storage();
$usercontext = \context_user::instance($USER->id);
$files = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id DESC', false);
if (count($files) !== 1) {
$errors['importfile'] = get_string('errorrequiredurlorfile', 'calendar');
}
}
} else if (($data['importfrom'] == CALENDAR_IMPORT_FROM_URL)) {
if (empty($data['url'])) {
$errors['url'] = get_string('errorrequiredurlorfile', 'calendar');
} else {
// Clean input calendar url.
$url = clean_param($data['url'], PARAM_URL);
try {
calendar_get_icalendar($url);
} catch (\moodle_exception $e) {
$errors['url'] = get_string('errorinvalidicalurl', 'calendar');
}
}
} else {
// Shouldn't happen.
$errors['url'] = get_string('errorrequiredurlorfile', 'calendar');
}
// Validate course/category event types (ensure appropriate field is also filled in).
if ($eventtype === 'course' && empty($data['courseid'])) {
$errors['courseid'] = get_string('selectacourse');
} else if ($eventtype === 'category' && empty($data['categoryid'])) {
$errors['categoryid'] = get_string('required');
}
return $errors;
}
public function definition_after_data() {
$mform =& $this->_form;
$mform->applyFilter('url', static::class . '::strip_webcal');
$mform->applyFilter('url', 'trim');
}
/**
* Replace webcal:// urls with http:// as
* curl does not understand this protocol
*
* @param string @url url to examine
* @return string url with webcal:// replaced
*/
public static function strip_webcal($url) {
if (strpos($url, 'webcal://') === 0) {
$url = str_replace('webcal://', 'http://', $url);
}
return $url;
}
}
@@ -0,0 +1,59 @@
<?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/>.
/**
* The mform for updating a calendar event. Based on the old event form.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\forms;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/lib/formslib.php');
/**
* The mform class for updating a calendar event.
*
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class update extends create {
/**
* Add the repeat elements for the form when editing an existing event.
*
* @param MoodleQuickForm $mform
*/
protected function add_event_repeat_elements($mform) {
$event = $this->_customdata['event'];
$mform->addElement('hidden', 'repeatid');
$mform->setType('repeatid', PARAM_INT);
if (!empty($event->repeatid)) {
$group = [];
$group[] = $mform->createElement('radio', 'repeateditall', null, get_string('repeateditall', 'calendar',
$event->eventrepeats), 1);
$group[] = $mform->createElement('radio', 'repeateditall', null, get_string('repeateditthis', 'calendar'), 0);
$mform->addGroup($group, 'repeatgroup', get_string('repeatedevents', 'calendar'), '<br />', false);
$mform->setDefault('repeateditall', 1);
$mform->setAdvanced('repeatgroup');
}
}
}
@@ -0,0 +1,139 @@
<?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/>.
/**
* Event create form and update form mapper.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\mappers;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/calendar/lib.php');
/**
* Event create form and update form mapper class.
*
* This class will perform the necessary data transformations to take
* a legacy event and build the appropriate data structure for both the
* create and update event forms.
*
* It will also do the reverse transformation
* and take the returned form data and provide a data structure that can
* be used to set legacy event properties.
*
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class create_update_form_mapper implements create_update_form_mapper_interface {
/**
* Generate the appropriate data for the form from a legacy event.
*
* @param \calendar_event $legacyevent
* @return stdClass
*/
public function from_legacy_event_to_data(\calendar_event $legacyevent) {
$legacyevent->count_repeats();
$data = $legacyevent->properties();
$data->timedurationuntil = $legacyevent->timestart + $legacyevent->timeduration;
$data->duration = (empty($legacyevent->timeduration)) ? 0 : 1;
if ($legacyevent->eventtype == 'group') {
// Set up the correct value for the to display on the form.
$data->groupid = $legacyevent->groupid;
$data->groupcourseid = $legacyevent->courseid;
}
if ($legacyevent->eventtype == 'course') {
// Set up the correct value for the to display on the form.
$data->courseid = $legacyevent->courseid;
}
$data->description = [
'text' => $data->description,
'format' => $data->format
];
// Don't return context or subscription because they're not form values and break validation.
if (isset($data->context)) {
unset($data->context);
}
if (isset($data->subscription)) {
unset($data->subscription);
}
return $data;
}
/**
* Generate the appropriate calendar_event properties from the form data.
*
* @param \stdClass $data
* @return stdClass
*/
public function from_data_to_event_properties(\stdClass $data) {
$properties = clone($data);
if ($data->eventtype == 'group') {
if (isset($data->groupcourseid)) {
$properties->courseid = $data->groupcourseid;
unset($properties->groupcourseid);
}
if (isset($data->groupid)) {
$properties->groupid = $data->groupid;
}
} else {
// Default course id if none is set.
if (empty($properties->courseid)) {
if ($properties->eventtype == 'site') {
$properties->courseid = SITEID;
} else {
$properties->courseid = 0;
}
} else {
$properties->courseid = $data->courseid;
}
if (empty($properties->groupid)) {
$properties->groupid = 0;
}
}
// Decode the form fields back into valid event property.
$properties->timeduration = $this->get_time_duration_from_form_data($data);
return $properties;
}
/**
* A helper function to calculate the time duration for an event based on
* the event_form data.
*
* @param \stdClass $data event_form data
* @return int
*/
private function get_time_duration_from_form_data(\stdClass $data) {
if ($data->duration == 1) {
return $data->timedurationuntil - $data->timestart;
} else if ($data->duration == 2) {
return $data->timedurationminutes * MINSECS;
} else {
return 0;
}
}
}
@@ -0,0 +1,53 @@
<?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/>.
/**
* Create update form mapper interface.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\mappers;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/calendar/lib.php');
/**
* Interface for a create_update_form_mapper class
*
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface create_update_form_mapper_interface {
/**
* Generate the appropriate data for the form from a legacy event.
*
* @param \calendar_event $legacyevent
* @return stdClass
*/
public function from_legacy_event_to_data(\calendar_event $legacyevent);
/**
* Generate the appropriate calendar_event properties from the form data.
*
* @param \stdClass $data
* @return stdClass
*/
public function from_data_to_event_properties(\stdClass $data);
}
@@ -0,0 +1,150 @@
<?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/>.
/**
* Event mapper.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\mappers;
defined('MOODLE_INTERNAL') || die();
use core_calendar\event;
use core_calendar\local\event\entities\action_event_interface;
use core_calendar\local\event\entities\event_interface;
use core_calendar\local\event\factories\event_factory_interface;
/**
* Event mapper class.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class event_mapper implements event_mapper_interface {
/**
* @var event_factory_interface $factory Event factory.
*/
protected $factory;
/**
* Constructor.
*
* @param event_factory_interface $factory Event factory.
*/
public function __construct(event_factory_interface $factory) {
$this->factory = $factory;
}
public function from_legacy_event_to_event(\calendar_event $legacyevent) {
$coalesce = function($property) use ($legacyevent) {
try {
return $legacyevent->$property;
} catch (\coding_exception $e) {
// The magic setter throews an exception if the
// property doesn't exist.
return null;
}
};
return $this->factory->create_instance(
(object)[
'id' => $coalesce('id'),
'name' => $coalesce('name'),
'description' => $coalesce('description'),
'location' => $coalesce('location'),
'format' => $coalesce('format'),
'categoryid' => $coalesce('categoryid'),
'courseid' => $coalesce('courseid'),
'groupid' => $coalesce('groupid'),
'userid' => $coalesce('userid'),
'repeatid' => $coalesce('repeatid'),
'component' => $coalesce('component'),
'modulename' => $coalesce('modulename'),
'instance' => $coalesce('instance'),
'eventtype' => $coalesce('eventtype'),
'timestart' => $coalesce('timestart'),
'timeduration' => $coalesce('timeduration'),
'timemodified' => $coalesce('timemodified'),
'timesort' => $coalesce('timesort'),
'visible' => $coalesce('visible'),
'subscriptionid' => $coalesce('subscriptionid')
]
);
}
public function from_event_to_legacy_event(event_interface $event) {
$action = ($event instanceof action_event_interface) ? $event->get_action() : null;
$timeduration = $event->get_times()->get_end_time()->getTimestamp() - $event->get_times()->get_start_time()->getTimestamp();
$properties = $this->from_event_to_stdclass($event);
// Normalise for the legacy event because it wants zero rather than null.
$properties->courseid = empty($properties->courseid) ? 0 : $properties->courseid;
$properties->categoryid = empty($properties->categoryid) ? 0 : $properties->categoryid;
$properties->groupid = empty($properties->groupid) ? 0 : $properties->groupid;
$properties->userid = empty($properties->userid) ? 0 : $properties->userid;
$properties->component = empty($properties->component) ? 0 : $properties->component;
$properties->modulename = empty($properties->modulename) ? 0 : $properties->modulename;
$properties->instance = empty($properties->instance) ? 0 : $properties->instance;
$properties->repeatid = empty($properties->repeatid) ? 0 : $properties->repeatid;
return new \calendar_event($properties);
}
public function from_event_to_stdclass(event_interface $event) {
$action = ($event instanceof action_event_interface) ? $event->get_action() : null;
$timeduration = $event->get_times()->get_end_time()->getTimestamp() - $event->get_times()->get_start_time()->getTimestamp();
return (object)$this->from_event_to_assoc_array($event);
}
public function from_event_to_assoc_array(event_interface $event) {
$action = ($event instanceof action_event_interface) ? $event->get_action() : null;
$timeduration = $event->get_times()->get_end_time()->getTimestamp() - $event->get_times()->get_start_time()->getTimestamp();
return [
'id' => $event->get_id(),
'name' => $event->get_name(),
'description' => $event->get_description()->get_value(),
'format' => $event->get_description()->get_format(),
'location' => $event->get_location(),
'courseid' => $event->get_course() ? $event->get_course()->get('id') : null,
'categoryid' => $event->get_category() ? $event->get_category()->get('id') : null,
'groupid' => $event->get_group() ? $event->get_group()->get('id') : null,
'userid' => $event->get_user() ? $event->get_user()->get('id') : null,
'repeatid' => $event->get_repeats() ? $event->get_repeats()->get_id() : null,
'component' => $event->get_component(),
'modulename' => $event->get_course_module() ? $event->get_course_module()->get('modname') : null,
'instance' => $event->get_course_module() ? $event->get_course_module()->get('instance') : null,
'eventtype' => $event->get_type(),
'timestart' => $event->get_times()->get_start_time()->getTimestamp(),
'timeduration' => $timeduration,
'timesort' => $event->get_times()->get_sort_time()->getTimestamp(),
'timeusermidnight' => $event->get_times()->get_usermidnight_time()->getTimestamp(),
'visible' => $event->is_visible() ? 1 : 0,
'timemodified' => $event->get_times()->get_modified_time()->getTimestamp(),
'subscriptionid' => $event->get_subscription() ? $event->get_subscription()->get('id') : null,
'actionname' => $action ? $action->get_name() : null,
'actionurl' => $action ? $action->get_url() : null,
'actionnum' => $action ? $action->get_item_count() : null,
'actionactionable' => $action ? $action->is_actionable() : null,
'sequence' => 1
];
}
}
@@ -0,0 +1,70 @@
<?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/>.
/**
* Event mapper interface.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\mappers;
defined('MOODLE_INTERNAL') || die();
use core_calendar\event;
use core_calendar\local\event\entities\event_interface;
/**
* Interface for an event mapper class
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface event_mapper_interface {
/**
* Map a legacy event to an event.
*
* @param \calendar_event $event The legacy event.
* @return event_interface The mapped event.
*/
public function from_legacy_event_to_event(\calendar_event $event);
/**
* Map an event to a legacy event.
*
* @param event_interface $event The legacy event.
* @return \calendar_event The mapped legacy event.
*/
public function from_event_to_legacy_event(event_interface $event);
/**
* Map an event to a stdClass
*
* @param event_interface $event The legacy event.
* @return \stdClass The mapped stdClass.
*/
public function from_event_to_stdclass(event_interface $event);
/**
* Map an event to an associative array.
*
* @param event_interface $event The legacy event.
* @return array The mapped legacy event array.
*/
public function from_event_to_assoc_array(event_interface $event);
}
@@ -0,0 +1,83 @@
<?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/>.
/**
* Course module cm_info proxy.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\proxies;
defined('MOODLE_INTERNAL') || die();
/**
* Course module stdClass proxy.
*
* This implementation differs from the regular std_proxy in that it takes
* a module name and instance instead of an id to construct the proxied class.
*
* This is needed as the event table does not store the id of course modules
* instead it stores the module name and instance.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cm_info_proxy implements proxy_interface {
/** @var \stdClass */
protected $base;
/** @var \cm_info */
protected $cm;
/**
* cm_info_proxy constructor.
*
* @param string $modname The module name.
* @param int $instance The module instance.
* @param int $courseid course id this module belongs to
*/
public function __construct($modname, $instance, $courseid) {
$this->base = (object)['course' => $courseid, 'modname' => $modname, 'instance' => $instance];
}
/**
* Retrieve a member of the proxied class.
*
* @param string $member The name of the member to retrieve
* @return mixed The member.
*/
public function get($member) {
if ($this->base && property_exists($this->base, $member)) {
return $this->base->{$member};
}
return $this->get_proxied_instance()->{$member};
}
/**
* Get the full instance of the proxied class.
*
* @return \cm_info
*/
public function get_proxied_instance() {
if (!$this->cm) {
$this->cm = get_fast_modinfo($this->base->course)->instances[$this->base->modname][$this->base->instance];
}
return $this->cm;
}
}
@@ -0,0 +1,90 @@
<?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/>.
/**
* Course category proxy.
*
* @package core_calendar
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\proxies;
defined('MOODLE_INTERNAL') || die();
/**
* Course category proxy.
*
* This returns an instance of a coursecat rather than a stdClass.
*
* @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class coursecat_proxy implements proxy_interface {
/**
* @var int $id The ID of the database record.
*/
protected $id;
/**
* @var \stdClass $base Base class to get members from.
*/
protected $base;
/**
* @var \core_course_category $category The proxied instance.
*/
protected $category;
/**
* coursecat_proxy constructor.
*
* @param int $id The ID of the record in the database.
*/
public function __construct($id) {
$this->id = $id;
$this->base = (object) [
'id' => $id,
];
}
/**
* Retrieve a member of the proxied class.
*
* @param string $member The name of the member to retrieve
* @return mixed The member.
*/
public function get($member) {
if ($this->base && property_exists($this->base, $member)) {
return $this->base->{$member};
}
return $this->get_proxied_instance()->{$member};
}
/**
* Get the full instance of the proxied class.
*
* @return \core_course_category
*/
public function get_proxied_instance(): \core_course_category {
if (!$this->category) {
$this->category = \core_course_category::get($this->id, IGNORE_MISSING, true);
}
return $this->category;
}
}
@@ -0,0 +1,52 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Proxy interface.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\proxies;
defined('MOODLE_INTERNAL') || die();
/**
* Interface for a proxy class.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface proxy_interface {
/**
* Retrieve a member of the proxied class.
*
* @param string $member The name of the member to retrieve
* @throws \core_calendar\local\event\exceptions\member_does_not_exist_exception If the proxied class does not have the
* requested member.
* @return mixed The member.
*/
public function get($member);
/**
* Get the full instance of the proxied class.
*
* @return mixed
*/
public function get_proxied_instance();
}
@@ -0,0 +1,101 @@
<?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/>.
/**
* std_proxy class.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\proxies;
defined('MOODLE_INTERNAL') || die();
use core_calendar\local\event\exceptions\member_does_not_exist_exception;
/**
* stdClass proxy.
*
* This class is intended to proxy things like user, group, etc 'classes'
* It will only run the callback to load the object from the DB when necessary.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class std_proxy implements proxy_interface {
/**
* @var int $id The ID of the database record.
*/
protected $id;
/**
* @var \stdClass $class The class we are proxying.
*/
protected $class;
/**
* @var callable $callback Callback to run which will load the class to proxy.
*/
protected $callback;
/**
* @var array $callbackargs Array of arguments to pass to the callback.
*/
protected $callbackargs;
/**
* @var \stdClass $base Base class to get members from.
*/
protected $base;
/**
* Constructor.
*
* @param int $id The ID of the record in the database.
* @param callable $callback Callback to load the class.
* @param \stdClass $base Class containing base values.
*/
public function __construct($id, callable $callback, \stdClass $base = null) {
$this->id = $id;
$this->callbackargs = [$id];
$this->callback = $callback;
$this->base = $base;
}
public function get($member) {
if ($member === 'id') {
return $this->id;
}
if ($this->base && property_exists($this->base, $member)) {
return $this->base->{$member};
}
if (!property_exists($this->get_proxied_instance(), $member)) {
throw new member_does_not_exist_exception(sprintf('Member %s does not exist', $member));
}
return $this->get_proxied_instance()->{$member};
}
public function get_proxied_instance() {
$callback = $this->callback;
return $this->class = $this->class ? $this->class : $callback(...$this->callbackargs);
}
}
@@ -0,0 +1,385 @@
<?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/>.
/**
* Raw event retrieval strategy.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\strategies;
defined('MOODLE_INTERNAL') || die();
/**
* Raw event retrieval strategy.
*
* This strategy is based on what used to be the calendar API's get_events function.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class raw_event_retrieval_strategy implements raw_event_retrieval_strategy_interface {
public function get_raw_events(
array $usersfilter = null,
array $groupsfilter = null,
array $coursesfilter = null,
array $categoriesfilter = null,
array $whereconditions = null,
array $whereparams = null,
$ordersql = null,
$offset = null,
$limitnum = null,
$ignorehidden = true
) {
return $this->get_raw_events_legacy_implementation(
!is_null($usersfilter) ? $usersfilter : true, // True means no filter in old implementation.
!is_null($groupsfilter) ? $groupsfilter : true,
!is_null($coursesfilter) ? $coursesfilter : true,
!is_null($categoriesfilter) ? $categoriesfilter : true,
$whereconditions,
$whereparams,
$ordersql,
$offset,
$limitnum,
$ignorehidden
);
}
/**
* The legacy implementation with minor tweaks.
*
* @param array|int|boolean $users array of users, user id or boolean for all/no user events
* @param array|int|boolean $groups array of groups, group id or boolean for all/no group events
* @param array|int|boolean $courses array of courses, course id or boolean for all/no course events
* @param array $whereconditions The conditions in the WHERE clause.
* @param array $whereparams The parameters for the WHERE clause.
* @param string $ordersql The ORDER BY clause.
* @param int $offset Offset.
* @param int $limitnum Limit.
* @param boolean $ignorehidden whether to select only visible events or all events
* @return array $events of selected events or an empty array if there aren't any (or there was an error)
*/
protected function get_raw_events_legacy_implementation(
$users,
$groups,
$courses,
$categories,
$whereconditions,
$whereparams,
$ordersql,
$offset,
$limitnum,
$ignorehidden
) {
global $DB;
$params = array();
// Quick test.
if (empty($users) && empty($groups) && empty($courses) && empty($categories)) {
return array();
}
if (is_numeric($users)) {
$users = array($users);
}
if (is_numeric($groups)) {
$groups = array($groups);
}
if (is_numeric($courses)) {
$courses = array($courses);
}
if (is_numeric($categories)) {
$categories = array($categories);
}
// Array of filter conditions. To be concatenated by the OR operator.
$filters = [];
// User filter.
if (is_array($users) && !empty($users)) {
// Events from a number of users.
list($insqlusers, $inparamsusers) = $DB->get_in_or_equal($users, SQL_PARAMS_NAMED);
$filters[] = "(e.userid $insqlusers AND e.courseid = 0 AND e.groupid = 0 AND e.categoryid = 0)";
$params = array_merge($params, $inparamsusers);
} else if ($users === true) {
// Events from ALL users.
$filters[] = "(e.userid != 0 AND e.courseid = 0 AND e.groupid = 0 AND e.categoryid = 0)";
}
// Boolean false (no users at all): We don't need to do anything.
// Group filter.
if (is_array($groups) && !empty($groups)) {
// Events from a number of groups.
list($insqlgroups, $inparamsgroups) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED);
$filters[] = "e.groupid $insqlgroups";
$params = array_merge($params, $inparamsgroups);
} else if ($groups === true) {
// Events from ALL groups.
$filters[] = "e.groupid != 0";
}
// Boolean false (no groups at all): We don't need to do anything.
// Course filter.
if (is_array($courses) && !empty($courses)) {
list($insqlcourses, $inparamscourses) = $DB->get_in_or_equal($courses, SQL_PARAMS_NAMED);
$filters[] = "(e.groupid = 0 AND e.courseid $insqlcourses)";
$params = array_merge($params, $inparamscourses);
} else if ($courses === true) {
// Events from ALL courses.
$filters[] = "(e.groupid = 0 AND e.courseid != 0)";
}
// Category filter.
if (is_array($categories) && !empty($categories)) {
list($insqlcategories, $inparamscategories) = $DB->get_in_or_equal($categories, SQL_PARAMS_NAMED);
$filters[] = "(e.groupid = 0 AND e.courseid = 0 AND e.categoryid $insqlcategories)";
$params = array_merge($params, $inparamscategories);
} else if ($categories === true) {
// Events from ALL categories.
$filters[] = "(e.groupid = 0 AND e.courseid = 0 AND e.categoryid != 0)";
}
// Security check: if, by now, we have NOTHING in $whereclause, then it means
// that NO event-selecting clauses were defined. Thus, we won't be returning ANY
// events no matter what. Allowing the code to proceed might return a completely
// valid query with only time constraints, thus selecting ALL events in that time frame!
if (empty($filters)) {
return array();
}
// Build our clause for the filters.
$filterclause = implode(' OR ', $filters);
// Array of where conditions for our query. To be concatenated by the AND operator.
$whereconditions[] = "($filterclause)";
// Show visible only.
if ($ignorehidden) {
$whereconditions[] = "(e.visible = 1)";
}
// Build the main query's WHERE clause.
$whereclause = implode(' AND ', $whereconditions);
// Build SQL subquery and conditions for filtered events based on priorities.
$subquerytimeconditions = array_filter($whereconditions, function($condition) {
return (strpos($condition, 'time') !== false);
});
$subquerywhere = '';
$subqueryconditions = [];
$subqueryparams = [];
$allusercourses = [];
if (is_array($users) && !empty($users)) {
$userrecords = $DB->get_records_sql("SELECT * FROM {user} WHERE id $insqlusers", $inparamsusers);
foreach ($userrecords as $userrecord) {
// Get the user's courses. Otherwise, get the default courses being shown by the calendar.
$usercourses = calendar_get_default_courses(null, 'id, category, groupmode, groupmodeforce',
false, $userrecord->id);
// Set calendar filters.
list($usercourses, $usergroups, $user) = calendar_set_filters($usercourses, true, $userrecord);
$filteredcourses = is_array($courses) ? $courses : [$courses];
$filteredcourses = array_filter($usercourses, function($course) use ($filteredcourses) {
return in_array($course, $filteredcourses);
});
$allusercourses = array_merge($allusercourses, $filteredcourses);
// Flag to indicate whether the query needs to exclude group overrides.
$viewgroupsonly = false;
if ($user) {
// Set filter condition for the user's events.
// Even though $user is a single scalar, we still use get_in_or_equal() because we are inside a loop.
list($inusers, $inuserparams) = $DB->get_in_or_equal($user, SQL_PARAMS_NAMED);
$condition = "(ev.userid $inusers AND ev.courseid = 0 AND ev.groupid = 0 AND ev.categoryid = 0)";
$subqueryconditions[] = $condition;
$subqueryparams = array_merge($subqueryparams, $inuserparams);
foreach ($usercourses as $courseid) {
if (has_capability('moodle/site:accessallgroups', \context_course::instance($courseid), $userrecord)) {
$usergroupmembership = groups_get_all_groups($courseid, $user, 0, 'g.id');
if (count($usergroupmembership) == 0) {
$viewgroupsonly = true;
break;
}
}
}
}
// Set filter condition for the user's group events.
if ($usergroups === true || $viewgroupsonly) {
// Fetch group events, but not group overrides.
$groupconditions = "(ev.groupid != 0 AND ev.eventtype = 'group')";
} else if (!empty($usergroups)) {
// Fetch group events and group overrides.
list($inusergroups, $inusergroupparams) = $DB->get_in_or_equal($usergroups, SQL_PARAMS_NAMED);
$groupconditions = "(ev.groupid $inusergroups)";
$subqueryparams = array_merge($subqueryparams, $inusergroupparams);
}
}
} else if ($users === true) {
// Events from ALL users.
$subqueryconditions[] = "(ev.userid != 0 AND ev.courseid = 0 AND ev.groupid = 0 AND ev.categoryid = 0)";
if (is_array($groups)) {
// Events from a number of groups.
list($insqlgroups, $inparamsgroups) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED);
$subqueryconditions[] = "ev.groupid $insqlgroups";
$subqueryparams = array_merge($subqueryparams, $inparamsgroups);
} else if ($groups === true) {
// Events from ALL groups.
$subqueryconditions[] = "ev.groupid != 0";
}
if ($courses === true) {
// ALL course events. It's not needed to worry about users' access as $users = true.
$subqueryconditions[] = "(ev.groupid = 0 AND ev.courseid != 0 AND ev.categoryid = 0)";
}
}
// Get courses to be used for the subquery.
$subquerycourses = [];
if (is_array($courses)) {
$subquerycourses = $courses;
}
// Merge with user courses, if necessary.
if (!empty($allusercourses)) {
$subquerycourses = array_merge($subquerycourses, $allusercourses);
// Make sure we remove duplicate values.
$subquerycourses = array_unique($subquerycourses);
}
// Set subquery filter condition for the courses.
if (!empty($subquerycourses)) {
list($incourses, $incoursesparams) = $DB->get_in_or_equal($subquerycourses, SQL_PARAMS_NAMED);
if (isset($groupconditions)) {
$groupconditions = $groupconditions." OR ";
} else {
$groupconditions = '';
}
$condition = "($groupconditions(ev.groupid = 0 AND ev.courseid $incourses AND ev.categoryid = 0))";
$subtimesparams = [];
if (!empty($subquerytimeconditions)) {
$subtimes = $this->subquerytimeconditions("courses", $subquerytimeconditions, $whereparams);
$condition .= $subtimes['where'];
$subtimesparams = $subtimes['params'];
}
$subqueryconditions[] = $condition;
$subqueryparams = array_merge($subqueryparams, $incoursesparams, $subtimesparams);
}
// Set subquery filter condition for the categories.
if ($categories === true) {
$subqueryconditions[] = "(ev.categoryid != 0 AND ev.eventtype = 'category')";
} else if (!empty($categories)) {
list($incategories, $incategoriesparams) = $DB->get_in_or_equal($categories, SQL_PARAMS_NAMED);
$condition = "(ev.groupid = 0 AND ev.courseid = 0 AND ev.categoryid $incategories)";
$subtimesparams = [];
if (!empty($subquerytimeconditions)) {
$subtimes = $this->subquerytimeconditions("cats", $subquerytimeconditions, $whereparams);
$condition .= $subtimes['where'];
$subtimesparams = $subtimes['params'];
}
$subqueryconditions[] = $condition;
$subqueryparams = array_merge($subqueryparams, $incategoriesparams, $subtimesparams);
}
// Build the WHERE condition for the sub-query.
if (!empty($subqueryconditions)) {
$unionstartquery = "SELECT modulename, instance, eventtype, priority
FROM {event} ev
WHERE ";
$subqueryunion = '('.$unionstartquery . implode(" UNION $unionstartquery ", $subqueryconditions).')';
} else {
$subqueryunion = '{event}';
}
// Merge subquery parameters to the parameters of the main query.
if (!empty($subqueryparams)) {
$params = array_merge($params, $subqueryparams);
}
// Sub-query that fetches the list of unique events that were filtered based on priority.
$subquery = "SELECT ev.modulename,
ev.instance,
ev.eventtype,
MIN(ev.priority) as priority
FROM $subqueryunion ev
GROUP BY ev.modulename, ev.instance, ev.eventtype";
// Build the main query.
$sql = "SELECT e.*, c.fullname AS coursefullname, c.shortname AS courseshortname
FROM {event} e
INNER JOIN ($subquery) fe
ON e.modulename = fe.modulename
AND e.instance = fe.instance
AND e.eventtype = fe.eventtype
AND (e.priority = fe.priority OR (e.priority IS NULL AND fe.priority IS NULL))
LEFT JOIN {modules} m
ON e.modulename = m.name
LEFT JOIN {course} c
ON c.id = e.courseid
WHERE (m.visible = 1 OR m.visible IS NULL) AND $whereclause
ORDER BY " . ($ordersql ? $ordersql : "e.timestart");
if (!empty($whereparams)) {
$params = array_merge($params, $whereparams);
}
$events = $DB->get_records_sql($sql, $params, $offset, $limitnum);
return $events === false ? [] : $events;
}
/**
* Returns a query fragment and params, with time constraints applied
*
* @param string $prefix
* @param array $conditions
* @param array $params
* @return array [<where>, <params>]
*/
protected function subquerytimeconditions(string $prefix, array $conditions, array $params): array {
$outwhere = '';
$outparams = [];
// Most specific to least specific.
$timeparams = ['timefromid', 'timefrom3', 'timefrom2', 'timefrom1', 'timefrom', 'timetoid', 'timeto2', 'timeto1', 'timeto'];
$whereconditions = [];
foreach ($conditions as $condition) {
$where = $condition;
// This query has been borrowed from the main WHERE clause, so the alias needs to be renamed to match the union.
$where = str_replace('e.id', 'ev.id', $where);
foreach ($timeparams as $timeparam) {
if (isset($params[$timeparam])) {
$where = str_replace(":{$timeparam}", ":{$prefix}{$timeparam}", $where);
$outparams["{$prefix}{$timeparam}"] = $params[$timeparam];
}
}
$whereconditions[] = $where;
}
if (count($whereconditions) > 0) {
$outwhere = ' AND ' . implode(' AND ', $whereconditions);
}
return ['where' => $outwhere, 'params' => $outparams];
}
}
@@ -0,0 +1,63 @@
<?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/>.
/**
* Raw event strategy retrieval interface.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\strategies;
defined('MOODLE_INTERNAL') || die();
/**
* Interface for an raw event retrival strategy class.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface raw_event_retrieval_strategy_interface {
/**
* Retrieve raw calendar event records from the DB.
*
* @param array|null $usersfilter Array of users to retrieve events for.
* @param array|null $groupsfilter Array of groups to retrieve events for.
* @param array|null $coursesfilter Array of courses to retrieve events for.
* @param array|null $categoriesfilter Array of categories to retrieve events for.
* @param array|null $whereconditions Array of where conditions to restrict results.
* @param array|null $whereparams Array of parameters for $whereconditions.
* @param string|null $ordersql SQL to order results.
* @param int|null $offset Amount to offset results by.
* @param int $limitnum Return at most this many results.
* @param bool $ignorehidden True to ignore hidden events. False to include them.
* @return \stdClass[] Array of event records.
*/
public function get_raw_events(
array $usersfilter = null,
array $groupsfilter = null,
array $coursesfilter = null,
array $categoriesfilter = null,
array $whereconditions = null,
array $whereparams = null,
$ordersql = null,
$offset = null,
$limitnum = 40,
$ignorehidden = true
);
}
@@ -0,0 +1,93 @@
<?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/>.
/**
* Class representing an action a user should take.
*
* @package core_calendar
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\value_objects;
defined('MOODLE_INTERNAL') || die();
use core_calendar\local\event\entities\action_interface;
/**
* Class representing an action a user should take
*
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class action implements action_interface {
/**
* @var string $name The action's name.
*/
protected $name;
/**
* @var \moodle_url $url The action's URL.
*/
protected $url;
/**
* @var int $itemcount How many items there are to action.
*/
protected $itemcount;
/**
* @var bool $actionable Whether or not the event is currently actionable.
*/
protected $actionable;
/**
* Constructor.
*
* @param string $name The action's name.
* @param \moodle_url $url The action's URL.
* @param int $itemcount How many items there are to action.
* @param bool $actionable Whether or not the event is currently actionable.
*/
public function __construct(
$name,
\moodle_url $url,
$itemcount,
$actionable
) {
$this->name = $name;
$this->url = $url;
$this->itemcount = $itemcount;
$this->actionable = $actionable;
}
public function get_name() {
return $this->name;
}
public function get_url() {
return $this->url;
}
public function get_item_count() {
return $this->itemcount;
}
public function is_actionable() {
return $this->actionable;
}
}
@@ -0,0 +1,49 @@
<?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/>.
/**
* Description value object interface.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\value_objects;
defined('MOODLE_INTERNAL') || die();
/**
* Interface for a description value object.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface description_interface {
/**
* Get the description's text.
*
* @return string The description's text.
*/
public function get_value();
/**
* Get the description's format.
*
* @return int The description's format.
*/
public function get_format();
}
@@ -0,0 +1,64 @@
<?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/>.
/**
* Description value object.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\value_objects;
defined('MOODLE_INTERNAL') || die();
/**
* Class representing a description value object.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class event_description implements description_interface {
/**
* @var string $value The description's text.
*/
protected $value;
/**
* @var int $format The description's format.
*/
protected $format;
/**
* Constructor.
*
* @param string $value The description's value.
* @param int $format The description's format.
*/
public function __construct($value, $format) {
$this->value = $value;
$this->format = $format;
}
public function get_value() {
return $this->value;
}
public function get_format() {
return $this->format;
}
}
@@ -0,0 +1,112 @@
<?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/>.
/**
* Event times class.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\value_objects;
defined('MOODLE_INTERNAL') || die();
/**
* Class representing event times.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class event_times implements times_interface {
/**
* @var \DateTimeImmutable $start Event start time.
*/
protected $start;
/**
* @var \DateTimeImmutable $end Event end time.
*/
protected $end;
/**
* @var \DateTimeImmutable $sort Time used to sort events.
*/
protected $sort;
/**
* @var \DateTimeImmutable $modified Time event was last modified.
*/
protected $modified;
/**
* @var \DateTimeImmutable $usermidnight User midnight for the event.
*/
protected $usermidnight;
/**
* Constructor.
*
* @param \DateTimeImmutable $start Event start time.
* @param \DateTimeImmutable $end Event end time.
* @param \DateTimeImmutable $sort Date used to sort events.
* @param \DateTimeImmutable $modified Time event was last updated.
* @param \DateTimeImmutable $usermidnight User midnight for the event.
*/
public function __construct(
\DateTimeImmutable $start,
\DateTimeImmutable $end,
\DateTimeImmutable $sort,
\DateTimeImmutable $modified,
\DateTimeImmutable $usermidnight
) {
$this->start = $start;
$this->end = $end;
$this->sort = $sort;
$this->modified = $modified;
$this->usermidnight = $usermidnight;
}
public function get_start_time() {
return $this->start;
}
public function get_end_time() {
return $this->end;
}
public function get_duration() {
return $this->end->diff($this->start);
}
public function get_modified_time() {
return $this->modified;
}
public function get_sort_time() {
return $this->sort;
}
/**
* Getter for usermidnight.
*
* @return \DateTimeImmutable
*/
public function get_usermidnight_time() {
return $this->usermidnight;
}
}
@@ -0,0 +1,77 @@
<?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/>.
/**
* Times interface.
*
* @package core_calendar
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\local\event\value_objects;
defined('MOODLE_INTERNAL') || die();
/**
* Interface for various times.
*
* @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface times_interface {
/**
* Get the start time.
*
* @return \DateTimeImmutable
*/
public function get_start_time();
/**
* Get the end time.
*
* @return \DateTimeImmutable
*/
public function get_end_time();
/**
* Get the duration (the time between start and end).
*
* @return \DateInterval
*/
public function get_duration();
/**
* Get the sort time.
*
* @return \DateTimeImmutable
*/
public function get_sort_time();
/**
* Get the modified time.
*
* @return \DateTimeImmutable
*/
public function get_modified_time();
/**
* Get the user midnight time.
*
* @return \DateTimeImmutable
*/
public function get_usermidnight_time();
}
@@ -0,0 +1,54 @@
<?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/>.
/**
* Class to display collection select for the refresh interval.
*
* @package core_calendar
* @copyright 2021 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\output;
use core\output\inplace_editable;
class refreshintervalcollection extends inplace_editable {
/**
* Constructor.
*
* @param \stdClass $subscription Subscription object
*/
public function __construct(\stdClass $subscription) {
$collection = calendar_get_pollinterval_choices();
parent::__construct('core_calendar', 'refreshinterval', $subscription->id, true, null, $subscription->pollinterval, null,
get_string('pollinterval', 'calendar'));
$this->set_type_select($collection);
}
public static function update(int $subscriptionid, int $pollinterval) {
if (calendar_can_edit_subscription($subscriptionid)) {
$subscription = calendar_get_subscription($subscriptionid);
$subscription->pollinterval = $pollinterval;
calendar_update_subscription($subscription);
$tmpl = new self($subscription);
return $tmpl;
} else {
throw new \moodle_exception('nopermissions', 'error', '', get_string('managesubscriptions', 'calendar'));
}
}
}
+704
View File
@@ -0,0 +1,704 @@
<?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/>.
/**
* Privacy class for requesting user data.
*
* @package core_calendar
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_calendar\privacy;
defined('MOODLE_INTERNAL') || die();
use \core_privacy\local\metadata\collection;
use \core_privacy\local\request\approved_contextlist;
use \core_privacy\local\request\context;
use \core_privacy\local\request\contextlist;
use \core_privacy\local\request\transform;
use \core_privacy\local\request\writer;
use \core_privacy\local\request\userlist;
use \core_privacy\local\request\approved_userlist;
/**
* Privacy Subsystem for core_calendar implementing metadata, plugin, and user_preference providers.
*
* @package core_calendar
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
\core_privacy\local\metadata\provider,
\core_privacy\local\request\plugin\provider,
\core_privacy\local\request\core_userlist_provider,
\core_privacy\local\request\user_preference_provider
{
/**
* Provides meta data that is stored about a user with core_calendar.
*
* @param collection $collection A collection of meta data items to be added to.
* @return collection Returns the collection of metadata.
*/
public static function get_metadata(collection $collection): collection {
// The calendar 'event' table contains user data.
$collection->add_database_table(
'event',
[
'name' => 'privacy:metadata:calendar:event:name',
'description' => 'privacy:metadata:calendar:event:description',
'eventtype' => 'privacy:metadata:calendar:event:eventtype',
'timestart' => 'privacy:metadata:calendar:event:timestart',
'timeduration' => 'privacy:metadata:calendar:event:timeduration',
],
'privacy:metadata:calendar:event'
);
// The calendar 'event_subscriptions' table contains user data.
$collection->add_database_table(
'event_subscriptions',
[
'name' => 'privacy:metadata:calendar:event_subscriptions:name',
'url' => 'privacy:metadata:calendar:event_subscriptions:url',
'eventtype' => 'privacy:metadata:calendar:event_subscriptions:eventtype',
],
'privacy:metadata:calendar:event_subscriptions'
);
// The calendar user preference setting 'calendar_savedflt'.
$collection->add_user_preference(
'calendar_savedflt',
'privacy:metadata:calendar:preferences:calendar_savedflt'
);
return $collection;
}
/**
* Get the list of contexts that contain calendar user information for the specified user.
*
* @param int $userid The user to search.
* @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
$contextlist = new contextlist();
// Calendar Events can exist at Site, Course Category, Course, Course Group, User, or Course Modules contexts.
$params = [
'sitecontext' => CONTEXT_SYSTEM,
'categorycontext' => CONTEXT_COURSECAT,
'coursecontext' => CONTEXT_COURSE,
'groupcontext' => CONTEXT_COURSE,
'usercontext' => CONTEXT_USER,
'cuserid' => $userid,
'modulecontext' => CONTEXT_MODULE,
'muserid' => $userid
];
// Get contexts of Calendar Events for the owner.
$sql = "SELECT ctx.id
FROM {context} ctx
JOIN {event} e ON
(e.eventtype = 'site' AND ctx.contextlevel = :sitecontext) OR
(e.categoryid = ctx.instanceid AND e.eventtype = 'category' AND ctx.contextlevel = :categorycontext) OR
(e.courseid = ctx.instanceid AND e.eventtype = 'course' AND ctx.contextlevel = :coursecontext) OR
(e.courseid = ctx.instanceid AND e.eventtype = 'group' AND ctx.contextlevel = :groupcontext) OR
(e.userid = ctx.instanceid AND e.eventtype = 'user' AND ctx.contextlevel = :usercontext)
WHERE e.userid = :cuserid";
$contextlist->add_from_sql($sql, $params);
$sql = "SELECT ctx.id
FROM {context} ctx
JOIN {course_modules} cm ON cm.id = ctx.instanceid AND ctx.contextlevel = :modulecontext
JOIN {modules} m ON m.id = cm.module
JOIN {event} e ON e.modulename = m.name AND e.courseid = cm.course AND e.instance = cm.instance
WHERE e.userid = :muserid";
$contextlist->add_from_sql($sql, $params);
// Calendar Subscriptions can exist at Site, Course Category, Course, Course Group, or User contexts.
$params = [
'sitecontext' => CONTEXT_SYSTEM,
'categorycontext' => CONTEXT_COURSECAT,
'coursecontext' => CONTEXT_COURSE,
'groupcontext' => CONTEXT_COURSE,
'usercontext' => CONTEXT_USER,
'userid' => $userid
];
// Get contexts for Calendar Subscriptions for the owner.
$sql = "SELECT ctx.id
FROM {context} ctx
JOIN {event_subscriptions} s ON
(s.eventtype = 'site' AND ctx.contextlevel = :sitecontext) OR
(s.categoryid = ctx.instanceid AND s.eventtype = 'category' AND ctx.contextlevel = :categorycontext) OR
(s.courseid = ctx.instanceid AND s.eventtype = 'course' AND ctx.contextlevel = :coursecontext) OR
(s.courseid = ctx.instanceid AND s.eventtype = 'group' AND ctx.contextlevel = :groupcontext) OR
(s.userid = ctx.instanceid AND s.eventtype = 'user' AND ctx.contextlevel = :usercontext)
WHERE s.userid = :userid";
$contextlist->add_from_sql($sql, $params);
// Return combined contextlist for Calendar Events & Calendar Subscriptions.
return $contextlist;
}
/**
* Get the list of users within a specific context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*/
public static function get_users_in_context(userlist $userlist) {
global $DB;
$context = $userlist->get_context();
// Calendar Events can exist at Site (CONTEXT_SYSTEM), Course Category (CONTEXT_COURSECAT),
// Course and Course Group (CONTEXT_COURSE), User (CONTEXT_USER), or Course Modules (CONTEXT_MODULE) contexts.
if ($context->contextlevel == CONTEXT_MODULE) {
$params = ['cmid' => $context->instanceid];
$sql = "SELECT e.userid
FROM {course_modules} cm
JOIN {modules} m ON m.id = cm.module
JOIN {event} e ON e.modulename = m.name AND e.courseid = cm.course AND e.instance = cm.instance
WHERE cm.id = :cmid";
$userlist->add_from_sql('userid', $sql, $params);
} else if ($context->contextlevel == CONTEXT_SYSTEM) {
// Get contexts of Calendar Events for the owner.
$sql = "SELECT userid FROM {event} WHERE eventtype = 'site'";
$userlist->add_from_sql('userid', $sql, []);
// Get contexts for Calendar Subscriptions for the owner.
$sql = "SELECT userid FROM {event_subscriptions} WHERE eventtype = 'site'";
$userlist->add_from_sql('userid', $sql, []);
} else if (in_array($context->contextlevel, [CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_USER])) {
$eventfields = [
CONTEXT_COURSECAT => 'categoryid',
CONTEXT_COURSE => 'courseid',
CONTEXT_USER => 'userid'
];
$eventfield = $eventfields[$context->contextlevel];
$eventtypes = [
CONTEXT_COURSECAT => 'category',
CONTEXT_COURSE => ['course' , 'group'],
CONTEXT_USER => 'user'
];
list($eventtypesql, $eventtypeparams) = $DB->get_in_or_equal($eventtypes[$context->contextlevel], SQL_PARAMS_NAMED);
$params = $eventtypeparams + ['instanceid' => $context->instanceid];
// Get contexts of Calendar Events for the owner.
$sql = "SELECT userid
FROM {event}
WHERE eventtype $eventtypesql
AND $eventfield = :instanceid";
$userlist->add_from_sql('userid', $sql, $params);
// Get contexts for Calendar Subscriptions for the owner.
$sql = "SELECT userid
FROM {event_subscriptions}
WHERE eventtype $eventtypesql
AND $eventfield = :instanceid";
$userlist->add_from_sql('userid', $sql, $params);
}
}
/**
* Export all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts to export information for.
*/
public static function export_user_data(approved_contextlist $contextlist) {
if (empty($contextlist)) {
return;
}
self::export_user_calendar_event_data($contextlist);
self::export_user_calendar_subscription_data($contextlist);
}
/**
* Export all user preferences for the plugin.
*
* @param int $userid The userid of the user whose data is to be exported.
*/
public static function export_user_preferences(int $userid) {
$calendarsavedflt = get_user_preferences('calendar_savedflt', null, $userid);
if (null !== $calendarsavedflt) {
writer::export_user_preference(
'core_calendar',
'calendarsavedflt',
$calendarsavedflt,
get_string('privacy:metadata:calendar:preferences:calendar_savedflt', 'core_calendar')
);
}
}
/**
* Delete all Calendar Event and Calendar Subscription data for all users in the specified context.
*
* @param \context $context Transform the specific context to delete data for.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
// Delete all Calendar Events in the specified context in batches.
if ($eventids = array_keys(self::get_calendar_event_ids_by_context($context))) {
self::delete_batch_records('event', 'id', $eventids);
}
// Delete all Calendar Subscriptions in the specified context in batches.
if ($subscriptionids = array_keys(self::get_calendar_subscription_ids_by_context($context))) {
self::delete_batch_records('event_subscriptions', 'id', $subscriptionids);
}
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
$context = $userlist->get_context();
$userids = $userlist->get_userids();
$allowedcontexts = [
CONTEXT_SYSTEM,
CONTEXT_COURSECAT,
CONTEXT_COURSE,
CONTEXT_MODULE,
CONTEXT_USER
];
if (!in_array($context->contextlevel, $allowedcontexts)) {
return;
}
if (empty($userids)) {
return;
}
// Delete all Calendar Events in the specified context in batches.
if ($eventids = array_keys(self::get_calendar_event_ids_by_context($context, $userids))) {
self::delete_batch_records('event', 'id', $eventids);
}
// Delete all Calendar Subscriptions in the specified context in batches.
if ($subscriptionids = array_keys(self::get_calendar_subscription_ids_by_context($context, $userids))) {
self::delete_batch_records('event_subscriptions', 'id', $subscriptionids);
}
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
if (empty($contextlist)) {
return;
}
// Delete all Calendar Events for the owner and specified contexts in batches.
$eventdetails = self::get_calendar_event_details_by_contextlist($contextlist);
$eventids = [];
foreach ($eventdetails as $eventdetail) {
$eventids[] = $eventdetail->eventid;
}
$eventdetails->close();
self::delete_batch_records('event', 'id', $eventids);
// Delete all Calendar Subscriptions for the owner and specified contexts in batches.
$subscriptiondetails = self::get_calendar_subscription_details_by_contextlist($contextlist);
$subscriptionids = [];
foreach ($subscriptiondetails as $subscriptiondetail) {
$subscriptionids[] = $subscriptiondetail->subscriptionid;
}
$subscriptiondetails->close();
self::delete_batch_records('event_subscriptions', 'id', $subscriptionids);
}
/**
* Helper function to export Calendar Events data by a User's contextlist.
*
* @param approved_contextlist $contextlist
* @throws \coding_exception
*/
protected static function export_user_calendar_event_data(approved_contextlist $contextlist) {
// Calendar Events can exist at Site, Course Category, Course, Course Group, User, or Course Modules contexts.
$eventdetails = self::get_calendar_event_details_by_contextlist($contextlist);
// Multiple Calendar Events of the same eventtype and time can exist for a context, so collate them for export.
$eventrecords = [];
foreach ($eventdetails as $eventdetail) {
// Create an array key based on the contextid, eventtype, and time.
$key = $eventdetail->contextid . $eventdetail->eventtype . $eventdetail->timestart;
if (array_key_exists($key, $eventrecords) === false) {
$eventrecords[$key] = [ $eventdetail ];
} else {
$eventrecords[$key] = array_merge($eventrecords[$key], [$eventdetail]);
}
}
$eventdetails->close();
// Export Calendar Event data.
foreach ($eventrecords as $eventrecord) {
$index = (count($eventrecord) > 1) ? 1 : 0;
foreach ($eventrecord as $event) {
// Export the events using the structure Calendar/Events/{datetime}/{eventtype}-event.json.
$subcontexts = [
get_string('calendar', 'calendar'),
get_string('events', 'calendar'),
date('c', $event->timestart)
];
$name = $event->eventtype . '-event';
// Use name {eventtype}-event-{index}.json if multiple eventtypes and time exists at the same context.
if ($index != 0) {
$name .= '-' . $index;
$index++;
}
$eventdetails = (object) [
'name' => $event->name,
'description' => $event->description,
'location' => $event->location,
'eventtype' => $event->eventtype,
'timestart' => transform::datetime($event->timestart),
'timeduration' => $event->timeduration
];
$context = \context::instance_by_id($event->contextid);
writer::with_context($context)->export_related_data($subcontexts, $name, $eventdetails);
}
}
}
/**
* Helper function to export Calendar Subscriptions data by a User's contextlist.
*
* @param approved_contextlist $contextlist
* @throws \coding_exception
*/
protected static function export_user_calendar_subscription_data(approved_contextlist $contextlist) {
// Calendar Subscriptions can exist at Site, Course Category, Course, Course Group, or User contexts.
$subscriptiondetails = self::get_calendar_subscription_details_by_contextlist($contextlist);
// Multiple Calendar Subscriptions of the same eventtype can exist for a context, so collate them for export.
$subscriptionrecords = [];
foreach ($subscriptiondetails as $subscriptiondetail) {
// Create an array key based on the contextid and eventtype.
$key = $subscriptiondetail->contextid . $subscriptiondetail->eventtype;
if (array_key_exists($key, $subscriptionrecords) === false) {
$subscriptionrecords[$key] = [ $subscriptiondetail ];
} else {
$subscriptionrecords[$key] = array_merge($subscriptionrecords[$key], [$subscriptiondetail]);
}
}
$subscriptiondetails->close();
// Export Calendar Subscription data.
foreach ($subscriptionrecords as $subscriptionrecord) {
$index = (count($subscriptionrecord) > 1) ? 1 : 0;
foreach ($subscriptionrecord as $subscription) {
// Export the events using the structure Calendar/Subscriptions/{eventtype}-subscription.json.
$subcontexts = [
get_string('calendar', 'calendar'),
get_string('subscriptions', 'calendar')
];
$name = $subscription->eventtype . '-subscription';
// Use name {eventtype}-subscription-{index}.json if multiple eventtypes exists at the same context.
if ($index != 0) {
$name .= '-' . $index;
$index++;
}
$context = \context::instance_by_id($subscription->contextid);
writer::with_context($context)->export_related_data($subcontexts, $name, $subscription);
}
}
}
/**
* Helper function to return all Calendar Event id results for a specified context and optionally
* included user list.
*
* @param \context $context
* @param array $userids
* @return array|null
* @throws \dml_exception
*/
protected static function get_calendar_event_ids_by_context(\context $context, $userids = array()) {
global $DB;
// Calendar Events can exist at Site (CONTEXT_SYSTEM), Course Category (CONTEXT_COURSECAT),
// Course and Course Group (CONTEXT_COURSE), User (CONTEXT_USER), or Course Modules (CONTEXT_MODULE) contexts.
if (!in_array($context->contextlevel, [CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_USER, CONTEXT_MODULE])) {
return [];
}
$whereusersql = '';
$userparams = array();
if (!empty($userids)) {
list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
$whereusersql = "AND e.userid {$usersql}";
}
if ($context->contextlevel == CONTEXT_MODULE) { // Course Module events.
$params = ['cmid' => $context->instanceid];
// Get Calendar Events for the specified Course Module context.
$sql = "SELECT DISTINCT e.id AS eventid
FROM {course_modules} cm
JOIN {modules} m ON m.id = cm.module
JOIN {event} e ON e.modulename = m.name AND e.courseid = cm.course AND e.instance = cm.instance
WHERE cm.id = :cmid
$whereusersql";
} else if ($context->contextlevel == CONTEXT_SYSTEM) { // Site events.
$params = [];
$sql = "SELECT DISTINCT e.id AS eventid
FROM {event} e
WHERE e.eventtype = 'site'
$whereusersql";
} else { // The rest.
$eventfields = [
CONTEXT_COURSECAT => 'categoryid',
CONTEXT_COURSE => 'courseid',
CONTEXT_USER => 'userid'
];
$eventfield = $eventfields[$context->contextlevel];
$eventtypes = [
CONTEXT_COURSECAT => 'category',
CONTEXT_COURSE => ['course' , 'group'],
CONTEXT_USER => 'user'
];
list($eventtypesql, $eventtypeparams) = $DB->get_in_or_equal($eventtypes[$context->contextlevel], SQL_PARAMS_NAMED);
$params = $eventtypeparams + ['instanceid' => $context->instanceid];
// Get Calendar Events for the specified Moodle context.
$sql = "SELECT DISTINCT e.id AS eventid
FROM {event} e
WHERE e.eventtype $eventtypesql
AND e.{$eventfield} = :instanceid
$whereusersql";
}
$params += $userparams;
return $DB->get_records_sql($sql, $params);
}
/**
* Helper function to return all Calendar Subscription id results for a specified context and optionally
* included user list.
*
* @param \context $context
* @param array $userids
* @return array
* @throws \dml_exception
*/
protected static function get_calendar_subscription_ids_by_context(\context $context, $userids = array()) {
global $DB;
// Calendar Subscriptions can exist at Site (CONTEXT_SYSTEM), Course Category (CONTEXT_COURSECAT),
// Course and Course Group (CONTEXT_COURSE), or User (CONTEXT_USER) contexts.
if (!in_array($context->contextlevel, [CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_USER])) {
return [];
}
$whereusersql = '';
$userparams = array();
if (!empty($userids)) {
list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
$whereusersql = "AND s.userid {$usersql}";
}
if ($context->contextlevel == CONTEXT_SYSTEM) {
$params = [];
// Get Calendar Subscriptions for the system context.
$sql = "SELECT DISTINCT s.id AS subscriptionid
FROM {event_subscriptions} s
WHERE s.eventtype = 'site'
$whereusersql";
} else {
$eventfields = [
CONTEXT_COURSECAT => 'categoryid',
CONTEXT_COURSE => 'courseid',
CONTEXT_USER => 'userid'
];
$eventfield = $eventfields[$context->contextlevel];
$eventtypes = [
CONTEXT_COURSECAT => 'category',
CONTEXT_COURSE => ['course' , 'group'],
CONTEXT_USER => 'user'
];
list($eventtypesql, $eventtypeparams) = $DB->get_in_or_equal($eventtypes[$context->contextlevel], SQL_PARAMS_NAMED);
$params = $eventtypeparams + ['instanceid' => $context->instanceid];
// Get Calendar Subscriptions for the specified context.
$sql = "SELECT DISTINCT s.id AS subscriptionid
FROM {event_subscriptions} s
WHERE s.eventtype $eventtypesql
AND s.{$eventfield} = :instanceid
$whereusersql";
}
$params += $userparams;
return $DB->get_records_sql($sql, $params);
}
/**
* Helper function to return the Calendar Events for a given user and context list.
*
* @param approved_contextlist $contextlist
* @return \moodle_recordset
* @throws \coding_exception
* @throws \dml_exception
*/
protected static function get_calendar_event_details_by_contextlist(approved_contextlist $contextlist) {
global $DB;
$userid = $contextlist->get_user()->id;
list($contextsql1, $contextparams1) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
list($contextsql2, $contextparams2) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
// Calendar Events can exist at Site, Course Category, Course, Course Group, User, or Course Modules contexts.
$params = [
'sitecontext' => CONTEXT_SYSTEM,
'categorycontext' => CONTEXT_COURSECAT,
'coursecontext' => CONTEXT_COURSE,
'groupcontext' => CONTEXT_COURSE,
'usercontext' => CONTEXT_USER,
'cuserid' => $userid,
'modulecontext' => CONTEXT_MODULE,
'muserid' => $userid
];
$params += $contextparams1;
$params += $contextparams2;
// Get Calendar Events details for the approved contexts and the owner.
$sql = "SELECT ctxid as contextid,
details.id as eventid,
details.name as name,
details.description as description,
details.location as location,
details.eventtype as eventtype,
details.timestart as timestart,
details.timeduration as timeduration
FROM (
SELECT e.id AS id,
ctx.id AS ctxid
FROM {context} ctx
INNER JOIN {event} e ON
(e.eventtype = 'site' AND ctx.contextlevel = :sitecontext) OR
(e.categoryid = ctx.instanceid AND e.eventtype = 'category' AND ctx.contextlevel = :categorycontext) OR
(e.courseid = ctx.instanceid AND e.eventtype = 'course' AND ctx.contextlevel = :coursecontext) OR
(e.courseid = ctx.instanceid AND e.eventtype = 'group' AND ctx.contextlevel = :groupcontext) OR
(e.userid = ctx.instanceid AND e.eventtype = 'user' AND ctx.contextlevel = :usercontext)
WHERE e.userid = :cuserid
AND ctx.id {$contextsql1}
UNION
SELECT e.id AS id,
ctx.id AS ctxid
FROM {context} ctx
INNER JOIN {course_modules} cm ON cm.id = ctx.instanceid AND ctx.contextlevel = :modulecontext
INNER JOIN {modules} m ON m.id = cm.module
INNER JOIN {event} e ON e.modulename = m.name AND e.courseid = cm.course AND e.instance = cm.instance
WHERE e.userid = :muserid
AND ctx.id {$contextsql2}
) ids
JOIN {event} details ON details.id = ids.id
ORDER BY ids.id";
return $DB->get_recordset_sql($sql, $params);
}
/**
* Helper function to return the Calendar Subscriptions for a given user and context list.
*
* @param approved_contextlist $contextlist
* @return \moodle_recordset
* @throws \coding_exception
* @throws \dml_exception
*/
protected static function get_calendar_subscription_details_by_contextlist(approved_contextlist $contextlist) {
global $DB;
$user = $contextlist->get_user();
list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
$params = [
'sitecontext' => CONTEXT_SYSTEM,
'categorycontext' => CONTEXT_COURSECAT,
'coursecontext' => CONTEXT_COURSE,
'groupcontext' => CONTEXT_COURSE,
'usercontext' => CONTEXT_USER,
'userid' => $user->id
];
$params += $contextparams;
// Get Calendar Subscriptions for the approved contexts and the owner.
$sql = "SELECT DISTINCT
c.id as contextid,
s.id as subscriptionid,
s.name as name,
s.url as url,
s.eventtype as eventtype
FROM {context} c
INNER JOIN {event_subscriptions} s ON
(s.eventtype = 'site' AND c.contextlevel = :sitecontext) OR
(s.categoryid = c.instanceid AND s.eventtype = 'category' AND c.contextlevel = :categorycontext) OR
(s.courseid = c.instanceid AND s.eventtype = 'course' AND c.contextlevel = :coursecontext) OR
(s.courseid = c.instanceid AND s.eventtype = 'group' AND c.contextlevel = :groupcontext) OR
(s.userid = c.instanceid AND s.eventtype = 'user' AND c.contextlevel = :usercontext)
WHERE s.userid = :userid
AND c.id {$contextsql}";
return $DB->get_recordset_sql($sql, $params);
}
/**
* Helper function to delete records in batches in order to minimise amount of deletion queries.
*
* @param string $tablename The table name to delete from.
* @param string $field The table column field name to delete records by.
* @param array $values The table column field values to delete records by.
* @throws \dml_exception
*/
protected static function delete_batch_records($tablename, $field, $values) {
global $DB;
// Batch deletion with an upper limit of 2000 records to minimise the number of deletion queries.
$batchrecords = array_chunk($values, 2000);
foreach ($batchrecords as $batchrecord) {
$DB->delete_records_list($tablename, $field, $batchrecord);
}
}
}
File diff suppressed because it is too large Load Diff
+270
View File
@@ -0,0 +1,270 @@
<?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/>.
namespace core_calendar;
/**
* Defines functions used by calendar type plugins.
*
* This library provides a unified interface for calendar types.
*
* @package core_calendar
* @copyright 2008 onwards Foodle Group {@link http://foodle.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class type_base {
/**
* Returns the name of the calendar.
*
* This is the non-translated name, usually just
* the name of the calendar folder.
*
* @return string the calendar name
*/
abstract public function get_name();
/**
* Returns a list of all the possible days for all months.
*
* This is used to generate the select box for the days
* in the date selector elements. Some months contain more days
* than others so this function should return all possible days as
* we can not predict what month will be chosen (the user
* may have JS turned off and we need to support this situation in
* Moodle).
*
* @return array the days
*/
abstract public function get_days();
/**
* Returns a list of all the names of the months.
*
* @return array the month names
*/
abstract public function get_months();
/**
* Returns the minimum year for the calendar.
*
* @return int The minimum year
*/
abstract public function get_min_year();
/**
* Returns the maximum year for the calendar
*
* @return int The maximum year
*/
abstract public function get_max_year();
/**
* Returns an array of years.
*
* @param int $minyear
* @param int $maxyear
* @return array the years
*/
abstract public function get_years($minyear = null, $maxyear = null);
/**
* Returns a multidimensional array with information for day, month, year
* and the order they are displayed when selecting a date.
* The order in the array will be the order displayed when selecting a date.
* Override this function to change the date selector order.
*
* @param int $minyear The year to start with
* @param int $maxyear The year to finish with
* @return array Full date information
*/
abstract public function get_date_order($minyear = null, $maxyear = null);
/**
* Returns the number of days in a week.
*
* @return int the number of days
*/
abstract public function get_num_weekdays();
/**
* Returns an indexed list of all the names of the weekdays.
*
* The list starts with the index 0. Each index, representing a
* day, must be an array that contains the indexes 'shortname'
* and 'fullname'.
*
* @return array array of days
*/
abstract public function get_weekdays();
/**
* Returns the index of the starting week day.
*
* This may vary, for example in the Gregorian calendar, some may consider Monday
* as the start of the week, where as others may consider Sunday the start.
*
* @return int
*/
abstract public function get_starting_weekday();
/**
* Returns the index of the weekday for a specific calendar date.
*
* @param int $year
* @param int $month
* @param int $day
* @return int
*/
abstract public function get_weekday($year, $month, $day);
/**
* Returns the number of days in a given month.
*
*
* @param int $year
* @param int $month
* @return int the number of days
*/
abstract public function get_num_days_in_month($year, $month);
/**
* Get the previous month.
*
* @param int $year
* @param int $month
* @return array previous month and year
*/
abstract public function get_prev_month($year, $month);
/**
* Get the next month.
*
* @param int $year
* @param int $month
* @return array the following month and year
*/
abstract public function get_next_month($year, $month);
/**
* Returns a formatted string that represents a date in user time.
*
* @param int $time the timestamp in UTC, as obtained from the database
* @param string $format strftime format
* @param int|float|string $timezone the timezone to use
* {@link https://moodledev.io/docs/apis/subsystems/time#timezone}
* @param bool $fixday if true then the leading zero from %d is removed,
* if false then the leading zero is maintained
* @param bool $fixhour if true then the leading zero from %I is removed,
* if false then the leading zero is maintained
* @return string the formatted date/time
*/
abstract public function timestamp_to_date_string($time, $format, $timezone, $fixday, $fixhour);
/**
* Given a $time timestamp in GMT (seconds since epoch), returns an array that represents
* the date in user time.
*
* @param int $time timestamp in GMT
* @param float|int|string $timezone the timezone to use to calculate the time
* {@link https://moodledev.io/docs/apis/subsystems/time#timezone}
* @return array an array that represents the date in user time
*/
abstract public function timestamp_to_date_array($time, $timezone = 99);
/**
* Provided with a day, month, year, hour and minute in the specific
* calendar type convert it into the equivalent Gregorian date.
*
* @param int $year
* @param int $month
* @param int $day
* @param int $hour
* @param int $minute
* @return array the converted date
*/
abstract public function convert_to_gregorian($year, $month, $day, $hour = 0, $minute = 0);
/**
* Provided with a day, month, year, hour and minute in a Gregorian date
* convert it into the specific calendar type date.
*
* @param int $year
* @param int $month
* @param int $day
* @param int $hour
* @param int $minute
* @return array the converted date
*/
abstract public function convert_from_gregorian($year, $month, $day, $hour = 0, $minute = 0);
/**
* This return locale for windows os.
*
* @return string locale
*/
abstract public function locale_win_charset();
/**
* Provided with a day, month, year, hour and minute in the specific
* calendar type convert it into the equivalent Unix Time Stamp.
*
* @param int $year
* @param int $month
* @param int $day
* @param int $hour
* @param int $minute
* @return int timestamp
*/
public function convert_to_timestamp($year, $month, $day, $hour = 0, $minute = 0) {
$gregorianinfo = $this->convert_to_gregorian($year, $month, $day, $hour, $minute);
return make_timestamp(
$gregorianinfo['year'],
$gregorianinfo['month'],
$gregorianinfo['day'],
$gregorianinfo['hour'],
$gregorianinfo['minute'],
0);
}
/**
* Get the previous day.
*
* @param int $daytimestamp The day timestamp.
* @return int previous day timestamp
*/
public function get_prev_day($daytimestamp) {
$date = new \DateTime();
$date->setTimestamp($daytimestamp);
$date->modify('-1 day');
return $date->getTimestamp();
}
/**
* Get the next day.
*
* @param int $daytimestamp The day timestamp.
* @return int the following day
*/
public function get_next_day($daytimestamp) {
$date = new \DateTime();
$date->setTimestamp($daytimestamp);
$date->modify('+1 day');
return $date->getTimestamp();
}
}
+92
View File
@@ -0,0 +1,92 @@
<?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/>.
namespace core_calendar;
/**
* Class \core_calendar\type_factory.
*
* Factory class producing required subclasses of {@link \core_calendar\type_base}.
*
* @package core_calendar
* @copyright 2008 onwards Foodle Group {@link http://foodle.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class type_factory {
/**
* Returns an instance of the currently used calendar type.
*
* @param string|null $type the calendar type to use, if none provided use logic to determine
* @return \core_calendar\type_base the created calendar_type class
* @throws \coding_exception if the calendar type file could not be loaded
*/
public static function get_calendar_instance($type = null) {
if (is_null($type)) {
$type = self::get_calendar_type();
}
$class = "\\calendartype_$type\\structure";
// Ensure the calendar type exists. It may occur that a user has selected a calendar type, which was then
// deleted. If this happens we want to fall back on the Gregorian calendar type.
if (!class_exists($class)) {
$class = "\\calendartype_gregorian\\structure";
}
return new $class();
}
/**
* Returns a list of calendar typess available for use.
*
* @return array the list of calendar types
*/
public static function get_list_of_calendar_types() {
$calendars = array();
$calendardirs = \core_component::get_plugin_list('calendartype');
foreach ($calendardirs as $name => $location) {
$calendars[$name] = get_string('name', "calendartype_{$name}");
}
return $calendars;
}
/**
* Returns the current calendar type in use.
*
* @return string the current calendar type being used
*/
public static function get_calendar_type() {
global $CFG, $USER, $SESSION, $COURSE;
// Course calendartype can override all other settings for this page.
if (!empty($COURSE->id) and $COURSE->id != SITEID and !empty($COURSE->calendartype)) {
$return = $COURSE->calendartype;
} else if (!empty($SESSION->calendartype)) { // Session calendartype can override other settings.
$return = $SESSION->calendartype;
} else if (!empty($USER->calendartype)) {
$return = $USER->calendartype;
} else if (!empty($CFG->calendartype)) {
$return = $CFG->calendartype;
} else {
$return = 'gregorian';
}
return $return;
}
}