diff --git a/blocks/calendar_month/block_calendar_month.php b/blocks/calendar_month/block_calendar_month.php index ace839e7b66..a6172f93a3f 100644 --- a/blocks/calendar_month/block_calendar_month.php +++ b/blocks/calendar_month/block_calendar_month.php @@ -70,22 +70,25 @@ class block_calendar_month extends block_base { if ($issite) { // Being displayed at site level. This will cause the filter to fall back to auto-detecting // the list of courses it will be grabbing events from. - $filtercourse = calendar_get_default_courses(); + $filtercourse = \core_calendar\api::get_default_courses(); } else { // Forcibly filter events to include only those from the particular course we are in. $filtercourse = array($courseid => $this->page->course); } - list($courses, $group, $user) = calendar_set_filters($filtercourse); + list($courses, $group, $user) = \core_calendar\api::set_filters($filtercourse); if ($issite) { // For the front page. - $this->content->text .= calendar_get_mini($courses, $group, $user, false, false, 'frontpage', $courseid, $time); + $this->content->text .= \core_calendar\api::get_mini_calendar($courses, $group, $user, false, false, + 'frontpage', $courseid, $time); // No filters for now. } else { // For any other course. - $this->content->text .= calendar_get_mini($courses, $group, $user, false, false, 'course', $courseid, $time); + $this->content->text .= \core_calendar\api::get_mini_calendar($courses, $group, $user, false, false, + 'course', $courseid, $time); $this->content->text .= '

'.get_string('eventskey', 'calendar').'

'; - $this->content->text .= '
'.calendar_filter_controls($this->page->url).'
'; + $this->content->text .= '
' . + \core_calendar\api::get_filter_controls($this->page->url) . '
'; } return $this->content; diff --git a/blocks/calendar_upcoming/block_calendar_upcoming.php b/blocks/calendar_upcoming/block_calendar_upcoming.php index 335bcc4e7e2..3dab047ae38 100644 --- a/blocks/calendar_upcoming/block_calendar_upcoming.php +++ b/blocks/calendar_upcoming/block_calendar_upcoming.php @@ -65,14 +65,14 @@ class block_calendar_upcoming extends block_base { if ($courseshown == SITEID) { // Being displayed at site level. This will cause the filter to fall back to auto-detecting // the list of courses it will be grabbing events from. - $filtercourse = calendar_get_default_courses(); + $filtercourse = \core_calendar\api::get_default_courses(); } else { // Forcibly filter events to include only those from the particular course we are in. $filtercourse = array($courseshown => $this->page->course); } } - list($courses, $group, $user) = calendar_set_filters($filtercourse); + list($courses, $group, $user) = \core_calendar\api::set_filters($filtercourse); $defaultlookahead = CALENDAR_DEFAULT_UPCOMING_LOOKAHEAD; if (isset($CFG->calendar_lookahead)) { @@ -85,12 +85,12 @@ class block_calendar_upcoming extends block_base { $defaultmaxevents = intval($CFG->calendar_maxevents); } $maxevents = get_user_preferences('calendar_maxevents', $defaultmaxevents); - $events = calendar_get_upcoming($courses, $group, $user, $lookahead, $maxevents); + $events = \core_calendar\api::get_upcoming($courses, $group, $user, $lookahead, $maxevents); if (!empty($this->instance)) { $link = 'view.php?view=day&course='.$courseshown.'&'; $showcourselink = ($this->page->course->id == SITEID); - $this->content->text = calendar_get_block_upcoming($events, $link, $showcourselink); + $this->content->text = self::get_upcoming_content($events, $link, $showcourselink); } if (empty($this->content->text)) { @@ -99,6 +99,54 @@ class block_calendar_upcoming extends block_base { return $this->content; } + + /** + * Get the upcoming event block content. + * + * @param array $events list of events + * @param \moodle_url|string $linkhref link to event referer + * @param boolean $showcourselink whether links to courses should be shown + * @return string|null $content html block content + */ + public static function get_upcoming_content($events, $linkhref = null, $showcourselink = false) { + $content = ''; + $lines = count($events); + + if (!$lines) { + return $content; + } + + for ($i = 0; $i < $lines; ++$i) { + if (!isset($events[$i]->time)) { + continue; + } + $events[$i] = \core_calendar\api::add_event_metadata($events[$i]); + $content .= '
' . $events[$i]->icon . ''; + if (!empty($events[$i]->referer)) { + // That's an activity event, so let's provide the hyperlink. + $content .= $events[$i]->referer; + } else { + if (!empty($linkhref)) { + $href = \core_calendar\api::get_link_href(new \moodle_url(CALENDAR_URL . $linkhref), 0, 0, 0, + $events[$i]->timestart); + $href->set_anchor('event_' . $events[$i]->id); + $content .= \html_writer::link($href, $events[$i]->name); + } else { + $content .= $events[$i]->name; + } + } + $events[$i]->time = str_replace('»', '
»', $events[$i]->time); + if ($showcourselink && !empty($events[$i]->courselink)) { + $content .= \html_writer::div($events[$i]->courselink, 'course'); + } + $content .= '
' . $events[$i]->time . '
'; + if ($i < $lines - 1) { + $content .= '
'; + } + } + + return $content; + } } diff --git a/calendar/classes/api.php b/calendar/classes/api.php new file mode 100644 index 00000000000..bb1efa892ca --- /dev/null +++ b/calendar/classes/api.php @@ -0,0 +1,2325 @@ +. + +/** + * Contains class containing the calendar API. + * + * @package core_calendar + * @copyright 2017 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_calendar; + +use context_course; + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/calendar/lib.php'); + +/** + * Class containing the calendar API. + * + * @package core_calendar + * @copyright 2017 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class api { + + /** + * Return the days of the week. + * + * @return array array of days + */ + public static function get_days() { + $calendartype = type_factory::get_calendar_instance(); + return $calendartype->get_weekdays(); + } + + /** + * Get the subscription from a given id. + * + * @param int $id id of the subscription + * @return \stdClass Subscription record from DB + * @throws \moodle_exception for an invalid id + */ + public static function get_subscription($id) { + global $DB; + + $cache = \cache::make('core', 'calendar_subscriptions'); + $subscription = $cache->get($id); + if (empty($subscription)) { + $subscription = $DB->get_record('event_subscriptions', array('id' => $id), '*', MUST_EXIST); + $cache->set($id, $subscription); + } + + return $subscription; + } + + /** + * Gets the first day of the week. + * + * @return int + */ + public static function get_starting_weekday() { + $calendartype = type_factory::get_calendar_instance(); + return $calendartype->get_starting_weekday(); + } + + /** + * Generates the HTML for a miniature calendar. + * + * @param array $courses list of course to list events from + * @param array $groups list of group + * @param array $users user's info + * @param int|bool $calmonth calendar month in numeric, default is set to false + * @param int|bool $calyear calendar month in numeric, default is set to false + * @param string|bool $placement the place/page the calendar is set to appear - passed on the the controls function + * @param int|bool $courseid id of the course the calendar is displayed on - passed on the the controls function + * @param int $time the unixtimestamp representing the date we want to view, this is used instead of $calmonth + * and $calyear to support multiple calendars + * @return string $content return html table for mini calendar + */ + public static function get_mini_calendar($courses, $groups, $users, $calmonth = false, $calyear = false, + $placement = false, $courseid = false, $time = 0) { + global $CFG, $OUTPUT, $PAGE; + + // Get the calendar type we are using. + $calendartype = type_factory::get_calendar_instance(); + + $display = new \stdClass; + + // Assume we are not displaying this month for now. + $display->thismonth = false; + + $content = ''; + + // Do this check for backwards compatibility. + // The core should be passing a timestamp rather than month and year. + // If a month and year are passed they will be in Gregorian. + if (!empty($calmonth) && !empty($calyear)) { + // Ensure it is a valid date, else we will just set it to the current timestamp. + if (checkdate($calmonth, 1, $calyear)) { + $time = make_timestamp($calyear, $calmonth, 1); + } else { + $time = time(); + } + $date = usergetdate($time); + if ($calmonth == $date['mon'] && $calyear == $date['year']) { + $display->thismonth = true; + } + // We can overwrite date now with the date used by the calendar type, + // if it is not Gregorian, otherwise there is no need as it is already in Gregorian. + if ($calendartype->get_name() != 'gregorian') { + $date = $calendartype->timestamp_to_date_array($time); + } + } else if (!empty($time)) { + // Get the specified date in the calendar type being used. + $date = $calendartype->timestamp_to_date_array($time); + $thisdate = $calendartype->timestamp_to_date_array(time()); + if ($date['month'] == $thisdate['month'] && $date['year'] == $thisdate['year']) { + $display->thismonth = true; + // If we are the current month we want to set the date to the current date, not the start of the month. + $date = $thisdate; + } + } else { + // Get the current date in the calendar type being used. + $time = time(); + $date = $calendartype->timestamp_to_date_array($time); + $display->thismonth = true; + } + + list($d, $m, $y) = array($date['mday'], $date['mon'], $date['year']); // This is what we want to display. + + // Get Gregorian date for the start of the month. + $gregoriandate = $calendartype->convert_to_gregorian($date['year'], $date['mon'], 1); + + // Store the gregorian date values to be used later. + list($gy, $gm, $gd, $gh, $gmin) = array($gregoriandate['year'], $gregoriandate['month'], $gregoriandate['day'], + $gregoriandate['hour'], $gregoriandate['minute']); + + // Get the max number of days in this month for this calendar type. + $display->maxdays = self::get_days_in_month($m, $y); + // Get the starting week day for this month. + $startwday = dayofweek(1, $m, $y); + // Get the days in a week. + $daynames = self::get_days(); + // Store the number of days in a week. + $numberofdaysinweek = $calendartype->get_num_weekdays(); + + // Set the min and max weekday. + $display->minwday = self::get_starting_weekday(); + $display->maxwday = $display->minwday + ($numberofdaysinweek - 1); + + // These are used for DB queries, so we want unixtime, so we need to use Gregorian dates. + $display->tstart = make_timestamp($gy, $gm, $gd, $gh, $gmin, 0); + $display->tend = $display->tstart + ($display->maxdays * DAYSECS) - 1; + + // Align the starting weekday to fall in our display range. + // This is simple, not foolproof. + if ($startwday < $display->minwday) { + $startwday += $numberofdaysinweek; + } + + // Get the events matching our criteria. Don't forget to offset the timestamps for the user's TZ. + $events = self::get_events($display->tstart, $display->tend, $users, $groups, $courses); + + // Set event course class for course events. + if (!empty($events)) { + foreach ($events as $eventid => $event) { + if (!empty($event->modulename)) { + $cm = get_coursemodule_from_instance($event->modulename, $event->instance); + if (!\core_availability\info_module::is_user_visible($cm, 0, false)) { + unset($events[$eventid]); + } + } + } + } + + // This is either a genius idea or an idiot idea: in order to not complicate things, we use this rule: if, after + // possibly removing SITEID from $courses, there is only one course left, then clicking on a day in the month + // will also set the $SESSION->cal_courses_shown variable to that one course. Otherwise, we 'd need to add extra + // arguments to this function. + $hrefparams = array(); + if (!empty($courses)) { + $courses = array_diff($courses, array(SITEID)); + if (count($courses) == 1) { + $hrefparams['course'] = reset($courses); + } + } + + // We want to have easy access by day, since the display is on a per-day basis. + self::get_events_by_day($events, $m, $y, $eventsbyday, $durationbyday, $typesbyday, $courses); + + // Accessibility: added summary and elements. + $summary = get_string('calendarheading', 'calendar', userdate($display->tstart, get_string('strftimemonthyear'))); + // Begin table. + $content .= ''; + if (($placement !== false) && ($courseid !== false)) { + $content .= ''; + } + $content .= ''; // Header row: day names. + + // Print out the names of the weekdays. + for ($i = $display->minwday; $i <= $display->maxwday; $i++) { + $pos = $i % $numberofdaysinweek; + $content .= '\n"; + } + + $content .= ''; // End of day names; prepare for day numbers. + + // For the table display. $week is the row; $dayweek is the column. + $dayweek = $startwday; + + // Padding (the first week may have blank days in the beginning). + for ($i = $display->minwday; $i < $startwday; ++$i) { + $content .= '' ."\n"; + } + + $weekend = CALENDAR_DEFAULT_WEEKEND; + if (isset($CFG->calendar_weekend)) { + $weekend = intval($CFG->calendar_weekend); + } + + // Now display all the calendar. + $daytime = strtotime('-1 day', $display->tstart); + for ($day = 1; $day <= $display->maxdays; ++$day, ++$dayweek) { + $cellattributes = array(); + $daytime = strtotime('+1 day', $daytime); + if ($dayweek > $display->maxwday) { + // We need to change week (table row). + $content .= ''; + $dayweek = $display->minwday; + } + + // Reset vars. + if ($weekend & (1 << ($dayweek % $numberofdaysinweek))) { + // Weekend. This is true no matter what the exact range is. + $class = 'weekend day'; + } else { + // Normal working day. + $class = 'day'; + } + + $eventids = array(); + if (!empty($eventsbyday[$day])) { + $eventids = $eventsbyday[$day]; + } + + if (!empty($durationbyday[$day])) { + $eventids = array_unique(array_merge($eventids, $durationbyday[$day])); + } + + $finishclass = false; + + if (!empty($eventids)) { + // There is at least one event on this day. + $class .= ' hasevent'; + $hrefparams['view'] = 'day'; + $dayhref = self::get_link_href(new \moodle_url(CALENDAR_URL . 'view.php', $hrefparams), 0, 0, 0, $daytime); + + $popupcontent = ''; + foreach ($eventids as $eventid) { + if (!isset($events[$eventid])) { + continue; + } + $event = new event($events[$eventid]); + $popupalt = ''; + $component = 'moodle'; + if (!empty($event->modulename)) { + $popupicon = 'icon'; + $popupalt = $event->modulename; + $component = $event->modulename; + } else if ($event->courseid == SITEID) { // Site event. + $popupicon = 'i/siteevent'; + } else if ($event->courseid != 0 && $event->courseid != SITEID + && $event->groupid == 0) { // Course event. + $popupicon = 'i/courseevent'; + } else if ($event->groupid) { // Group event. + $popupicon = 'i/groupevent'; + } else { // Must be a user event. + $popupicon = 'i/userevent'; + } + + if ($event->timeduration) { + $startdate = $calendartype->timestamp_to_date_array($event->timestart); + $enddate = $calendartype->timestamp_to_date_array($event->timestart + $event->timeduration - 1); + if ($enddate['mon'] == $m && $enddate['year'] == $y && $enddate['mday'] == $day) { + $finishclass = true; + } + } + + $dayhref->set_anchor('event_' . $event->id); + + $popupcontent .= \html_writer::start_tag('div'); + $popupcontent .= $OUTPUT->pix_icon($popupicon, $popupalt, $component); + // Show ical source if needed. + if (!empty($event->subscription) && $CFG->calendar_showicalsource) { + $a = new \stdClass(); + $a->name = format_string($event->name, true); + $a->source = $event->subscription->name; + $name = get_string('namewithsource', 'calendar', $a); + } else { + if ($finishclass) { + $samedate = $startdate['mon'] == $enddate['mon'] && + $startdate['year'] == $enddate['year'] && + $startdate['mday'] == $enddate['mday']; + + if ($samedate) { + $name = format_string($event->name, true); + } else { + $name = format_string($event->name, true) . ' (' . get_string('eventendtime', 'calendar') . ')'; + } + } else { + $name = format_string($event->name, true); + } + } + $popupcontent .= \html_writer::link($dayhref, $name); + $popupcontent .= \html_writer::end_tag('div'); + } + + if ($display->thismonth && $day == $d) { + $popupdata = self::get_popup(true, $daytime, $popupcontent); + } else { + $popupdata = self::get_popup(false, $daytime, $popupcontent); + } + + // Class and cell content. + if (isset($typesbyday[$day]['startglobal'])) { + $class .= ' calendar_event_global'; + } else if (isset($typesbyday[$day]['startcourse'])) { + $class .= ' calendar_event_course'; + } else if (isset($typesbyday[$day]['startgroup'])) { + $class .= ' calendar_event_group'; + } else if (isset($typesbyday[$day]['startuser'])) { + $class .= ' calendar_event_user'; + } + if ($finishclass) { + $class .= ' duration_finish'; + } + $data = array( + 'url' => $dayhref, + 'day' => $day, + 'content' => $popupdata['data-core_calendar-popupcontent'], + 'title' => $popupdata['data-core_calendar-title'] + ); + $cell = $OUTPUT->render_from_template('core_calendar/minicalendar_day_link', $data); + } else { + $cell = $day; + } + + $durationclass = false; + if (isset($typesbyday[$day]['durationglobal'])) { + $durationclass = ' duration_global'; + } else if (isset($typesbyday[$day]['durationcourse'])) { + $durationclass = ' duration_course'; + } else if (isset($typesbyday[$day]['durationgroup'])) { + $durationclass = ' duration_group'; + } else if (isset($typesbyday[$day]['durationuser'])) { + $durationclass = ' duration_user'; + } + if ($durationclass) { + $class .= ' duration ' . $durationclass; + } + + // If event has a class set then add it to the table day '; + } + $content .= ''; // Last row ends. + + $content .= '
' . self::get_top_controls($placement, + array('id' => $courseid, 'time' => $time)) . '
' . + $daynames[$pos]['shortname'] . "
 
tag. + // Note: only one colour for minicalendar. + if (isset($eventsbyday[$day])) { + foreach ($eventsbyday[$day] as $eventid) { + if (!isset($events[$eventid])) { + continue; + } + $event = $events[$eventid]; + if (!empty($event->class)) { + $class .= ' ' . $event->class; + } + break; + } + } + + if ($display->thismonth && $day == $d) { + // The current cell is for today - add appropriate classes and additional information for styling. + $class .= ' today'; + $today = get_string('today', 'calendar') . ' ' . userdate(time(), get_string('strftimedayshort')); + + if (!isset($eventsbyday[$day]) && !isset($durationbyday[$day])) { + $class .= ' eventnone'; + $popupdata = self::get_popup(true, false); + $data = array( + 'url' => '#', + 'day' => $day, + 'content' => $popupdata['data-core_calendar-popupcontent'], + 'title' => $popupdata['data-core_calendar-title'] + ); + $cell = $OUTPUT->render_from_template('core_calendar/minicalendar_day_link', $data); + } + $cell = get_accesshide($today . ' ') . $cell; + } + + // Just display it. + $cellattributes['class'] = $class; + $content .= \html_writer::tag('td', $cell, $cellattributes); + } + + // Padding (the last week may have blank days at the end). + for ($i = $dayweek; $i <= $display->maxwday; ++$i) { + $content .= ' 
'; // Tabular display of days ends. + return $content; + } + + /** + * Gets the calendar popup. + * + * @param bool $today false except when called on the current day. + * @param mixed $timestart $events[$eventid]->timestart, OR false if there are no events. + * @param string $popupcontent content for the popup window/layout. + * @return string eventid for the calendar_tooltip popup window/layout. + */ + public static function get_popup($today = false, $timestart, $popupcontent = '') { + $popupcaption = ''; + if ($today) { + $popupcaption = get_string('today', 'calendar') . ' '; + } + + if (false === $timestart) { + $popupcaption .= userdate(time(), get_string('strftimedayshort')); + $popupcontent = get_string('eventnone', 'calendar'); + + } else { + $popupcaption .= get_string('eventsfor', 'calendar', userdate($timestart, get_string('strftimedayshort'))); + } + + return array( + 'data-core_calendar-title' => $popupcaption, + 'data-core_calendar-popupcontent' => $popupcontent, + ); + } + + /** + * Gets the calendar upcoming events. + * + * @param array $courses array of courses + * @param array|int|bool $groups array of groups, group id or boolean for all/no group events + * @param array|int|bool $users array of users, user id or boolean for all/no user events + * @param int $daysinfuture number of days in the future we 'll look + * @param int $maxevents maximum number of events + * @param int $fromtime start time + * @return array $output array of upcoming events + */ + public static function get_upcoming($courses, $groups, $users, $daysinfuture, $maxevents, $fromtime = 0) { + global $COURSE; + + $display = new \stdClass; + $display->range = $daysinfuture; // How many days in the future we 'll look. + $display->maxevents = $maxevents; + + $output = array(); + + $processed = 0; + $now = time(); // We 'll need this later. + $usermidnighttoday = usergetmidnight($now); + + if ($fromtime) { + $display->tstart = $fromtime; + } else { + $display->tstart = $usermidnighttoday; + } + + // This works correctly with respect to the user's DST, but it is accurate + // only because $fromtime is always the exact midnight of some day! + $display->tend = usergetmidnight($display->tstart + DAYSECS * $display->range + 3 * HOURSECS) - 1; + + // Get the events matching our criteria. + $events = self::get_events($display->tstart, $display->tend, $users, $groups, $courses); + + // This is either a genius idea or an idiot idea: in order to not complicate things, we use this rule: if, after + // possibly removing SITEID from $courses, there is only one course left, then clicking on a day in the month + // will also set the $SESSION->cal_courses_shown variable to that one course. Otherwise, we 'd need to add extra + // arguments to this function. + $hrefparams = array(); + if (!empty($courses)) { + $courses = array_diff($courses, array(SITEID)); + if (count($courses) == 1) { + $hrefparams['course'] = reset($courses); + } + } + + if ($events !== false) { + $modinfo = get_fast_modinfo($COURSE); + foreach ($events as $event) { + if (!empty($event->modulename)) { + if ($event->courseid == $COURSE->id) { + if (isset($modinfo->instances[$event->modulename][$event->instance])) { + $cm = $modinfo->instances[$event->modulename][$event->instance]; + if (!$cm->uservisible) { + continue; + } + } + } else { + if (!$cm = get_coursemodule_from_instance($event->modulename, $event->instance)) { + continue; + } + if (!\core_availability\info_module::is_user_visible($cm, 0, false)) { + continue; + } + } + } + + if ($processed >= $display->maxevents) { + break; + } + + $event->time = self::get_format_event_time($event, $now, $hrefparams); + $output[] = $event; + $processed++; + } + } + + return $output; + } + + /** + * Get a HTML link to a course. + * + * @param int $courseid the course id + * @return string a link to the course (as HTML); empty if the course id is invalid + */ + public static function get_courselink($courseid) { + if (!$courseid) { + return ''; + } + + self::get_course_cached($coursecache, $courseid); + $context = \context_course::instance($courseid); + $fullname = format_string($coursecache[$courseid]->fullname, true, array('context' => $context)); + $url = new \moodle_url('/course/view.php', array('id' => $courseid)); + $link = \html_writer::link($url, $fullname); + + return $link; + } + + /** + * Get current module cache. + * + * @param array $coursecache list of course cache + * @param string $modulename name of the module + * @param int $instance module instance number + * @return \stdClass|bool $module information + */ + public static function get_module_cached(&$coursecache, $modulename, $instance) { + $module = get_coursemodule_from_instance($modulename, $instance); + + if ($module === false) { + return false; + } + + if (!self::get_course_cached($coursecache, $module->course)) { + return false; + } + return $module; + } + + /** + * Get current course cache. + * + * @param array $coursecache list of course cache + * @param int $courseid id of the course + * @return \stdClass $coursecache[$courseid] return the specific course cache + */ + public static function get_course_cached(&$coursecache, $courseid) { + if (!isset($coursecache[$courseid])) { + $coursecache[$courseid] = get_course($courseid); + } + return $coursecache[$courseid]; + } + + /** + * Get group from groupid for calendar display. + * + * @param int $groupid + * @return \stdClass group object with fields 'id', 'name' and 'courseid' + */ + public static function get_group_cached($groupid) { + static $groupscache = array(); + if (!isset($groupscache[$groupid])) { + $groupscache[$groupid] = groups_get_group($groupid, 'id,name,courseid'); + } + return $groupscache[$groupid]; + } + + /** + * Add calendar event metadata. + * + * @param \stdClass $event event info + * @return \stdClass $event metadata + */ + public static function add_event_metadata($event) { + global $CFG, $OUTPUT; + + // Support multilang in event->name. + $event->name = format_string($event->name, true); + + if (!empty($event->modulename)) { // Activity event. + // The module name is set. I will assume that it has to be displayed, and + // also that it is an automatically-generated event. And of course that the + // fields for get_coursemodule_from_instance are set correctly. + $module = self::get_module_cached($coursecache, $event->modulename, $event->instance); + + if ($module === false) { + return; + } + + $modulename = get_string('modulename', $event->modulename); + if (get_string_manager()->string_exists($event->eventtype, $event->modulename)) { + // Will be used as alt text if the event icon. + $eventtype = get_string($event->eventtype, $event->modulename); + } else { + $eventtype = ''; + } + $icon = $OUTPUT->pix_url('icon', $event->modulename) . ''; + + $event->icon = '' . $eventtype . ''; + $event->referer = '' . $event->name . ''; + $event->courselink = self::get_courselink($module->course); + $event->cmid = $module->id; + } else if ($event->courseid == SITEID) { // Site event. + $event->icon = '' .
+                get_string('globalevent', 'calendar') . ''; + $event->cssclass = 'calendar_event_global'; + } else if ($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) { // Course event. + $event->icon = '' .
+                get_string('courseevent', 'calendar') . ''; + $event->courselink = self::get_courselink($event->courseid); + $event->cssclass = 'calendar_event_course'; + } else if ($event->groupid) { // Group event. + if ($group = self::get_group_cached($event->groupid)) { + $groupname = format_string($group->name, true, \context_course::instance($group->courseid)); + } else { + $groupname = ''; + } + $event->icon = \html_writer::empty_tag('image', array('src' => $OUTPUT->pix_url('i/groupevent'), + 'alt' => get_string('groupevent', 'calendar'), 'title' => $groupname, 'class' => 'icon')); + $event->courselink = self::get_courselink($event->courseid) . ', ' . $groupname; + $event->cssclass = 'calendar_event_group'; + } else if ($event->userid) { // User event. + $event->icon = '' .
+                get_string('userevent', 'calendar') . ''; + $event->cssclass = 'calendar_event_user'; + } + + return $event; + } + + /** + * Get calendar events + * + * @param int $tstart Start time of time range for events + * @param int $tend End time of time range for events + * @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 boolean $withduration whether only events starting within time range selected + * or events in progress/already started selected as well + * @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) + */ + public static function get_events($tstart, $tend, $users, $groups, $courses, $withduration = true, $ignorehidden = true) { + global $DB; + + $params = array(); + // Quick test. + if (empty($users) && empty($groups) && empty($courses)) { + return array(); + } + + // Array of filter conditions. To be concatenated by the OR operator. + $filters = []; + + // User filter. + if ((is_array($users) && !empty($users)) or is_numeric($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)"; + $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)"; + } + // Boolean false (no users at all): We don't need to do anything. + + // Group filter. + if ((is_array($groups) && !empty($groups)) or is_numeric($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)) or is_numeric($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)"; + } + + // 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)"]; + + // Time clause. + if ($withduration) { + $timeclause = "((e.timestart >= :tstart1 OR e.timestart + e.timeduration > :tstart2) AND e.timestart <= :tend)"; + $params['tstart1'] = $tstart; + $params['tstart2'] = $tstart; + $params['tend'] = $tend; + } else { + $timeclause = "(e.timestart >= :tstart AND e.timestart <= :tend)"; + $params['tstart'] = $tstart; + $params['tend'] = $tend; + } + $whereconditions[] = $timeclause; + + // 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. + $subquerywhere = ''; + $subqueryconditions = []; + + // Get the user's courses. Otherwise, get the default courses being shown by the calendar. + $usercourses = self::get_default_courses(); + + // Set calendar filters. + list($usercourses, $usergroups, $user) = self::set_filters($usercourses, true); + $subqueryparams = []; + + // Flag to indicate whether the query needs to exclude group overrides. + $viewgroupsonly = false; + + if ($user) { + // Set filter condition for the user's events. + $subqueryconditions[] = "(ev.userid = :user AND ev.courseid = 0 AND ev.groupid = 0)"; + $subqueryparams['user'] = $user; + + foreach ($usercourses as $courseid) { + if (has_capability('moodle/site:accessallgroups', context_course::instance($courseid))) { + $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. + $subqueryconditions[] = "(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); + $subqueryconditions[] = "(ev.groupid $inusergroups)"; + $subqueryparams = array_merge($subqueryparams, $inusergroupparams); + } + + // Get courses to be used for the subquery. + $subquerycourses = []; + if (is_array($courses)) { + $subquerycourses = $courses; + } else if (is_numeric($courses)) { + $subquerycourses[] = $courses; + } + // Merge with user courses, if necessary. + if (!empty($usercourses)) { + $subquerycourses = array_merge($subquerycourses, $usercourses); + // 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); + $subqueryconditions[] = "(ev.groupid = 0 AND ev.courseid $incourses)"; + $subqueryparams = array_merge($subqueryparams, $incoursesparams); + } + + // Build the WHERE condition for the sub-query. + if (!empty($subqueryconditions)) { + $subquerywhere = 'WHERE ' . implode(" OR ", $subqueryconditions); + } + + // 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, + MAX(ev.priority) as priority + FROM {event} ev + $subquerywhere + GROUP BY ev.modulename, ev.instance, ev.eventtype"; + + // Build the main query. + $sql = "SELECT e.* + 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 + WHERE (m.visible = 1 OR m.visible IS NULL) AND $whereclause + ORDER BY e.timestart"; + $events = $DB->get_records_sql($sql, $params); + + if ($events === false) { + $events = array(); + } + return $events; + } + + /** + * Get calendar events by id. + * + * @param array $eventids list of event ids + * @return array Array of event entries, empty array if nothing found + */ + public static function get_events_by_id($eventids) { + global $DB; + + if (!is_array($eventids) || empty($eventids)) { + return array(); + } + + list($wheresql, $params) = $DB->get_in_or_equal($eventids); + $wheresql = "id $wheresql"; + + return $DB->get_records_select('event', $wheresql, $params); + } + + /** + * Get control options for calendar. + * + * @param string $type of calendar + * @param array $data calendar information + * @return string $content return available control for the calender in html + */ + public static function get_top_controls($type, $data) { + global $PAGE, $OUTPUT; + + // Get the calendar type we are using. + $calendartype = type_factory::get_calendar_instance(); + + $content = ''; + + // Ensure course id passed if relevant. + $courseid = ''; + if (!empty($data['id'])) { + $courseid = '&course=' . $data['id']; + } + + // If we are passing a month and year then we need to convert this to a timestamp to + // support multiple calendars. No where in core should these be passed, this logic + // here is for third party plugins that may use this function. + if (!empty($data['m']) && !empty($date['y'])) { + if (!isset($data['d'])) { + $data['d'] = 1; + } + if (!checkdate($data['m'], $data['d'], $data['y'])) { + $time = time(); + } else { + $time = make_timestamp($data['y'], $data['m'], $data['d']); + } + } else if (!empty($data['time'])) { + $time = $data['time']; + } else { + $time = time(); + } + + // Get the date for the calendar type. + $date = $calendartype->timestamp_to_date_array($time); + + $urlbase = $PAGE->url; + + // We need to get the previous and next months in certain cases. + if ($type == 'frontpage' || $type == 'course' || $type == 'month') { + $prevmonth = self::get_prev_month($date['mon'], $date['year']); + $prevmonthtime = $calendartype->convert_to_gregorian($prevmonth[1], $prevmonth[0], 1); + $prevmonthtime = make_timestamp($prevmonthtime['year'], $prevmonthtime['month'], $prevmonthtime['day'], + $prevmonthtime['hour'], $prevmonthtime['minute']); + + $nextmonth = self::get_next_month($date['mon'], $date['year']); + $nextmonthtime = $calendartype->convert_to_gregorian($nextmonth[1], $nextmonth[0], 1); + $nextmonthtime = make_timestamp($nextmonthtime['year'], $nextmonthtime['month'], $nextmonthtime['day'], + $nextmonthtime['hour'], $nextmonthtime['minute']); + } + + switch ($type) { + case 'frontpage': + $prevlink = self::get_link_previous(get_string('monthprev', 'access'), $urlbase, false, false, false, + true, $prevmonthtime); + $nextlink = self::get_link_next(get_string('monthnext', 'access'), $urlbase, false, false, false, true, + $nextmonthtime); + $calendarlink = self::get_link_href(new \moodle_url(CALENDAR_URL . 'view.php', array('view' => 'month')), + false, false, false, $time); + + if (!empty($data['id'])) { + $calendarlink->param('course', $data['id']); + } + + $right = $nextlink; + + $content .= \html_writer::start_tag('div', array('class' => 'calendar-controls')); + $content .= $prevlink . ' | '; + $content .= \html_writer::tag('span', \html_writer::link($calendarlink, + userdate($time, get_string('strftimemonthyear')), array('title' => get_string('monththis', 'calendar')) + ), array('class' => 'current')); + $content .= ' | ' . $right; + $content .= "\n"; + $content .= \html_writer::end_tag('div'); + + break; + case 'course': + $prevlink = self::get_link_previous(get_string('monthprev', 'access'), $urlbase, false, false, false, + true, $prevmonthtime); + $nextlink = self::get_link_next(get_string('monthnext', 'access'), $urlbase, false, false, false, + true, $nextmonthtime); + $calendarlink = self::get_link_href(new \moodle_url(CALENDAR_URL . 'view.php', array('view' => 'month')), + false, false, false, $time); + + if (!empty($data['id'])) { + $calendarlink->param('course', $data['id']); + } + + $content .= \html_writer::start_tag('div', array('class' => 'calendar-controls')); + $content .= $prevlink . ' | '; + $content .= \html_writer::tag('span', \html_writer::link($calendarlink, + userdate($time, get_string('strftimemonthyear')), array('title' => get_string('monththis', 'calendar')) + ), array('class' => 'current')); + $content .= ' | ' . $nextlink; + $content .= ""; + $content .= \html_writer::end_tag('div'); + break; + case 'upcoming': + $calendarlink = self::get_link_href(new \moodle_url(CALENDAR_URL . 'view.php', array('view' => 'upcoming')), + false, false, false, $time); + if (!empty($data['id'])) { + $calendarlink->param('course', $data['id']); + } + $calendarlink = \html_writer::link($calendarlink, userdate($time, get_string('strftimemonthyear'))); + $content .= \html_writer::tag('div', $calendarlink, array('class' => 'centered')); + break; + case 'display': + $calendarlink = self::get_link_href(new \moodle_url(CALENDAR_URL . 'view.php', array('view' => 'month')), + false, false, false, $time); + if (!empty($data['id'])) { + $calendarlink->param('course', $data['id']); + } + $calendarlink = \html_writer::link($calendarlink, userdate($time, get_string('strftimemonthyear'))); + $content .= \html_writer::tag('h3', $calendarlink); + break; + case 'month': + $prevlink = self::get_link_previous(userdate($prevmonthtime, get_string('strftimemonthyear')), + 'view.php?view=month' . $courseid . '&', false, false, false, false, $prevmonthtime); + $nextlink = self::get_link_next(userdate($nextmonthtime, get_string('strftimemonthyear')), + 'view.php?view=month' . $courseid . '&', false, false, false, false, $nextmonthtime); + + $content .= \html_writer::start_tag('div', array('class' => 'calendar-controls')); + $content .= $prevlink . ' | '; + $content .= $OUTPUT->heading(userdate($time, get_string('strftimemonthyear')), 2, 'current'); + $content .= ' | ' . $nextlink; + $content .= ''; + $content .= \html_writer::end_tag('div')."\n"; + break; + case 'day': + $days = self::get_days(); + + $prevtimestamp = strtotime('-1 day', $time); + $nexttimestamp = strtotime('+1 day', $time); + + $prevdate = $calendartype->timestamp_to_date_array($prevtimestamp); + $nextdate = $calendartype->timestamp_to_date_array($nexttimestamp); + + $prevname = $days[$prevdate['wday']]['fullname']; + $nextname = $days[$nextdate['wday']]['fullname']; + $prevlink = self::get_link_previous($prevname, 'view.php?view=day' . $courseid . '&', false, false, + false, false, $prevtimestamp); + $nextlink = self::get_link_next($nextname, 'view.php?view=day' . $courseid . '&', false, false, false, + false, $nexttimestamp); + + $content .= \html_writer::start_tag('div', array('class' => 'calendar-controls')); + $content .= $prevlink; + $content .= ' | ' .userdate($time, + get_string('strftimedaydate')) . ''; + $content .= ' | ' . $nextlink; + $content .= ""; + $content .= \html_writer::end_tag('div') . "\n"; + + break; + } + + return $content; + } + + /** + * Formats a filter control element. + * + * @param \moodle_url $url of the filter + * @param int $type constant defining the type filter + * @return string html content of the element + */ + public static function get_filter_controls_element(\moodle_url $url, $type) { + global $OUTPUT; + + switch ($type) { + case CALENDAR_EVENT_GLOBAL: + $typeforhumans = 'global'; + $class = 'calendar_event_global'; + break; + case CALENDAR_EVENT_COURSE: + $typeforhumans = 'course'; + $class = 'calendar_event_course'; + break; + case CALENDAR_EVENT_GROUP: + $typeforhumans = 'groups'; + $class = 'calendar_event_group'; + break; + case CALENDAR_EVENT_USER: + $typeforhumans = 'user'; + $class = 'calendar_event_user'; + break; + } + + if (self::show_event_type($type)) { + $icon = $OUTPUT->pix_icon('t/hide', get_string('hide')); + $str = get_string('hide' . $typeforhumans . 'events', 'calendar'); + } else { + $icon = $OUTPUT->pix_icon('t/show', get_string('show')); + $str = get_string('show' . $typeforhumans . 'events', 'calendar'); + } + $content = \html_writer::start_tag('li', array('class' => 'calendar_event')); + $content .= \html_writer::start_tag('a', array('href' => $url, 'rel' => 'nofollow')); + $content .= \html_writer::tag('span', $icon, array('class' => $class)); + $content .= \html_writer::tag('span', $str, array('class' => 'eventname')); + $content .= \html_writer::end_tag('a'); + $content .= \html_writer::end_tag('li'); + + return $content; + } + + /** + * Get the controls filter for calendar. + * + * Filter is used to hide calendar info from the display page + * + * @param \moodle_url $returnurl return-url for filter controls + * @return string $content return filter controls in html + */ + public static function get_filter_controls(\moodle_url $returnurl) { + $groupevents = true; + + $seturl = new \moodle_url('/calendar/set.php', array('return' => base64_encode($returnurl->out_as_local_url(false)), + 'sesskey' => sesskey())); + $content = \html_writer::start_tag('ul'); + + $seturl->param('var', 'showglobal'); + $content .= self::get_filter_controls_element($seturl, CALENDAR_EVENT_GLOBAL); + + $seturl->param('var', 'showcourses'); + $content .= self::get_filter_controls_element($seturl, CALENDAR_EVENT_COURSE); + + if (isloggedin() && !isguestuser()) { + if ($groupevents) { + // This course MIGHT have group events defined, so show the filter. + $seturl->param('var', 'showgroups'); + $content .= self::get_filter_controls_element($seturl, CALENDAR_EVENT_GROUP); + } + $seturl->param('var', 'showuser'); + $content .= self::get_filter_controls_element($seturl, CALENDAR_EVENT_USER); + } + $content .= \html_writer::end_tag('ul'); + + return $content; + } + + /** + * Return the representation day + * + * @param int $tstamp Timestamp in GMT + * @param int|bool $now current Unix timestamp + * @param bool $usecommonwords + * @return string the formatted date/time + */ + public static function get_day_representation($tstamp, $now = false, $usecommonwords = true) { + + static $shortformat; + if (empty($shortformat)) { + $shortformat = get_string('strftimedayshort'); + } + + if ($now === false) { + $now = time(); + } + + // To have it in one place, if a change is needed. + $formal = userdate($tstamp, $shortformat); + + $datestamp = usergetdate($tstamp); + $datenow = usergetdate($now); + + if ($usecommonwords == false) { + // We don't want words, just a date. + return $formal; + } else if ($datestamp['year'] == $datenow['year'] && $datestamp['yday'] == $datenow['yday']) { + return get_string('today', 'calendar'); + } else if (($datestamp['year'] == $datenow['year'] && $datestamp['yday'] == $datenow['yday'] - 1 ) || + ($datestamp['year'] == $datenow['year'] - 1 && $datestamp['mday'] == 31 && $datestamp['mon'] == 12 + && $datenow['yday'] == 1)) { + return get_string('yesterday', 'calendar'); + } else if (($datestamp['year'] == $datenow['year'] && $datestamp['yday'] == $datenow['yday'] + 1 ) || + ($datestamp['year'] == $datenow['year'] + 1 && $datenow['mday'] == 31 && $datenow['mon'] == 12 + && $datestamp['yday'] == 1)) { + return get_string('tomorrow', 'calendar'); + } else { + return $formal; + } + } + + /** + * return the formatted representation time + * + * @param int $time the timestamp in UTC, as obtained from the database + * @return string the formatted date/time + */ + public static function get_time_representation($time) { + static $langtimeformat = null; + + if ($langtimeformat === null) { + $langtimeformat = get_string('strftimetime'); + } + + $timeformat = get_user_preferences('calendar_timeformat'); + if (empty($timeformat)) { + $timeformat = get_config(null, 'calendar_site_timeformat'); + } + + return userdate($time, empty($timeformat) ? $langtimeformat : $timeformat); + } + + /** + * Adds day, month, year arguments to a URL and returns a moodle_url object. + * + * @param string|\moodle_url $linkbase + * @param int $d The number of the day. + * @param int $m The number of the month. + * @param int $y The number of the year. + * @param int $time the unixtime, used for multiple calendar support. The values $d, + * $m and $y are kept for backwards compatibility. + * @return \moodle_url|null $linkbase + */ + public static function get_link_href($linkbase, $d, $m, $y, $time = 0) { + if (empty($linkbase)) { + return null; + } + + if (!($linkbase instanceof \moodle_url)) { + $linkbase = new \moodle_url($linkbase); + } + + // If a day, month and year were passed then convert it to a timestamp. If these were passed + // then we can assume the day, month and year are passed as Gregorian, as no where in core + // should we be passing these values rather than the time. + if (!empty($d) && !empty($m) && !empty($y)) { + if (checkdate($m, $d, $y)) { + $time = make_timestamp($y, $m, $d); + } else { + $time = time(); + } + } else if (empty($time)) { + $time = time(); + } + + $linkbase->param('time', $time); + + return $linkbase; + } + + /** + * Build and return a previous month HTML link, with an arrow. + * + * @param string $text The text label. + * @param string|\moodle_url $linkbase The URL stub. + * @param int $d The number of the date. + * @param int $m The number of the month. + * @param int $y year The number of the year. + * @param bool $accesshide Default visible, or hide from all except screenreaders. + * @param int $time the unixtime, used for multiple calendar support. The values $d, + * $m and $y are kept for backwards compatibility. + * @return string HTML string. + */ + public static function get_link_previous($text, $linkbase, $d, $m, $y, $accesshide = false, $time = 0) { + $href = self::get_link_href(new \moodle_url($linkbase), $d, $m, $y, $time); + + if (empty($href)) { + return $text; + } + + return link_arrow_left($text, (string)$href, $accesshide, 'previous'); + } + + /** + * Build and return a next month HTML link, with an arrow. + * + * @param string $text The text label. + * @param string|\moodle_url $linkbase The URL stub. + * @param int $d the number of the Day + * @param int $m The number of the month. + * @param int $y The number of the year. + * @param bool $accesshide Default visible, or hide from all except screenreaders. + * @param int $time the unixtime, used for multiple calendar support. The values $d, + * $m and $y are kept for backwards compatibility. + * @return string HTML string. + */ + public static function get_link_next($text, $linkbase, $d, $m, $y, $accesshide = false, $time = 0) { + $href = self::get_link_href(new \moodle_url($linkbase), $d, $m, $y, $time); + + if (empty($href)) { + return $text; + } + + return link_arrow_right($text, (string)$href, $accesshide, 'next'); + } + + /** + * Return the number of days in month + * + * @param int $month the number of the month. + * @param int $year the number of the year + * @return int + */ + public static function get_days_in_month($month, $year) { + $calendartype = type_factory::get_calendar_instance(); + return $calendartype->get_num_days_in_month($year, $month); + } + + /** + * Get the next following month + * + * @param int $month the number of the month. + * @param int $year the number of the year. + * @return array the following month + */ + public static function get_next_month($month, $year) { + $calendartype = type_factory::get_calendar_instance(); + return $calendartype->get_next_month($year, $month); + } + + /** + * Get the previous month. + * + * @param int $month the number of the month. + * @param int $year the number of the year. + * @return array previous month + */ + public static function get_prev_month($month, $year) { + $calendartype = type_factory::get_calendar_instance(); + return $calendartype->get_prev_month($year, $month); + } + + /** + * Get per-day basis events. + * + * @param array $events list of events + * @param int $month the number of the month + * @param int $year the number of the year + * @param array $eventsbyday event on specific day + * @param array $durationbyday duration of the event in days + * @param array $typesbyday event type (eg: global, course, user, or group) + * @param array $courses list of courses + * @return void + */ + public static function get_events_by_day($events, $month, $year, &$eventsbyday, &$durationbyday, &$typesbyday, &$courses) { + $calendartype = type_factory::get_calendar_instance(); + + $eventsbyday = array(); + $typesbyday = array(); + $durationbyday = array(); + + if ($events === false) { + return; + } + + foreach ($events as $event) { + $startdate = $calendartype->timestamp_to_date_array($event->timestart); + if ($event->timeduration) { + $enddate = $calendartype->timestamp_to_date_array($event->timestart + $event->timeduration - 1); + } else { + $enddate = $startdate; + } + + // Simple arithmetic: $year * 13 + $month is a distinct integer for each distinct ($year, $month) pair. + if (!($startdate['year'] * 13 + $startdate['mon'] <= $year * 13 + $month) && + ($enddate['year'] * 13 + $enddate['mon'] >= $year * 13 + $month)) { + continue; + } + + $eventdaystart = intval($startdate['mday']); + + if ($startdate['mon'] == $month && $startdate['year'] == $year) { + // Give the event to its day. + $eventsbyday[$eventdaystart][] = $event->id; + + // Mark the day as having such an event. + if ($event->courseid == SITEID && $event->groupid == 0) { + $typesbyday[$eventdaystart]['startglobal'] = true; + // Set event class for global event. + $events[$event->id]->class = 'calendar_event_global'; + } else if ($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) { + $typesbyday[$eventdaystart]['startcourse'] = true; + // Set event class for course event. + $events[$event->id]->class = 'calendar_event_course'; + } else if ($event->groupid) { + $typesbyday[$eventdaystart]['startgroup'] = true; + // Set event class for group event. + $events[$event->id]->class = 'calendar_event_group'; + } else if ($event->userid) { + $typesbyday[$eventdaystart]['startuser'] = true; + // Set event class for user event. + $events[$event->id]->class = 'calendar_event_user'; + } + } + + if ($event->timeduration == 0) { + // Proceed with the next. + continue; + } + + // The event starts on $month $year or before. + if ($startdate['mon'] == $month && $startdate['year'] == $year) { + $lowerbound = intval($startdate['mday']); + } else { + $lowerbound = 0; + } + + // Also, it ends on $month $year or later. + if ($enddate['mon'] == $month && $enddate['year'] == $year) { + $upperbound = intval($enddate['mday']); + } else { + $upperbound = self::get_days_in_month($month, $year); + } + + // Mark all days between $lowerbound and $upperbound (inclusive) as duration. + for ($i = $lowerbound + 1; $i <= $upperbound; ++$i) { + $durationbyday[$i][] = $event->id; + if ($event->courseid == SITEID && $event->groupid == 0) { + $typesbyday[$i]['durationglobal'] = true; + } else if ($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) { + $typesbyday[$i]['durationcourse'] = true; + } else if ($event->groupid) { + $typesbyday[$i]['durationgroup'] = true; + } else if ($event->userid) { + $typesbyday[$i]['durationuser'] = true; + } + } + + } + return; + } + + /** + * Returns the courses to load events for, the + * + * @param array $courseeventsfrom An array of courses to load calendar events for + * @param bool $ignorefilters specify the use of filters, false is set as default + * @return array An array of courses, groups, and user to load calendar events for based upon filters + */ + public static function set_filters(array $courseeventsfrom, $ignorefilters = false) { + global $USER, $CFG; + + // For backwards compatability we have to check whether the courses array contains + // just id's in which case we need to load course objects. + $coursestoload = array(); + foreach ($courseeventsfrom as $id => $something) { + if (!is_object($something)) { + $coursestoload[] = $id; + unset($courseeventsfrom[$id]); + } + } + + $courses = array(); + $user = false; + $group = false; + + // Get the capabilities that allow seeing group events from all groups. + $allgroupscaps = array('moodle/site:accessallgroups', 'moodle/calendar:manageentries'); + + $isloggedin = isloggedin(); + + if ($ignorefilters || self::show_event_type(CALENDAR_EVENT_COURSE)) { + $courses = array_keys($courseeventsfrom); + } + if ($ignorefilters || self::show_event_type(CALENDAR_EVENT_GLOBAL)) { + $courses[] = SITEID; + } + $courses = array_unique($courses); + sort($courses); + + if (!empty($courses) && in_array(SITEID, $courses)) { + // Sort courses for consistent colour highlighting. + // Effectively ignoring SITEID as setting as last course id. + $key = array_search(SITEID, $courses); + unset($courses[$key]); + $courses[] = SITEID; + } + + if ($ignorefilters || ($isloggedin && self::show_event_type(CALENDAR_EVENT_USER))) { + $user = $USER->id; + } + + if (!empty($courseeventsfrom) && (self::show_event_type(CALENDAR_EVENT_GROUP) || $ignorefilters)) { + + if (count($courseeventsfrom) == 1) { + $course = reset($courseeventsfrom); + if (has_any_capability($allgroupscaps, \context_course::instance($course->id))) { + $coursegroups = groups_get_all_groups($course->id, 0, 0, 'g.id'); + $group = array_keys($coursegroups); + } + } + if ($group === false) { + if (!empty($CFG->calendar_adminseesall) && has_any_capability($allgroupscaps, \context_system::instance())) { + $group = true; + } else if ($isloggedin) { + $groupids = array(); + foreach ($courseeventsfrom as $courseid => $course) { + // If the user is an editing teacher in there. + if (!empty($USER->groupmember[$course->id])) { + // We've already cached the users groups for this course so we can just use that. + $groupids = array_merge($groupids, $USER->groupmember[$course->id]); + } else if ($course->groupmode != NOGROUPS || !$course->groupmodeforce) { + // If this course has groups, show events from all of those related to the current user. + $coursegroups = groups_get_user_groups($course->id, $USER->id); + $groupids = array_merge($groupids, $coursegroups['0']); + } + } + if (!empty($groupids)) { + $group = $groupids; + } + } + } + } + if (empty($courses)) { + $courses = false; + } + + return array($courses, $group, $user); + } + + /** + * Return the capability for editing calendar event + * + * @param event $event event object + * @return bool capability to edit event + */ + public static function can_edit_event($event) { + global $USER, $DB; + + // Must be logged in. + if (!isloggedin()) { + return false; + } + + // Can not be using guest account. + if (isguestuser()) { + return false; + } + + // You cannot edit calendar subscription events presently. + if (!empty($event->subscriptionid)) { + if (!empty($event->subscription->url)) { + // This event can be updated externally, so it cannot be edited. + return false; + } + } + + $sitecontext = \context_system::instance(); + + // If user has manageentries at site level, return true. + if (has_capability('moodle/calendar:manageentries', $sitecontext)) { + return true; + } + + // If groupid is set, it's definitely a group event. + if (!empty($event->groupid)) { + // Allow users to add/edit group events if - + // 1) They have manageentries for the course OR + // 2) They have managegroupentries AND are in the group. + $group = $DB->get_record('groups', array('id' => $event->groupid)); + return $group && ( + has_capability('moodle/calendar:manageentries', $event->context) || + (has_capability('moodle/calendar:managegroupentries', $event->context) + && groups_is_member($event->groupid))); + } else if (!empty($event->courseid)) { + // If groupid is not set, but course is set, it's definiely a course event. + return has_capability('moodle/calendar:manageentries', $event->context); + } else if (!empty($event->userid) && $event->userid == $USER->id) { + // If course is not set, but userid id set, it's a user event. + return (has_capability('moodle/calendar:manageownentries', $event->context)); + } else if (!empty($event->userid)) { + return (has_capability('moodle/calendar:manageentries', $event->context)); + } + + return false; + } + + /** + * Returns the default courses to display on the calendar when there isn't a specific + * course to display. + * + * @return array $courses Array of courses to display + */ + public static function get_default_courses() { + global $CFG, $DB; + + if (!isloggedin()) { + return array(); + } + + if (!empty($CFG->calendar_adminseesall) && has_capability('moodle/calendar:manageentries', \context_system::instance())) { + $select = ', ' . \context_helper::get_preload_record_columns_sql('ctx'); + $join = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)"; + $sql = "SELECT c.* $select + FROM {course} c + $join + WHERE EXISTS (SELECT 1 FROM {event} e WHERE e.courseid = c.id) + "; + $courses = $DB->get_records_sql($sql, array('contextlevel' => CONTEXT_COURSE), 0, 20); + foreach ($courses as $course) { + \context_helper::preload_from_record($course); + } + return $courses; + } + + $courses = enrol_get_my_courses(); + + return $courses; + } + + /** + * Get event format time + * + * @param event $event event object + * @param int $now current time in gmt + * @param array $linkparams list of params for event link + * @param bool $usecommonwords the words as formatted date/time. + * @param int $showtime determine the show time GMT timestamp + * @return string $eventtime link/string for event time + */ + public static function get_format_event_time($event, $now, $linkparams = null, $usecommonwords = true, $showtime = 0) { + $starttime = $event->timestart; + $endtime = $event->timestart + $event->timeduration; + + if (empty($linkparams) || !is_array($linkparams)) { + $linkparams = array(); + } + + $linkparams['view'] = 'day'; + + // OK, now to get a meaningful display. + // Check if there is a duration for this event. + if ($event->timeduration) { + // Get the midnight of the day the event will start. + $usermidnightstart = usergetmidnight($starttime); + // Get the midnight of the day the event will end. + $usermidnightend = usergetmidnight($endtime); + // Check if we will still be on the same day. + if ($usermidnightstart == $usermidnightend) { + // Check if we are running all day. + if ($event->timeduration == DAYSECS) { + $time = get_string('allday', 'calendar'); + } else { // Specify the time we will be running this from. + $datestart = self::get_time_representation($starttime); + $dateend = self::get_time_representation($endtime); + $time = $datestart . ' » ' . $dateend; + } + + // Set printable representation. + if (!$showtime) { + $day = self::get_day_representation($event->timestart, $now, $usecommonwords); + $url = self::get_link_href(new \moodle_url(CALENDAR_URL . 'view.php', $linkparams), 0, 0, 0, $endtime); + $eventtime = \html_writer::link($url, $day) . ', ' . $time; + } else { + $eventtime = $time; + } + } else { // It must spans two or more days. + $daystart = self::get_day_representation($event->timestart, $now, $usecommonwords) . ', '; + if ($showtime == $usermidnightstart) { + $daystart = ''; + } + $timestart = self::get_time_representation($event->timestart); + $dayend = self::get_day_representation($event->timestart + $event->timeduration, $now, $usecommonwords) . ', '; + if ($showtime == $usermidnightend) { + $dayend = ''; + } + $timeend = self::get_time_representation($event->timestart + $event->timeduration); + + // Set printable representation. + if ($now >= $usermidnightstart && $now < strtotime('+1 day', $usermidnightstart)) { + $url = self::get_link_href(new \moodle_url(CALENDAR_URL . 'view.php', $linkparams), 0, 0, 0, $endtime); + $eventtime = $timestart . ' » ' . \html_writer::link($url, $dayend) . $timeend; + } else { + // The event is in the future, print start and end links. + $url = self::get_link_href(new \moodle_url(CALENDAR_URL . 'view.php', $linkparams), 0, 0, 0, $starttime); + $eventtime = \html_writer::link($url, $daystart) . $timestart . ' » '; + + $url = self::get_link_href(new \moodle_url(CALENDAR_URL . 'view.php', $linkparams), 0, 0, 0, $endtime); + $eventtime .= \html_writer::link($url, $dayend) . $timeend; + } + } + } else { // There is no time duration. + $time = self::get_time_representation($event->timestart); + // Set printable representation. + if (!$showtime) { + $day = self::get_day_representation($event->timestart, $now, $usecommonwords); + $url = self::get_link_href(new \moodle_url(CALENDAR_URL . 'view.php', $linkparams), 0, 0, 0, $starttime); + $eventtime = \html_writer::link($url, $day) . ', ' . trim($time); + } else { + $eventtime = $time; + } + } + + // Check if It has expired. + if ($event->timestart + $event->timeduration < $now) { + $eventtime = '' . str_replace(' href=', ' class="dimmed" href=', $eventtime) . ''; + } + + return $eventtime; + } + + /** + * Checks to see if the requested type of event should be shown for the given user. + * + * @param int $type The type to check the display for (default is to display all) + * @param \stdClass|int|null $user The user to check for - by default the current user + * @return bool True if the tyep should be displayed false otherwise + */ + public static function show_event_type($type, $user = null) { + $default = CALENDAR_EVENT_GLOBAL + CALENDAR_EVENT_COURSE + CALENDAR_EVENT_GROUP + CALENDAR_EVENT_USER; + + if (get_user_preferences('calendar_persistflt', 0, $user) === 0) { + global $SESSION; + if (!isset($SESSION->calendarshoweventtype)) { + $SESSION->calendarshoweventtype = $default; + } + return $SESSION->calendarshoweventtype & $type; + } else { + return get_user_preferences('calendar_savedflt', $default, $user) & $type; + } + } + + /** + * Sets the display of the event type given $display. + * + * If $display = true the event type will be shown. + * If $display = false the event type will NOT be shown. + * If $display = null the current value will be toggled and saved. + * + * @param int $type object of CALENDAR_EVENT_XXX + * @param bool $display option to display event type + * @param \stdClass|int $user moodle user object or id, null means current user + */ + public static function set_event_type_display($type, $display = null, $user = null) { + $persist = get_user_preferences('calendar_persistflt', 0, $user); + $default = CALENDAR_EVENT_GLOBAL + CALENDAR_EVENT_COURSE + CALENDAR_EVENT_GROUP + CALENDAR_EVENT_USER; + if ($persist === 0) { + global $SESSION; + if (!isset($SESSION->calendarshoweventtype)) { + $SESSION->calendarshoweventtype = $default; + } + $preference = $SESSION->calendarshoweventtype; + } else { + $preference = get_user_preferences('calendar_savedflt', $default, $user); + } + $current = $preference & $type; + if ($display === null) { + $display = !$current; + } + if ($display && !$current) { + $preference += $type; + } else if (!$display && $current) { + $preference -= $type; + } + if ($persist === 0) { + $SESSION->calendarshoweventtype = $preference; + } else { + if ($preference == $default) { + unset_user_preference('calendar_savedflt', $user); + } else { + set_user_preference('calendar_savedflt', $preference, $user); + } + } + } + + /** + * Get calendar's allowed types + * + * @param \stdClass $allowed list of allowed edit for event type + * @param \stdClass|int $course object of a course or course id + */ + public static function get_allowed_types(&$allowed, $course = null) { + global $USER, $DB; + + $allowed = new \stdClass(); + $allowed->user = has_capability('moodle/calendar:manageownentries', \context_system::instance()); + $allowed->groups = false; + $allowed->courses = false; + $allowed->site = has_capability('moodle/calendar:manageentries', \context_course::instance(SITEID)); + + if (!empty($course)) { + if (!is_object($course)) { + $course = $DB->get_record('course', array('id' => $course), '*', MUST_EXIST); + } + if ($course->id != SITEID) { + $coursecontext = \context_course::instance($course->id); + $allowed->user = has_capability('moodle/calendar:manageownentries', $coursecontext); + + if (has_capability('moodle/calendar:manageentries', $coursecontext)) { + $allowed->courses = array($course->id => 1); + + if ($course->groupmode != NOGROUPS || !$course->groupmodeforce) { + if (has_capability('moodle/site:accessallgroups', $coursecontext)) { + $allowed->groups = groups_get_all_groups($course->id); + } else { + $allowed->groups = groups_get_all_groups($course->id, $USER->id); + } + } + } else if (has_capability('moodle/calendar:managegroupentries', $coursecontext)) { + if ($course->groupmode != NOGROUPS || !$course->groupmodeforce) { + if (has_capability('moodle/site:accessallgroups', $coursecontext)) { + $allowed->groups = groups_get_all_groups($course->id); + } else { + $allowed->groups = groups_get_all_groups($course->id, $USER->id); + } + } + } + } + } + } + + /** + * See if user can add calendar entries at all + * used to print the "New Event" button + * + * @param \stdClass $course object of a course or course id + * @return bool has the capability to add at least one event type + */ + public static function can_add_event_to_course($course) { + if (!isloggedin() || isguestuser()) { + return false; + } + + self::get_allowed_types($allowed, $course); + + return (bool)($allowed->user || $allowed->groups || $allowed->courses || $allowed->site); + } + + /** + * Check wether the current user is permitted to add events + * + * @param \stdClass $event object of event + * @return bool has the capability to add event + */ + public static function can_add_event($event) { + global $USER, $DB; + + // Can not be using guest account. + if (!isloggedin() or isguestuser()) { + return false; + } + + $sitecontext = \context_system::instance(); + + // If user has manageentries at site level, always return true. + if (has_capability('moodle/calendar:manageentries', $sitecontext)) { + return true; + } + + switch ($event->eventtype) { + case 'course': + return has_capability('moodle/calendar:manageentries', $event->context); + case 'group': + // Allow users to add/edit group events if - + // 1) They have manageentries (= entries for whole course). + // 2) They have managegroupentries AND are in the group. + $group = $DB->get_record('groups', array('id' => $event->groupid)); + return $group && ( + has_capability('moodle/calendar:manageentries', $event->context) || + (has_capability('moodle/calendar:managegroupentries', $event->context) + && groups_is_member($event->groupid))); + case 'user': + if ($event->userid == $USER->id) { + return (has_capability('moodle/calendar:manageownentries', $event->context)); + } + // There is intentionally no 'break'. + case 'site': + return has_capability('moodle/calendar:manageentries', $event->context); + default: + return has_capability('moodle/calendar:manageentries', $event->context); + } + } + + /** + * Returns option list for the poll interval setting. + * + * @return array An array of poll interval options. Interval => description. + */ + public static function get_poll_interval_choices() { + return array( + '0' => new \lang_string('never', 'calendar'), + HOURSECS => new \lang_string('hourly', 'calendar'), + DAYSECS => new \lang_string('daily', 'calendar'), + WEEKSECS => new \lang_string('weekly', 'calendar'), + '2628000' => new \lang_string('monthly', 'calendar'), + YEARSECS => new \lang_string('annually', 'calendar') + ); + } + + /** + * Returns option list of available options for the calendar event type, given the current user and course. + * + * @param int $courseid The id of the course + * @return array An array containing the event types the user can create. + */ + public static function get_event_type_choices($courseid) { + $choices = array(); + $allowed = new \stdClass; + self::get_allowed_types($allowed, $courseid); + + if ($allowed->user) { + $choices['user'] = get_string('userevents', 'calendar'); + } + if ($allowed->site) { + $choices['site'] = get_string('siteevents', 'calendar'); + } + if (!empty($allowed->courses)) { + $choices['course'] = get_string('courseevents', 'calendar'); + } + if (!empty($allowed->groups) and is_array($allowed->groups)) { + $choices['group'] = get_string('group'); + } + + return array($choices, $allowed->groups); + } + + /** + * Add an iCalendar subscription to the database. + * + * @param \stdClass $sub The subscription object (e.g. from the form) + * @return int The insert ID, if any. + */ + public static function add_subscription($sub) { + global $DB, $USER, $SITE; + + if ($sub->eventtype === 'site') { + $sub->courseid = $SITE->id; + } else if ($sub->eventtype === 'group' || $sub->eventtype === 'course') { + $sub->courseid = $sub->course; + } else { + // User events. + $sub->courseid = 0; + } + $sub->userid = $USER->id; + + // File subscriptions never update. + if (empty($sub->url)) { + $sub->pollinterval = 0; + } + + if (!empty($sub->name)) { + if (empty($sub->id)) { + $id = $DB->insert_record('event_subscriptions', $sub); + // We cannot cache the data here because $sub is not complete. + $sub->id = $id; + // Trigger event, calendar subscription added. + $eventparams = array('objectid' => $sub->id, + 'context' => self::get_calendar_context($sub), + 'other' => array('eventtype' => $sub->eventtype, 'courseid' => $sub->courseid) + ); + $event = \core\event\calendar_subscription_created::create($eventparams); + $event->trigger(); + return $id; + } else { + // Why are we doing an update here? + self::update_subscription($sub); + return $sub->id; + } + } else { + print_error('errorbadsubscription', 'importcalendar'); + } + } + + /** + * Add an iCalendar event to the Moodle calendar. + * + * @param \stdClass $event The RFC-2445 iCalendar event + * @param int $courseid The course ID + * @param int $subscriptionid The iCalendar subscription ID + * @param string $timezone The X-WR-TIMEZONE iCalendar property if provided + * @return int Code: CALENDAR_IMPORT_EVENT_UPDATED = updated, CALENDAR_IMPORT_EVENT_INSERTED = inserted, 0 = error + */ + public static function add_icalendar_event($event, $courseid, $subscriptionid, $timezone='UTC') { + global $DB; + + // Probably an unsupported X-MICROSOFT-CDO-BUSYSTATUS event. + if (empty($event->properties['SUMMARY'])) { + return 0; + } + + $name = $event->properties['SUMMARY'][0]->value; + $name = str_replace('\n', '
', $name); + $name = str_replace('\\', '', $name); + $name = preg_replace('/\s+/u', ' ', $name); + + $eventrecord = new \stdClass; + $eventrecord->name = clean_param($name, PARAM_NOTAGS); + + if (empty($event->properties['DESCRIPTION'][0]->value)) { + $description = ''; + } else { + $description = $event->properties['DESCRIPTION'][0]->value; + $description = clean_param($description, PARAM_NOTAGS); + $description = str_replace('\n', '
', $description); + $description = str_replace('\\', '', $description); + $description = preg_replace('/\s+/u', ' ', $description); + } + $eventrecord->description = $description; + + // Probably a repeating event with RRULE etc. TODO: skip for now. + if (empty($event->properties['DTSTART'][0]->value)) { + return 0; + } + + if (isset($event->properties['DTSTART'][0]->parameters['TZID'])) { + $tz = $event->properties['DTSTART'][0]->parameters['TZID']; + } else { + $tz = $timezone; + } + $tz = \core_date::normalise_timezone($tz); + $eventrecord->timestart = strtotime($event->properties['DTSTART'][0]->value . ' ' . $tz); + if (empty($event->properties['DTEND'])) { + $eventrecord->timeduration = 0; // No duration if no end time specified. + } else { + if (isset($event->properties['DTEND'][0]->parameters['TZID'])) { + $endtz = $event->properties['DTEND'][0]->parameters['TZID']; + } else { + $endtz = $timezone; + } + $endtz = \core_date::normalise_timezone($endtz); + $eventrecord->timeduration = strtotime($event->properties['DTEND'][0]->value . ' ' . $endtz) - $eventrecord->timestart; + } + + // Check to see if it should be treated as an all day event. + if ($eventrecord->timeduration == DAYSECS) { + // Check to see if the event started at Midnight on the imported calendar. + date_default_timezone_set($timezone); + if (date('H:i:s', $eventrecord->timestart) === "00:00:00") { + // This event should be an all day event. + $eventrecord->timeduration = 0; + } + \core_date::set_default_server_timezone(); + } + + $eventrecord->uuid = $event->properties['UID'][0]->value; + $eventrecord->timemodified = time(); + + // Add the iCal subscription details if required. + // We should never do anything with an event without a subscription reference. + $sub = self::get_subscription($subscriptionid); + $eventrecord->subscriptionid = $subscriptionid; + $eventrecord->userid = $sub->userid; + $eventrecord->groupid = $sub->groupid; + $eventrecord->courseid = $sub->courseid; + $eventrecord->eventtype = $sub->eventtype; + + if ($updaterecord = $DB->get_record('event', array('uuid' => $eventrecord->uuid, + 'subscriptionid' => $eventrecord->subscriptionid))) { + $eventrecord->id = $updaterecord->id; + $return = CALENDAR_IMPORT_EVENT_UPDATED; // Update. + } else { + $return = CALENDAR_IMPORT_EVENT_INSERTED; // Insert. + } + if ($createdevent = event::create($eventrecord, false)) { + if (!empty($event->properties['RRULE'])) { + // Repeating events. + date_default_timezone_set($tz); // Change time zone to parse all events. + $rrule = new rrule_manager($event->properties['RRULE'][0]->value); + $rrule->parse_rrule(); + $rrule->create_events($createdevent); + \core_date::set_default_server_timezone(); // Change time zone back to what it was. + } + return $return; + } else { + return 0; + } + } + + /** + * Update a subscription from the form data in one of the rows in the existing subscriptions table. + * + * @param int $subscriptionid The ID of the subscription we are acting upon. + * @param int $pollinterval The poll interval to use. + * @param int $action The action to be performed. One of update or remove. + * @return string A log of the import progress, including errors + */ + public static function process_subscription_row($subscriptionid, $pollinterval, $action) { + // Fetch the subscription from the database making sure it exists. + $sub = self::get_subscription($subscriptionid); + + // Update or remove the subscription, based on action. + switch ($action) { + case CALENDAR_SUBSCRIPTION_UPDATE: + // Skip updating file subscriptions. + if (empty($sub->url)) { + break; + } + $sub->pollinterval = $pollinterval; + self::update_subscription($sub); + + // Update the events. + return "

" . get_string('subscriptionupdated', 'calendar', $sub->name) . "

" . + self::update_subscription_events($subscriptionid); + case CALENDAR_SUBSCRIPTION_REMOVE: + self::delete_subscription($subscriptionid); + return get_string('subscriptionremoved', 'calendar', $sub->name); + break; + default: + break; + } + return ''; + } + + /** + * Delete subscription and all related events. + * + * @param int|\stdClass $subscription subscription or it's id, which needs to be deleted. + */ + public static function delete_subscription($subscription) { + global $DB; + + if (!is_object($subscription)) { + $subscription = $DB->get_record('event_subscriptions', array('id' => $subscription), '*', MUST_EXIST); + } + + // Delete subscription and related events. + $DB->delete_records('event', array('subscriptionid' => $subscription->id)); + $DB->delete_records('event_subscriptions', array('id' => $subscription->id)); + \cache_helper::invalidate_by_definition('core', 'calendar_subscriptions', array(), array($subscription->id)); + + // Trigger event, calendar subscription deleted. + $eventparams = array('objectid' => $subscription->id, + 'context' => self::get_calendar_context($subscription), + 'other' => array('courseid' => $subscription->courseid) + ); + $event = \core\event\calendar_subscription_deleted::create($eventparams); + $event->trigger(); + } + + /** + * From a URL, fetch the calendar and return an iCalendar object. + * + * @param string $url The iCalendar URL + * @return \iCalendar The iCalendar object + * @throws \moodle_exception + */ + public static function get_icalendar($url) { + global $CFG; + + require_once($CFG->libdir . '/filelib.php'); + + $curl = new \curl(); + $curl->setopt(array('CURLOPT_FOLLOWLOCATION' => 1, 'CURLOPT_MAXREDIRS' => 5)); + $calendar = $curl->get($url); + + // Http code validation should actually be the job of curl class. + if (!$calendar || $curl->info['http_code'] != 200 || !empty($curl->errorno)) { + throw new \moodle_exception('errorinvalidicalurl', 'calendar'); + } + + $ical = new \iCalendar(); + $ical->unserialize($calendar); + + return $ical; + } + + /** + * Import events from an iCalendar object into a course calendar. + * + * @param \iCalendar $ical The iCalendar object. + * @param int $courseid The course ID for the calendar. + * @param int $subscriptionid The subscription ID. + * @return string A log of the import progress, including errors. + */ + public static function import_icalendar_events($ical, $courseid, $subscriptionid = null) { + global $DB; + + $return = ''; + $eventcount = 0; + $updatecount = 0; + + // Large calendars take a while... + if (!CLI_SCRIPT) { + \core_php_time_limit::raise(300); + } + + // Mark all events in a subscription with a zero timestamp. + if (!empty($subscriptionid)) { + $sql = "UPDATE {event} SET timemodified = :time WHERE subscriptionid = :id"; + $DB->execute($sql, array('time' => 0, 'id' => $subscriptionid)); + } + + // Grab the timezone from the iCalendar file to be used later. + if (isset($ical->properties['X-WR-TIMEZONE'][0]->value)) { + $timezone = $ical->properties['X-WR-TIMEZONE'][0]->value; + } else { + $timezone = 'UTC'; + } + + $return = ''; + foreach ($ical->components['VEVENT'] as $event) { + $res = self::add_icalendar_event($event, $courseid, $subscriptionid, $timezone); + switch ($res) { + case CALENDAR_IMPORT_EVENT_UPDATED: + $updatecount++; + break; + case CALENDAR_IMPORT_EVENT_INSERTED: + $eventcount++; + break; + case 0: + $return .= '

' . get_string('erroraddingevent', 'calendar') . ': '; + if (empty($event->properties['SUMMARY'])) { + $return .= '(' . get_string('notitle', 'calendar') . ')'; + } else { + $return .= $event->properties['SUMMARY'][0]->value; + } + $return .= "

\n"; + break; + } + } + + $return .= "

" . get_string('eventsimported', 'calendar', $eventcount) . "

"; + $return .= "

" . get_string('eventsupdated', 'calendar', $updatecount) . "

"; + + // Delete remaining zero-marked events since they're not in remote calendar. + if (!empty($subscriptionid)) { + $deletecount = $DB->count_records('event', array('timemodified' => 0, 'subscriptionid' => $subscriptionid)); + if (!empty($deletecount)) { + $DB->delete_records('event', array('timemodified' => 0, 'subscriptionid' => $subscriptionid)); + $return .= "

" . get_string('eventsdeleted', 'calendar') . ": {$deletecount}

\n"; + } + } + + return $return; + } + + /** + * Fetch a calendar subscription and update the events in the calendar. + * + * @param int $subscriptionid The course ID for the calendar. + * @return string A log of the import progress, including errors. + */ + public static function update_subscription_events($subscriptionid) { + $sub = self::get_subscription($subscriptionid); + + // Don't update a file subscription. + if (empty($sub->url)) { + return 'File subscription not updated.'; + } + + $ical = self::get_icalendar($sub->url); + $return = self::import_icalendar_events($ical, $sub->courseid, $subscriptionid); + $sub->lastupdated = time(); + + self::update_subscription($sub); + + return $return; + } + + /** + * Update a calendar subscription. Also updates the associated cache. + * + * @param \stdClass|array $subscription Subscription record. + * @throws \coding_exception If something goes wrong + */ + public static function update_subscription($subscription) { + global $DB; + + if (is_array($subscription)) { + $subscription = (object)$subscription; + } + if (empty($subscription->id) || !$DB->record_exists('event_subscriptions', array('id' => $subscription->id))) { + throw new \coding_exception('Cannot update a subscription without a valid id'); + } + + $DB->update_record('event_subscriptions', $subscription); + + // Update cache. + $cache = \cache::make('core', 'calendar_subscriptions'); + $cache->set($subscription->id, $subscription); + + // Trigger event, calendar subscription updated. + $eventparams = array('userid' => $subscription->userid, + 'objectid' => $subscription->id, + 'context' => self::get_calendar_context($subscription), + 'other' => array('eventtype' => $subscription->eventtype, 'courseid' => $subscription->courseid) + ); + $event = \core\event\calendar_subscription_updated::create($eventparams); + $event->trigger(); + } + + /** + * Checks to see if the user can edit a given subscription feed. + * + * @param mixed $subscriptionorid Subscription object or id + * @return bool true if current user can edit the subscription else false + */ + public static function can_edit_subscription($subscriptionorid) { + if (is_array($subscriptionorid)) { + $subscription = (object)$subscriptionorid; + } else if (is_object($subscriptionorid)) { + $subscription = $subscriptionorid; + } else { + $subscription = self::get_subscription($subscriptionorid); + } + + $allowed = new \stdClass; + $courseid = $subscription->courseid; + $groupid = $subscription->groupid; + + self::get_allowed_types($allowed, $courseid); + switch ($subscription->eventtype) { + case 'user': + return $allowed->user; + case 'course': + if (isset($allowed->courses[$courseid])) { + return $allowed->courses[$courseid]; + } else { + return false; + } + case 'site': + return $allowed->site; + case 'group': + if (isset($allowed->groups[$groupid])) { + return $allowed->groups[$groupid]; + } else { + return false; + } + default: + return false; + } + } + + /** + * Helper function to determine the context of a calendar subscription. + * Subscriptions can be created in two contexts COURSE, or USER. + * + * @param \stdClass $subscription + * @return \context instance + */ + public static function get_calendar_context($subscription) { + // Determine context based on calendar type. + if ($subscription->eventtype === 'site') { + $context = \context_course::instance(SITEID); + } else if ($subscription->eventtype === 'group' || $subscription->eventtype === 'course') { + $context = \context_course::instance($subscription->courseid); + } else { + $context = \context_user::instance($subscription->userid); + } + return $context; + } +} diff --git a/calendar/classes/event.php b/calendar/classes/event.php index 2c64ac9353e..62d17faa3ea 100644 --- a/calendar/classes/event.php +++ b/calendar/classes/event.php @@ -78,7 +78,7 @@ class event { } if (!empty($data->subscriptionid)) { - $data->subscription = calendar_get_subscription($data->subscriptionid); + $data->subscription = api::get_subscription($data->subscriptionid); } // Default to a user event. @@ -306,7 +306,7 @@ class event { if (empty($this->properties->id) || $this->properties->id < 1) { if ($checkcapability) { - if (!calendar_add_event_allowed($this->properties)) { + if (!\core_calendar\api::can_add_event($this->properties)) { print_error('nopermissiontoupdatecalendar'); } } @@ -417,7 +417,7 @@ class event { } else { if ($checkcapability) { - if (!calendar_edit_event_allowed($this->properties)) { + if (!api::can_edit_event($this->properties)) { print_error('nopermissiontoupdatecalendar'); } } diff --git a/calendar/delete.php b/calendar/delete.php index 8ccd294b4d1..68ca6c8cd4a 100644 --- a/calendar/delete.php +++ b/calendar/delete.php @@ -59,7 +59,7 @@ if (!$course) { } // Check the user has the required capabilities to edit an event -if (!calendar_edit_event_allowed($event)) { +if (!\core_calendar\api::can_edit_event($event)) { print_error('nopermissions'); } diff --git a/calendar/event.php b/calendar/event.php index b300af2751b..12813321f68 100644 --- a/calendar/event.php +++ b/calendar/event.php @@ -94,7 +94,7 @@ if ($courseid != SITEID && !empty($courseid)) { $issite = false; } else { $course = get_site(); - $courses = calendar_get_default_courses(); + $courses = \core_calendar\api::get_default_courses(); $issite = true; } require_login($course, false); @@ -114,7 +114,7 @@ $formoptions = new stdClass; if ($eventid !== 0) { $title = get_string('editevent', 'calendar'); $event = \core_calendar\event::load($eventid); - if (!calendar_edit_event_allowed($event)) { + if (!\core_calendar\api::can_edit_event($event)) { print_error('nopermissions'); } $event->action = $action; @@ -122,7 +122,7 @@ if ($eventid !== 0) { $event->timedurationuntil = $event->timestart + $event->timeduration; $event->count_repeats(); - if (!calendar_add_event_allowed($event)) { + if (!\core_calendar\api::can_add_event($event)) { print_error('nopermissions'); } @@ -134,7 +134,7 @@ if ($eventid !== 0) { } else { $title = get_string('newevent', 'calendar'); - calendar_get_allowed_types($formoptions->eventtypes, $course); + \core_calendar\api::get_allowed_types($formoptions->eventtypes, $course); $event = new stdClass(); $event->action = $action; $event->course = $courseid; @@ -150,7 +150,7 @@ if ($eventid !== 0) { } $event->timestart = $time; $event = new \core_calendar\event($event); - if (!calendar_add_event_allowed($event)) { + if (!\core_calendar\api::can_add_event($event)) { print_error('nopermissions'); } } diff --git a/calendar/export.php b/calendar/export.php index 979c89272b9..302513a0cc7 100644 --- a/calendar/export.php +++ b/calendar/export.php @@ -82,7 +82,7 @@ if ($courseid != SITEID && !empty($courseid)) { $issite = false; } else { $course = get_site(); - $courses = calendar_get_default_courses(); + $courses = \core_calendar\api::get_default_courses(); $issite = true; } require_course_login($course); @@ -108,7 +108,7 @@ if ($issite) { $PAGE->navbar->add($course->shortname, new moodle_url('/course/view.php', array('id'=>$course->id))); } $link = new moodle_url(CALENDAR_URL.'view.php', array('view'=>'upcoming', 'course'=>$calendar->courseid)); -$PAGE->navbar->add(get_string('calendar', 'calendar'), calendar_get_link_href($link, 0, 0, 0, $time)); +$PAGE->navbar->add(get_string('calendar', 'calendar'), \core_calendar\api::get_link_href($link, 0, 0, 0, $time)); $PAGE->navbar->add($pagetitle); $PAGE->set_title($course->shortname.': '.get_string('calendar', 'calendar').': '.$pagetitle); @@ -133,7 +133,7 @@ $formdata = array( // If today it's weekend, give the "next week" option. 'allownextweek' => $weekend & (1 << $now['wday']), // If it's the last week of the month, give the "next month" option. - 'allownextmonth' => calendar_days_in_month($now['mon'], $now['year']) - $now['mday'] < $numberofdaysinweek, + 'allownextmonth' => \core_calendar\api::get_days_in_month($now['mon'], $now['year']) - $now['mday'] < $numberofdaysinweek, // If today it's weekend but tomorrow it isn't, do NOT give the "this week" option. 'allowthisweek' => !(($weekend & (1 << $now['wday'])) && !($weekend & (1 << (($now['wday'] + 1) % $numberofdaysinweek)))) ); diff --git a/calendar/export_execute.php b/calendar/export_execute.php index da5f078e202..6b300ce9a72 100644 --- a/calendar/export_execute.php +++ b/calendar/export_execute.php @@ -60,7 +60,8 @@ if (!empty($generateurl)) { if(!empty($what) && !empty($time)) { if(in_array($what, $allowed_what) && in_array($time, $allowed_time)) { $courses = enrol_get_users_courses($user->id, true, 'id, visible, shortname'); - // Array of courses that we will pass to calendar_get_events() which is initially set to the list of the user's courses. + // Array of courses that we will pass to \core_calendar\api::get_events() which + // is initially set to the list of the user's courses. $paramcourses = $courses; if ($what == 'all' || $what == 'groups') { $groups = array(); @@ -94,12 +95,12 @@ if(!empty($what) && !empty($time)) { switch($time) { case 'weeknow': - $startweekday = calendar_get_starting_weekday(); + $startweekday = \core_calendar\api::get_starting_weekday(); $startmonthday = find_day_in_month($now['mday'] - ($numberofdaysinweek - 1), $startweekday, $now['mon'], $now['year']); $startmonth = $now['mon']; $startyear = $now['year']; - if($startmonthday > calendar_days_in_month($startmonth, $startyear)) { - list($startmonth, $startyear) = calendar_add_month($startmonth, $startyear); + if ($startmonthday > \core_calendar\api::get_days_in_month($startmonth, $startyear)) { + list($startmonth, $startyear) = \core_calendar\api::get_next_month($startmonth, $startyear); $startmonthday = find_day_in_month(1, $startweekday, $startmonth, $startyear); } $gregoriandate = $calendartype->convert_to_gregorian($startyear, $startmonth, $startmonthday); @@ -109,8 +110,8 @@ if(!empty($what) && !empty($time)) { $endmonthday = $startmonthday + $numberofdaysinweek; $endmonth = $startmonth; $endyear = $startyear; - if($endmonthday > calendar_days_in_month($endmonth, $endyear)) { - list($endmonth, $endyear) = calendar_add_month($endmonth, $endyear); + if ($endmonthday > \core_calendar\api::get_days_in_month($endmonth, $endyear)) { + list($endmonth, $endyear) = \core_calendar\api::get_next_month($endmonth, $endyear); $endmonthday = find_day_in_month(1, $startweekday, $endmonth, $endyear); } $gregoriandate = $calendartype->convert_to_gregorian($endyear, $endmonth, $endmonthday); @@ -118,12 +119,12 @@ if(!empty($what) && !empty($time)) { $gregoriandate['hour'], $gregoriandate['minute']); break; case 'weeknext': - $startweekday = calendar_get_starting_weekday(); + $startweekday = \core_calendar\api::get_starting_weekday(); $startmonthday = find_day_in_month($now['mday'] + 1, $startweekday, $now['mon'], $now['year']); $startmonth = $now['mon']; $startyear = $now['year']; - if($startmonthday > calendar_days_in_month($startmonth, $startyear)) { - list($startmonth, $startyear) = calendar_add_month($startmonth, $startyear); + if ($startmonthday > \core_calendar\api::get_days_in_month($startmonth, $startyear)) { + list($startmonth, $startyear) = \core_calendar\api::get_next_month($startmonth, $startyear); $startmonthday = find_day_in_month(1, $startweekday, $startmonth, $startyear); } $gregoriandate = $calendartype->convert_to_gregorian($startyear, $startmonth, $startmonthday); @@ -133,8 +134,8 @@ if(!empty($what) && !empty($time)) { $endmonthday = $startmonthday + $numberofdaysinweek; $endmonth = $startmonth; $endyear = $startyear; - if($endmonthday > calendar_days_in_month($endmonth, $endyear)) { - list($endmonth, $endyear) = calendar_add_month($endmonth, $endyear); + if ($endmonthday > \core_calendar\api::get_days_in_month($endmonth, $endyear)) { + list($endmonth, $endyear) = \core_calendar\api::get_next_month($endmonth, $endyear); $endmonthday = find_day_in_month(1, $startweekday, $endmonth, $endyear); } $gregoriandate = $calendartype->convert_to_gregorian($endyear, $endmonth, $endmonthday); @@ -147,11 +148,11 @@ if(!empty($what) && !empty($time)) { $timestart = make_timestamp($gregoriandate['year'], $gregoriandate['month'], $gregoriandate['day'], $gregoriandate['hour'], $gregoriandate['minute']); - $timeend = $timestart + (calendar_days_in_month($now['mon'], $now['year']) * DAYSECS); + $timeend = $timestart + (\core_calendar\api::get_days_in_month($now['mon'], $now['year']) * DAYSECS); break; case 'monthnext': // Get the next month for this calendar. - list($nextmonth, $nextyear) = calendar_add_month($now['mon'], $now['year']); + list($nextmonth, $nextyear) = \core_calendar\api::get_next_month($now['mon'], $now['year']); // Convert to gregorian. $gregoriandate = $calendartype->convert_to_gregorian($nextyear, $nextmonth, 1); @@ -159,7 +160,7 @@ if(!empty($what) && !empty($time)) { // Create the timestamps. $timestart = make_timestamp($gregoriandate['year'], $gregoriandate['month'], $gregoriandate['day'], $gregoriandate['hour'], $gregoriandate['minute']); - $timeend = $timestart + (calendar_days_in_month($nextmonth, $nextyear) * DAYSECS); + $timeend = $timestart + (\core_calendar\api::get_days_in_month($nextmonth, $nextyear) * DAYSECS); break; case 'recentupcoming': //Events in the last 5 or next 60 days @@ -179,7 +180,7 @@ if(!empty($what) && !empty($time)) { die(); } } -$events = calendar_get_events($timestart, $timeend, $users, $groups, array_keys($paramcourses), false); +$events = \core_calendar\api::get_events($timestart, $timeend, $users, $groups, array_keys($paramcourses), false); $ical = new iCalendar; $ical->add_property('method', 'PUBLISH'); diff --git a/calendar/externallib.php b/calendar/externallib.php index 4c85df7de4f..8a3e13fa212 100644 --- a/calendar/externallib.php +++ b/calendar/externallib.php @@ -81,7 +81,7 @@ class core_calendar_external extends external_api { $eventobj = \core_calendar\event::load($event['eventid']); // Let's check if the user is allowed to delete an event. - if (!calendar_edit_event_allowed($eventobj)) { + if (!\core_calendar\api::can_edit_event($eventobj)) { throw new moodle_exception("nopermissions"); } // Time to do the magic. @@ -228,14 +228,14 @@ class core_calendar_external extends external_api { } // Event list does not check visibility and permissions, we'll check that later. - $eventlist = calendar_get_events($params['options']['timestart'], $params['options']['timeend'], $funcparam['users'], $funcparam['groups'], - $funcparam['courses'], true, $params['options']['ignorehidden']); + $eventlist = \core_calendar\api::get_events($params['options']['timestart'], $params['options']['timeend'], + $funcparam['users'], $funcparam['groups'], $funcparam['courses'], true, $params['options']['ignorehidden']); // WS expects arrays. $events = array(); // We need to get events asked for eventids. - if ($eventsbyid = calendar_get_events_by_id($params['events']['eventids'])) { + if ($eventsbyid = \core_calendar\api::get_events_by_id($params['events']['eventids'])) { $eventlist += $eventsbyid; } @@ -260,7 +260,7 @@ class core_calendar_external extends external_api { (!empty($eventobj->groupid) && in_array($eventobj->groupid, $groups)) || (!empty($eventobj->courseid) && in_array($eventobj->courseid, $courses)) || ($USER->id == $eventobj->userid) || - (calendar_edit_event_allowed($eventid))) { + (\core_calendar\api::can_edit_event($eventid))) { $events[$eventid] = $event; } else { $warnings[] = array('item' => $eventid, 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to view this event'); @@ -373,7 +373,7 @@ class core_calendar_external extends external_api { $eventobj = new \core_calendar\event($event); // Let's check if the user is allowed to delete an event. - if (!calendar_add_event_allowed($eventobj)) { + if (!\core_calendar\api::can_add_event($eventobj)) { $warnings [] = array('item' => $event['name'], 'warningcode' => 'nopermissions', 'message' => 'you do not have permissions to create this event'); continue; } diff --git a/calendar/lib.php b/calendar/lib.php index a3dc727d6c4..103740a94d0 100644 --- a/calendar/lib.php +++ b/calendar/lib.php @@ -125,1919 +125,6 @@ define('CALENDAR_SUBSCRIPTION_REMOVE', 2); */ define('CALENDAR_EVENT_USER_OVERRIDE_PRIORITY', 9999999); -/** - * Return the days of the week - * - * @return array array of days - */ -function calendar_get_days() { - $calendartype = \core_calendar\type_factory::get_calendar_instance(); - return $calendartype->get_weekdays(); -} - -/** - * Get the subscription from a given id - * - * @since Moodle 2.5 - * @param int $id id of the subscription - * @return stdClass Subscription record from DB - * @throws moodle_exception for an invalid id - */ -function calendar_get_subscription($id) { - global $DB; - - $cache = cache::make('core', 'calendar_subscriptions'); - $subscription = $cache->get($id); - if (empty($subscription)) { - $subscription = $DB->get_record('event_subscriptions', array('id' => $id), '*', MUST_EXIST); - // cache the data. - $cache->set($id, $subscription); - } - return $subscription; -} - -/** - * Gets the first day of the week - * - * Used to be define('CALENDAR_STARTING_WEEKDAY', blah); - * - * @return int - */ -function calendar_get_starting_weekday() { - $calendartype = \core_calendar\type_factory::get_calendar_instance(); - return $calendartype->get_starting_weekday(); -} - -/** - * Generates the HTML for a miniature calendar - * - * @param array $courses list of course to list events from - * @param array $groups list of group - * @param array $users user's info - * @param int|bool $calmonth calendar month in numeric, default is set to false - * @param int|bool $calyear calendar month in numeric, default is set to false - * @param string|bool $placement the place/page the calendar is set to appear - passed on the the controls function - * @param int|bool $courseid id of the course the calendar is displayed on - passed on the the controls function - * @param int $time the unixtimestamp representing the date we want to view, this is used instead of $calmonth - * and $calyear to support multiple calendars - * @return string $content return html table for mini calendar - */ -function calendar_get_mini($courses, $groups, $users, $calmonth = false, $calyear = false, $placement = false, - $courseid = false, $time = 0) { - global $CFG, $OUTPUT, $PAGE; - - // Get the calendar type we are using. - $calendartype = \core_calendar\type_factory::get_calendar_instance(); - - $display = new stdClass; - - // Assume we are not displaying this month for now. - $display->thismonth = false; - - $content = ''; - - // Do this check for backwards compatibility. The core should be passing a timestamp rather than month and year. - // If a month and year are passed they will be in Gregorian. - if (!empty($calmonth) && !empty($calyear)) { - // Ensure it is a valid date, else we will just set it to the current timestamp. - if (checkdate($calmonth, 1, $calyear)) { - $time = make_timestamp($calyear, $calmonth, 1); - } else { - $time = time(); - } - $date = usergetdate($time); - if ($calmonth == $date['mon'] && $calyear == $date['year']) { - $display->thismonth = true; - } - // We can overwrite date now with the date used by the calendar type, if it is not Gregorian, otherwise - // there is no need as it is already in Gregorian. - if ($calendartype->get_name() != 'gregorian') { - $date = $calendartype->timestamp_to_date_array($time); - } - } else if (!empty($time)) { - // Get the specified date in the calendar type being used. - $date = $calendartype->timestamp_to_date_array($time); - $thisdate = $calendartype->timestamp_to_date_array(time()); - if ($date['month'] == $thisdate['month'] && $date['year'] == $thisdate['year']) { - $display->thismonth = true; - // If we are the current month we want to set the date to the current date, not the start of the month. - $date = $thisdate; - } - } else { - // Get the current date in the calendar type being used. - $time = time(); - $date = $calendartype->timestamp_to_date_array($time); - $display->thismonth = true; - } - - list($d, $m, $y) = array($date['mday'], $date['mon'], $date['year']); // This is what we want to display. - - // Get Gregorian date for the start of the month. - $gregoriandate = $calendartype->convert_to_gregorian($date['year'], $date['mon'], 1); - - // Store the gregorian date values to be used later. - list($gy, $gm, $gd, $gh, $gmin) = array($gregoriandate['year'], $gregoriandate['month'], $gregoriandate['day'], - $gregoriandate['hour'], $gregoriandate['minute']); - - // Get the max number of days in this month for this calendar type. - $display->maxdays = calendar_days_in_month($m, $y); - // Get the starting week day for this month. - $startwday = dayofweek(1, $m, $y); - // Get the days in a week. - $daynames = calendar_get_days(); - // Store the number of days in a week. - $numberofdaysinweek = $calendartype->get_num_weekdays(); - - // Set the min and max weekday. - $display->minwday = calendar_get_starting_weekday(); - $display->maxwday = $display->minwday + ($numberofdaysinweek - 1); - - // These are used for DB queries, so we want unixtime, so we need to use Gregorian dates. - $display->tstart = make_timestamp($gy, $gm, $gd, $gh, $gmin, 0); - $display->tend = $display->tstart + ($display->maxdays * DAYSECS) - 1; - - // Align the starting weekday to fall in our display range - // This is simple, not foolproof. - if ($startwday < $display->minwday) { - $startwday += $numberofdaysinweek; - } - - // Get the events matching our criteria. Don't forget to offset the timestamps for the user's TZ! - $events = calendar_get_events($display->tstart, $display->tend, $users, $groups, $courses); - - // Set event course class for course events - if (!empty($events)) { - foreach ($events as $eventid => $event) { - if (!empty($event->modulename)) { - $cm = get_coursemodule_from_instance($event->modulename, $event->instance); - if (!\core_availability\info_module::is_user_visible($cm, 0, false)) { - unset($events[$eventid]); - } - } - } - } - - // This is either a genius idea or an idiot idea: in order to not complicate things, we use this rule: if, after - // possibly removing SITEID from $courses, there is only one course left, then clicking on a day in the month - // will also set the $SESSION->cal_courses_shown variable to that one course. Otherwise, we 'd need to add extra - // arguments to this function. - $hrefparams = array(); - if(!empty($courses)) { - $courses = array_diff($courses, array(SITEID)); - if(count($courses) == 1) { - $hrefparams['course'] = reset($courses); - } - } - - // We want to have easy access by day, since the display is on a per-day basis. - calendar_events_by_day($events, $m, $y, $eventsbyday, $durationbyday, $typesbyday, $courses); - - // Accessibility: added summary and elements. - $summary = get_string('calendarheading', 'calendar', userdate($display->tstart, get_string('strftimemonthyear'))); - // Begin table. - $content .= ''; - if (($placement !== false) && ($courseid !== false)) { - $content .= ''; - } - $content .= ''; // Header row: day names - - // Print out the names of the weekdays. - for ($i = $display->minwday; $i <= $display->maxwday; ++$i) { - $pos = $i % $numberofdaysinweek; - $content .= '\n"; - } - - $content .= ''; // End of day names; prepare for day numbers - - // For the table display. $week is the row; $dayweek is the column. - $dayweek = $startwday; - - // Paddding (the first week may have blank days in the beginning) - for($i = $display->minwday; $i < $startwday; ++$i) { - $content .= ''."\n"; - } - - $weekend = CALENDAR_DEFAULT_WEEKEND; - if (isset($CFG->calendar_weekend)) { - $weekend = intval($CFG->calendar_weekend); - } - - // Now display all the calendar - $daytime = strtotime('-1 day', $display->tstart); - for($day = 1; $day <= $display->maxdays; ++$day, ++$dayweek) { - $cellattributes = array(); - $daytime = strtotime('+1 day', $daytime); - if($dayweek > $display->maxwday) { - // We need to change week (table row) - $content .= ''; - $dayweek = $display->minwday; - } - - // Reset vars. - if ($weekend & (1 << ($dayweek % $numberofdaysinweek))) { - // Weekend. This is true no matter what the exact range is. - $class = 'weekend day'; - } else { - // Normal working day. - $class = 'day'; - } - - $eventids = array(); - if (!empty($eventsbyday[$day])) { - $eventids = $eventsbyday[$day]; - } - - if (!empty($durationbyday[$day])) { - $eventids = array_unique(array_merge($eventids, $durationbyday[$day])); - } - - $finishclass = false; - - if (!empty($eventids)) { - // There is at least one event on this day. - - $class .= ' hasevent'; - $hrefparams['view'] = 'day'; - $dayhref = calendar_get_link_href(new moodle_url(CALENDAR_URL . 'view.php', $hrefparams), 0, 0, 0, $daytime); - - $popupcontent = ''; - foreach ($eventids as $eventid) { - if (!isset($events[$eventid])) { - continue; - } - $event = new \core_calendar\event($events[$eventid]); - $popupalt = ''; - $component = 'moodle'; - if (!empty($event->modulename)) { - $popupicon = 'icon'; - $popupalt = $event->modulename; - $component = $event->modulename; - } else if ($event->courseid == SITEID) { // Site event. - $popupicon = 'i/siteevent'; - } else if ($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) { // Course event. - $popupicon = 'i/courseevent'; - } else if ($event->groupid) { // Group event. - $popupicon = 'i/groupevent'; - } else { // Must be a user event. - $popupicon = 'i/userevent'; - } - - if ($event->timeduration) { - $startdate = $calendartype->timestamp_to_date_array($event->timestart); - $enddate = $calendartype->timestamp_to_date_array($event->timestart + $event->timeduration - 1); - if ($enddate['mon'] == $m && $enddate['year'] == $y && $enddate['mday'] == $day) { - $finishclass = true; - } - } - - $dayhref->set_anchor('event_'.$event->id); - - $popupcontent .= html_writer::start_tag('div'); - if ($component == 'moodle') { - $popupcontent .= $OUTPUT->pix_icon($popupicon, $popupalt, $component); - } else { - $popupcontent .= $OUTPUT->image_icon($popupicon, $popupalt, $component); - } - // Show ical source if needed. - if (!empty($event->subscription) && $CFG->calendar_showicalsource) { - $a = new stdClass(); - $a->name = format_string($event->name, true); - $a->source = $event->subscription->name; - $name = get_string('namewithsource', 'calendar', $a); - } else { - if ($finishclass) { - $samedate = $startdate['mon'] == $enddate['mon'] && - $startdate['year'] == $enddate['year'] && - $startdate['mday'] == $enddate['mday']; - - if ($samedate) { - $name = format_string($event->name, true); - } else { - $name = format_string($event->name, true) . ' (' . get_string('eventendtime', 'calendar') . ')'; - } - } else { - $name = format_string($event->name, true); - } - } - $popupcontent .= html_writer::link($dayhref, $name); - $popupcontent .= html_writer::end_tag('div'); - } - - if ($display->thismonth && $day == $d) { - $popupdata = calendar_get_popup(true, $daytime, $popupcontent); - } else { - $popupdata = calendar_get_popup(false, $daytime, $popupcontent); - } - - // Class and cell content - if(isset($typesbyday[$day]['startglobal'])) { - $class .= ' calendar_event_global'; - } else if(isset($typesbyday[$day]['startcourse'])) { - $class .= ' calendar_event_course'; - } else if(isset($typesbyday[$day]['startgroup'])) { - $class .= ' calendar_event_group'; - } else if(isset($typesbyday[$day]['startuser'])) { - $class .= ' calendar_event_user'; - } - if ($finishclass) { - $class .= ' duration_finish'; - } - - $data = array( - 'url' => $dayhref, - 'day' => $day, - 'content' => $popupdata['data-core_calendar-popupcontent'], - 'title' => $popupdata['data-core_calendar-title'] - ); - $cell = $OUTPUT->render_from_template('core_calendar/minicalendar_day_link', $data); - } else { - $cell = $day; - } - - $durationclass = false; - if (isset($typesbyday[$day]['durationglobal'])) { - $durationclass = ' duration_global'; - } else if(isset($typesbyday[$day]['durationcourse'])) { - $durationclass = ' duration_course'; - } else if(isset($typesbyday[$day]['durationgroup'])) { - $durationclass = ' duration_group'; - } else if(isset($typesbyday[$day]['durationuser'])) { - $durationclass = ' duration_user'; - } - if ($durationclass) { - $class .= ' duration '.$durationclass; - } - - // If event has a class set then add it to the table day '; - } - $content .= ''; // Last row ends - - $content .= '
'. calendar_top_controls($placement, array('id' => $courseid, 'time' => $time)) .'
'. - $daynames[$pos]['shortname'] ."
 
tag - // Note: only one colour for minicalendar - if(isset($eventsbyday[$day])) { - foreach($eventsbyday[$day] as $eventid) { - if (!isset($events[$eventid])) { - continue; - } - $event = $events[$eventid]; - if (!empty($event->class)) { - $class .= ' '.$event->class; - } - break; - } - } - - if ($display->thismonth && $day == $d) { - // The current cell is for today - add appropriate classes and additional information for styling. - $class .= ' today'; - $today = get_string('today', 'calendar').' '.userdate(time(), get_string('strftimedayshort')); - - if (!isset($eventsbyday[$day]) && !isset($durationbyday[$day])) { - $class .= ' eventnone'; - $popupdata = calendar_get_popup(true, false); - $data = array( - 'url' => '#', - 'day' => $day, - 'content' => $popupdata['data-core_calendar-popupcontent'], - 'title' => $popupdata['data-core_calendar-title'] - ); - $cell = $OUTPUT->render_from_template('core_calendar/minicalendar_day_link', $data); - } - $cell = get_accesshide($today . ' ') . $cell; - } - - // Just display it - $cellattributes['class'] = $class; - $content .= html_writer::tag('td', $cell, $cellattributes); - } - - // Paddding (the last week may have blank days at the end) - for($i = $dayweek; $i <= $display->maxwday; ++$i) { - $content .= ' 
'; // Tabular display of days ends - return $content; -} - -/** - * Gets the calendar popup - * - * It called at multiple points in from calendar_get_mini. - * Copied and modified from calendar_get_mini. - * - * @param bool $is_today false except when called on the current day. - * @param mixed $event_timestart $events[$eventid]->timestart, OR false if there are no events. - * @param string $popupcontent content for the popup window/layout. - * @return string eventid for the calendar_tooltip popup window/layout. - */ -function calendar_get_popup($today = false, $timestart, $popupcontent = '') { - global $PAGE; - - $popupcaption = ''; - if ($today) { - $popupcaption = get_string('today', 'calendar') . ' '; - } - - if (false === $timestart) { - $popupcaption .= userdate(time(), get_string('strftimedayshort')); - $popupcontent = get_string('eventnone', 'calendar'); - - } else { - $popupcaption .= get_string('eventsfor', 'calendar', userdate($timestart, get_string('strftimedayshort'))); - } - - return array( - 'data-core_calendar-title' => $popupcaption, - 'data-core_calendar-popupcontent' => $popupcontent, - ); -} - -/** - * Gets the calendar upcoming event - * - * @param array $courses array of courses - * @param array|int|bool $groups array of groups, group id or boolean for all/no group events - * @param array|int|bool $users array of users, user id or boolean for all/no user events - * @param int $daysinfuture number of days in the future we 'll look - * @param int $maxevents maximum number of events - * @param int $fromtime start time - * @return array $output array of upcoming events - */ -function calendar_get_upcoming($courses, $groups, $users, $daysinfuture, $maxevents, $fromtime=0) { - global $CFG, $COURSE, $DB; - - $display = new stdClass; - $display->range = $daysinfuture; // How many days in the future we 'll look - $display->maxevents = $maxevents; - - $output = array(); - - // Prepare "course caching", since it may save us a lot of queries - $coursecache = array(); - - $processed = 0; - $now = time(); // We 'll need this later - $usermidnighttoday = usergetmidnight($now); - - if ($fromtime) { - $display->tstart = $fromtime; - } else { - $display->tstart = $usermidnighttoday; - } - - // This works correctly with respect to the user's DST, but it is accurate - // only because $fromtime is always the exact midnight of some day! - $display->tend = usergetmidnight($display->tstart + DAYSECS * $display->range + 3 * HOURSECS) - 1; - - // Get the events matching our criteria - $events = calendar_get_events($display->tstart, $display->tend, $users, $groups, $courses); - - // This is either a genius idea or an idiot idea: in order to not complicate things, we use this rule: if, after - // possibly removing SITEID from $courses, there is only one course left, then clicking on a day in the month - // will also set the $SESSION->cal_courses_shown variable to that one course. Otherwise, we 'd need to add extra - // arguments to this function. - - $hrefparams = array(); - if(!empty($courses)) { - $courses = array_diff($courses, array(SITEID)); - if(count($courses) == 1) { - $hrefparams['course'] = reset($courses); - } - } - - if ($events !== false) { - - $modinfo = get_fast_modinfo($COURSE); - - foreach($events as $event) { - - - if (!empty($event->modulename)) { - if ($event->courseid == $COURSE->id) { - if (isset($modinfo->instances[$event->modulename][$event->instance])) { - $cm = $modinfo->instances[$event->modulename][$event->instance]; - if (!$cm->uservisible) { - continue; - } - } - } else { - if (!$cm = get_coursemodule_from_instance($event->modulename, $event->instance)) { - continue; - } - if (!\core_availability\info_module::is_user_visible($cm, 0, false)) { - continue; - } - } - } - - if ($processed >= $display->maxevents) { - break; - } - - $event->time = calendar_format_event_time($event, $now, $hrefparams); - $output[] = $event; - ++$processed; - } - } - return $output; -} - - -/** - * Get a HTML link to a course. - * - * @param int $courseid the course id - * @return string a link to the course (as HTML); empty if the course id is invalid - */ -function calendar_get_courselink($courseid) { - - if (!$courseid) { - return ''; - } - - calendar_get_course_cached($coursecache, $courseid); - $context = context_course::instance($courseid); - $fullname = format_string($coursecache[$courseid]->fullname, true, array('context' => $context)); - $url = new moodle_url('/course/view.php', array('id' => $courseid)); - $link = html_writer::link($url, $fullname); - - return $link; -} - - -/** - * Add calendar event metadata - * - * @param stdClass $event event info - * @return stdClass $event metadata - */ -function calendar_add_event_metadata($event) { - global $CFG, $OUTPUT; - - //Support multilang in event->name - $event->name = format_string($event->name,true); - - if(!empty($event->modulename)) { // Activity event - // The module name is set. I will assume that it has to be displayed, and - // also that it is an automatically-generated event. And of course that the - // fields for get_coursemodule_from_instance are set correctly. - $module = calendar_get_module_cached($coursecache, $event->modulename, $event->instance); - - if ($module === false) { - return; - } - - $modulename = get_string('modulename', $event->modulename); - if (get_string_manager()->string_exists($event->eventtype, $event->modulename)) { - // will be used as alt text if the event icon - $eventtype = get_string($event->eventtype, $event->modulename); - } else { - $eventtype = ''; - } - $event->icon = $OUTPUT->image_icon('icon', $event->modulename, $eventtype); - - $event->referer = ''.$event->name.''; - $event->courselink = calendar_get_courselink($module->course); - $event->cmid = $module->id; - - } else if($event->courseid == SITEID) { // Site event - $event->icon = $OUTPUT->pix_icon('i/siteevent', get_string('globalevent', 'calendar')); - $event->cssclass = 'calendar_event_global'; - } else if($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) { // Course event - $event->icon = $OUTPUT->pix_icon('i/courseevent', get_string('courseevent', 'calendar')); - $event->courselink = calendar_get_courselink($event->courseid); - $event->cssclass = 'calendar_event_course'; - } else if ($event->groupid) { // Group event - if ($group = calendar_get_group_cached($event->groupid)) { - $groupname = format_string($group->name, true, context_course::instance($group->courseid)); - } else { - $groupname = ''; - } - $event->icon = $OUTPUT->pix_icon('i/groupevent', get_string('groupevent', 'calendar')); - $event->courselink = calendar_get_courselink($event->courseid) . ', ' . $groupname; - $event->cssclass = 'calendar_event_group'; - } else if($event->userid) { // User event - $event->icon = $OUTPUT->pix_icon('i/userevent', get_string('userevent', 'calendar')); - $event->cssclass = 'calendar_event_user'; - } - return $event; -} - -/** - * Get calendar events - * - * @param int $tstart Start time of time range for events - * @param int $tend End time of time range for events - * @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 boolean $withduration whether only events starting within time range selected - * or events in progress/already started selected as well - * @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) - */ -function calendar_get_events($tstart, $tend, $users, $groups, $courses, $withduration = true, $ignorehidden = true) { - global $DB; - - $params = array(); - // Quick test. - if (empty($users) && empty($groups) && empty($courses)) { - return array(); - } - - // Array of filter conditions. To be concatenated by the OR operator. - $filters = []; - - // User filter. - if ((is_array($users) && !empty($users)) or is_numeric($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)"; - $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)"; - } - // Boolean false (no users at all): We don't need to do anything. - - // Group filter. - if ((is_array($groups) && !empty($groups)) or is_numeric($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)) or is_numeric($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)"; - } - - // 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)"]; - - // Time clause. - if ($withduration) { - $timeclause = "((e.timestart >= :tstart1 OR e.timestart + e.timeduration > :tstart2) AND e.timestart <= :tend)"; - $params['tstart1'] = $tstart; - $params['tstart2'] = $tstart; - $params['tend'] = $tend; - } else { - $timeclause = "(e.timestart >= :tstart AND e.timestart <= :tend)"; - $params['tstart'] = $tstart; - $params['tend'] = $tend; - } - $whereconditions[] = $timeclause; - - // 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. - $subquerywhere = ''; - $subqueryconditions = []; - - // Get the user's courses. Otherwise, get the default courses being shown by the calendar. - $usercourses = calendar_get_default_courses(); - - // Set calendar filters. - list($usercourses, $usergroups, $user) = calendar_set_filters($usercourses, true); - $subqueryparams = []; - - // Flag to indicate whether the query needs to exclude group overrides. - $viewgroupsonly = false; - - if ($user) { - // Set filter condition for the user's events. - $subqueryconditions[] = "(ev.userid = :user AND ev.courseid = 0 AND ev.groupid = 0)"; - $subqueryparams['user'] = $user; - - foreach ($usercourses as $courseid) { - if (has_capability('moodle/site:accessallgroups', context_course::instance($courseid))) { - $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. - $subqueryconditions[] = "(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); - $subqueryconditions[] = "(ev.groupid $inusergroups)"; - $subqueryparams = array_merge($subqueryparams, $inusergroupparams); - } - - // Get courses to be used for the subquery. - $subquerycourses = []; - if (is_array($courses)) { - $subquerycourses = $courses; - } else if (is_numeric($courses)) { - $subquerycourses[] = $courses; - } - // Merge with user courses, if necessary. - if (!empty($usercourses)) { - $subquerycourses = array_merge($subquerycourses, $usercourses); - // 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); - $subqueryconditions[] = "(ev.groupid = 0 AND ev.courseid $incourses)"; - $subqueryparams = array_merge($subqueryparams, $incoursesparams); - } - - // Build the WHERE condition for the sub-query. - if (!empty($subqueryconditions)) { - $subquerywhere = 'WHERE ' . implode(" OR ", $subqueryconditions); - } - - // 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, - MAX(ev.priority) as priority - FROM {event} ev - $subquerywhere - GROUP BY ev.modulename, ev.instance, ev.eventtype"; - - // Build the main query. - $sql = "SELECT e.* - 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 - WHERE (m.visible = 1 OR m.visible IS NULL) AND $whereclause - ORDER BY e.timestart"; - $events = $DB->get_records_sql($sql, $params); - - if ($events === false) { - $events = array(); - } - return $events; -} - -/** Get calendar events by id - * - * @since Moodle 2.5 - * @param array $eventids list of event ids - * @return array Array of event entries, empty array if nothing found - */ - -function calendar_get_events_by_id($eventids) { - global $DB; - - if (!is_array($eventids) || empty($eventids)) { - return array(); - } - list($wheresql, $params) = $DB->get_in_or_equal($eventids); - $wheresql = "id $wheresql"; - - return $DB->get_records_select('event', $wheresql, $params); -} - -/** - * Get control options for Calendar - * - * @param string $type of calendar - * @param array $data calendar information - * @return string $content return available control for the calender in html - */ -function calendar_top_controls($type, $data) { - global $PAGE, $OUTPUT; - - // Get the calendar type we are using. - $calendartype = \core_calendar\type_factory::get_calendar_instance(); - - $content = ''; - - // Ensure course id passed if relevant. - $courseid = ''; - if (!empty($data['id'])) { - $courseid = '&course='.$data['id']; - } - - // If we are passing a month and year then we need to convert this to a timestamp to - // support multiple calendars. No where in core should these be passed, this logic - // here is for third party plugins that may use this function. - if (!empty($data['m']) && !empty($date['y'])) { - if (!isset($data['d'])) { - $data['d'] = 1; - } - if (!checkdate($data['m'], $data['d'], $data['y'])) { - $time = time(); - } else { - $time = make_timestamp($data['y'], $data['m'], $data['d']); - } - } else if (!empty($data['time'])) { - $time = $data['time']; - } else { - $time = time(); - } - - // Get the date for the calendar type. - $date = $calendartype->timestamp_to_date_array($time); - - $urlbase = $PAGE->url; - - // We need to get the previous and next months in certain cases. - if ($type == 'frontpage' || $type == 'course' || $type == 'month') { - $prevmonth = calendar_sub_month($date['mon'], $date['year']); - $prevmonthtime = $calendartype->convert_to_gregorian($prevmonth[1], $prevmonth[0], 1); - $prevmonthtime = make_timestamp($prevmonthtime['year'], $prevmonthtime['month'], $prevmonthtime['day'], - $prevmonthtime['hour'], $prevmonthtime['minute']); - - $nextmonth = calendar_add_month($date['mon'], $date['year']); - $nextmonthtime = $calendartype->convert_to_gregorian($nextmonth[1], $nextmonth[0], 1); - $nextmonthtime = make_timestamp($nextmonthtime['year'], $nextmonthtime['month'], $nextmonthtime['day'], - $nextmonthtime['hour'], $nextmonthtime['minute']); - } - - switch ($type) { - case 'frontpage': - $prevlink = calendar_get_link_previous(get_string('monthprev', 'access'), $urlbase, false, false, false, true, $prevmonthtime); - $nextlink = calendar_get_link_next(get_string('monthnext', 'access'), $urlbase, false, false, false, true, $nextmonthtime); - $calendarlink = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', array('view' => 'month')), false, false, false, $time); - - if (!empty($data['id'])) { - $calendarlink->param('course', $data['id']); - } - - $prevlink = $prevlink; - $right = $nextlink; - - $content .= html_writer::start_tag('div', array('class'=>'calendar-controls')); - $content .= $prevlink.' | '; - $content .= html_writer::tag('span', html_writer::link($calendarlink, userdate($time, get_string('strftimemonthyear')), array('title'=>get_string('monththis','calendar'))), array('class'=>'current')); - $content .= ' | '. $right; - $content .= "\n"; - $content .= html_writer::end_tag('div'); - - break; - case 'course': - $prevlink = calendar_get_link_previous(get_string('monthprev', 'access'), $urlbase, false, false, false, true, $prevmonthtime); - $nextlink = calendar_get_link_next(get_string('monthnext', 'access'), $urlbase, false, false, false, true, $nextmonthtime); - $calendarlink = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', array('view' => 'month')), false, false, false, $time); - - if (!empty($data['id'])) { - $calendarlink->param('course', $data['id']); - } - - $content .= html_writer::start_tag('div', array('class'=>'calendar-controls')); - $content .= $prevlink.' | '; - $content .= html_writer::tag('span', html_writer::link($calendarlink, userdate($time, get_string('strftimemonthyear')), array('title'=>get_string('monththis','calendar'))), array('class'=>'current')); - $content .= ' | '. $nextlink; - $content .= ""; - $content .= html_writer::end_tag('div'); - break; - case 'upcoming': - $calendarlink = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', array('view' => 'upcoming')), false, false, false, $time); - if (!empty($data['id'])) { - $calendarlink->param('course', $data['id']); - } - $calendarlink = html_writer::link($calendarlink, userdate($time, get_string('strftimemonthyear'))); - $content .= html_writer::tag('div', $calendarlink, array('class'=>'centered')); - break; - case 'display': - $calendarlink = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', array('view' => 'month')), false, false, false, $time); - if (!empty($data['id'])) { - $calendarlink->param('course', $data['id']); - } - $calendarlink = html_writer::link($calendarlink, userdate($time, get_string('strftimemonthyear'))); - $content .= html_writer::tag('h3', $calendarlink); - break; - case 'month': - $prevlink = calendar_get_link_previous(userdate($prevmonthtime, get_string('strftimemonthyear')), 'view.php?view=month'.$courseid.'&', false, false, false, false, $prevmonthtime); - $nextlink = calendar_get_link_next(userdate($nextmonthtime, get_string('strftimemonthyear')), 'view.php?view=month'.$courseid.'&', false, false, false, false, $nextmonthtime); - - $content .= html_writer::start_tag('div', array('class'=>'calendar-controls')); - $content .= $prevlink . ' | '; - $content .= $OUTPUT->heading(userdate($time, get_string('strftimemonthyear')), 2, 'current'); - $content .= ' | ' . $nextlink; - $content .= ''; - $content .= html_writer::end_tag('div')."\n"; - break; - case 'day': - $days = calendar_get_days(); - - $prevtimestamp = strtotime('-1 day', $time); - $nexttimestamp = strtotime('+1 day', $time); - - $prevdate = $calendartype->timestamp_to_date_array($prevtimestamp); - $nextdate = $calendartype->timestamp_to_date_array($nexttimestamp); - - $prevname = $days[$prevdate['wday']]['fullname']; - $nextname = $days[$nextdate['wday']]['fullname']; - $prevlink = calendar_get_link_previous($prevname, 'view.php?view=day'.$courseid.'&', false, false, false, false, $prevtimestamp); - $nextlink = calendar_get_link_next($nextname, 'view.php?view=day'.$courseid.'&', false, false, false, false, $nexttimestamp); - - $content .= html_writer::start_tag('div', array('class'=>'calendar-controls')); - $content .= $prevlink; - $content .= ' | '.userdate($time, get_string('strftimedaydate')).''; - $content .= ' | '. $nextlink; - $content .= ""; - $content .= html_writer::end_tag('div')."\n"; - - break; - } - return $content; -} - -/** - * Formats a filter control element. - * - * @param moodle_url $url of the filter - * @param int $type constant defining the type filter - * @return string html content of the element - */ -function calendar_filter_controls_element(moodle_url $url, $type) { - global $OUTPUT; - switch ($type) { - case CALENDAR_EVENT_GLOBAL: - $typeforhumans = 'global'; - $class = 'calendar_event_global'; - break; - case CALENDAR_EVENT_COURSE: - $typeforhumans = 'course'; - $class = 'calendar_event_course'; - break; - case CALENDAR_EVENT_GROUP: - $typeforhumans = 'groups'; - $class = 'calendar_event_group'; - break; - case CALENDAR_EVENT_USER: - $typeforhumans = 'user'; - $class = 'calendar_event_user'; - break; - } - if (calendar_show_event_type($type)) { - $icon = $OUTPUT->pix_icon('t/hide', get_string('hide')); - $str = get_string('hide'.$typeforhumans.'events', 'calendar'); - } else { - $icon = $OUTPUT->pix_icon('t/show', get_string('show')); - $str = get_string('show'.$typeforhumans.'events', 'calendar'); - } - $content = html_writer::start_tag('li', array('class' => 'calendar_event')); - $content .= html_writer::start_tag('a', array('href' => $url, 'rel' => 'nofollow')); - $content .= html_writer::tag('span', $icon, array('class' => $class)); - $content .= html_writer::tag('span', $str, array('class' => 'eventname')); - $content .= html_writer::end_tag('a'); - $content .= html_writer::end_tag('li'); - return $content; -} - -/** - * Get the controls filter for calendar. - * - * Filter is used to hide calendar info from the display page - * - * @param moodle_url $returnurl return-url for filter controls - * @return string $content return filter controls in html - */ -function calendar_filter_controls(moodle_url $returnurl) { - global $CFG, $USER, $OUTPUT; - - $groupevents = true; - $id = optional_param( 'id',0,PARAM_INT ); - $seturl = new moodle_url('/calendar/set.php', array('return' => base64_encode($returnurl->out_as_local_url(false)), 'sesskey'=>sesskey())); - $content = html_writer::start_tag('ul'); - - $seturl->param('var', 'showglobal'); - $content .= calendar_filter_controls_element($seturl, CALENDAR_EVENT_GLOBAL); - - $seturl->param('var', 'showcourses'); - $content .= calendar_filter_controls_element($seturl, CALENDAR_EVENT_COURSE); - - if (isloggedin() && !isguestuser()) { - if ($groupevents) { - // This course MIGHT have group events defined, so show the filter - $seturl->param('var', 'showgroups'); - $content .= calendar_filter_controls_element($seturl, CALENDAR_EVENT_GROUP); - } else { - // This course CANNOT have group events, so lose the filter - } - $seturl->param('var', 'showuser'); - $content .= calendar_filter_controls_element($seturl, CALENDAR_EVENT_USER); - } - $content .= html_writer::end_tag('ul'); - - return $content; -} - -/** - * Return the representation day - * - * @param int $tstamp Timestamp in GMT - * @param int $now current Unix timestamp - * @param bool $usecommonwords - * @return string the formatted date/time - */ -function calendar_day_representation($tstamp, $now = false, $usecommonwords = true) { - - static $shortformat; - if(empty($shortformat)) { - $shortformat = get_string('strftimedayshort'); - } - - if($now === false) { - $now = time(); - } - - // To have it in one place, if a change is needed - $formal = userdate($tstamp, $shortformat); - - $datestamp = usergetdate($tstamp); - $datenow = usergetdate($now); - - if($usecommonwords == false) { - // We don't want words, just a date - return $formal; - } - else if($datestamp['year'] == $datenow['year'] && $datestamp['yday'] == $datenow['yday']) { - // Today - return get_string('today', 'calendar'); - } - else if( - ($datestamp['year'] == $datenow['year'] && $datestamp['yday'] == $datenow['yday'] - 1 ) || - ($datestamp['year'] == $datenow['year'] - 1 && $datestamp['mday'] == 31 && $datestamp['mon'] == 12 && $datenow['yday'] == 1) - ) { - // Yesterday - return get_string('yesterday', 'calendar'); - } - else if( - ($datestamp['year'] == $datenow['year'] && $datestamp['yday'] == $datenow['yday'] + 1 ) || - ($datestamp['year'] == $datenow['year'] + 1 && $datenow['mday'] == 31 && $datenow['mon'] == 12 && $datestamp['yday'] == 1) - ) { - // Tomorrow - return get_string('tomorrow', 'calendar'); - } - else { - return $formal; - } -} - -/** - * return the formatted representation time - * - * @param int $time the timestamp in UTC, as obtained from the database - * @return string the formatted date/time - */ -function calendar_time_representation($time) { - static $langtimeformat = NULL; - if($langtimeformat === NULL) { - $langtimeformat = get_string('strftimetime'); - } - $timeformat = get_user_preferences('calendar_timeformat'); - if(empty($timeformat)){ - $timeformat = get_config(NULL,'calendar_site_timeformat'); - } - // The ? is needed because the preference might be present, but empty - return userdate($time, empty($timeformat) ? $langtimeformat : $timeformat); -} - -/** - * Adds day, month, year arguments to a URL and returns a moodle_url object. - * - * @param string|moodle_url $linkbase - * @param int $d The number of the day. - * @param int $m The number of the month. - * @param int $y The number of the year. - * @param int $time the unixtime, used for multiple calendar support. The values $d, - * $m and $y are kept for backwards compatibility. - * @return moodle_url|null $linkbase - */ -function calendar_get_link_href($linkbase, $d, $m, $y, $time = 0) { - if (empty($linkbase)) { - return ''; - } - if (!($linkbase instanceof moodle_url)) { - $linkbase = new moodle_url($linkbase); - } - - // If a day, month and year were passed then convert it to a timestamp. If these were passed - // then we can assume the day, month and year are passed as Gregorian, as no where in core - // should we be passing these values rather than the time. - if (!empty($d) && !empty($m) && !empty($y)) { - if (checkdate($m, $d, $y)) { - $time = make_timestamp($y, $m, $d); - } else { - $time = time(); - } - } else if (empty($time)) { - $time = time(); - } - - $linkbase->param('time', $time); - - return $linkbase; -} - -/** - * Build and return a previous month HTML link, with an arrow. - * - * @param string $text The text label. - * @param string|moodle_url $linkbase The URL stub. - * @param int $d The number of the date. - * @param int $m The number of the month. - * @param int $y year The number of the year. - * @param bool $accesshide Default visible, or hide from all except screenreaders. - * @param int $time the unixtime, used for multiple calendar support. The values $d, - * $m and $y are kept for backwards compatibility. - * @return string HTML string. - */ -function calendar_get_link_previous($text, $linkbase, $d, $m, $y, $accesshide = false, $time = 0) { - $href = calendar_get_link_href(new moodle_url($linkbase), $d, $m, $y, $time); - if (empty($href)) { - return $text; - } - return link_arrow_left($text, (string)$href, $accesshide, 'previous'); -} - -/** - * Build and return a next month HTML link, with an arrow. - * - * @param string $text The text label. - * @param string|moodle_url $linkbase The URL stub. - * @param int $d the number of the Day - * @param int $m The number of the month. - * @param int $y The number of the year. - * @param bool $accesshide Default visible, or hide from all except screenreaders. - * @param int $time the unixtime, used for multiple calendar support. The values $d, - * $m and $y are kept for backwards compatibility. - * @return string HTML string. - */ -function calendar_get_link_next($text, $linkbase, $d, $m, $y, $accesshide = false, $time = 0) { - $href = calendar_get_link_href(new moodle_url($linkbase), $d, $m, $y, $time); - if (empty($href)) { - return $text; - } - return link_arrow_right($text, (string)$href, $accesshide, 'next'); -} - -/** - * Return the name of the weekday - * - * @param string $englishname - * @return string of the weekeday - */ -function calendar_wday_name($englishname) { - return get_string(strtolower($englishname), 'calendar'); -} - -/** - * Return the number of days in month - * - * @param int $month the number of the month. - * @param int $year the number of the year - * @return int - */ -function calendar_days_in_month($month, $year) { - $calendartype = \core_calendar\type_factory::get_calendar_instance(); - return $calendartype->get_num_days_in_month($year, $month); -} - -/** - * Get the upcoming event block - * - * @param array $events list of events - * @param moodle_url|string $linkhref link to event referer - * @param boolean $showcourselink whether links to courses should be shown - * @return string|null $content html block content - */ -function calendar_get_block_upcoming($events, $linkhref = NULL, $showcourselink = false) { - $content = ''; - $lines = count($events); - if (!$lines) { - return $content; - } - - for ($i = 0; $i < $lines; ++$i) { - if (!isset($events[$i]->time)) { // Just for robustness - continue; - } - $events[$i] = calendar_add_event_metadata($events[$i]); - $content .= '
'.$events[$i]->icon.''; - if (!empty($events[$i]->referer)) { - // That's an activity event, so let's provide the hyperlink - $content .= $events[$i]->referer; - } else { - if(!empty($linkhref)) { - $href = calendar_get_link_href(new moodle_url(CALENDAR_URL . $linkhref), 0, 0, 0, $events[$i]->timestart); - $href->set_anchor('event_'.$events[$i]->id); - $content .= html_writer::link($href, $events[$i]->name); - } - else { - $content .= $events[$i]->name; - } - } - $events[$i]->time = str_replace('»', '
»', $events[$i]->time); - if ($showcourselink && !empty($events[$i]->courselink)) { - $content .= html_writer::div($events[$i]->courselink, 'course'); - } - $content .= '
'.$events[$i]->time.'
'; - if ($i < $lines - 1) $content .= '
'; - } - - return $content; -} - -/** - * Get the next following month - * - * @param int $month the number of the month. - * @param int $year the number of the year. - * @return array the following month - */ -function calendar_add_month($month, $year) { - // Get the calendar type we are using. - $calendartype = \core_calendar\type_factory::get_calendar_instance(); - return $calendartype->get_next_month($year, $month); -} - -/** - * Get the previous month. - * - * @param int $month the number of the month. - * @param int $year the number of the year. - * @return array previous month - */ -function calendar_sub_month($month, $year) { - // Get the calendar type we are using. - $calendartype = \core_calendar\type_factory::get_calendar_instance(); - return $calendartype->get_prev_month($year, $month); -} - -/** - * Get per-day basis events - * - * @param array $events list of events - * @param int $month the number of the month - * @param int $year the number of the year - * @param array $eventsbyday event on specific day - * @param array $durationbyday duration of the event in days - * @param array $typesbyday event type (eg: global, course, user, or group) - * @param array $courses list of courses - * @return void - */ -function calendar_events_by_day($events, $month, $year, &$eventsbyday, &$durationbyday, &$typesbyday, &$courses) { - // Get the calendar type we are using. - $calendartype = \core_calendar\type_factory::get_calendar_instance(); - - $eventsbyday = array(); - $typesbyday = array(); - $durationbyday = array(); - - if($events === false) { - return; - } - - foreach ($events as $event) { - $startdate = $calendartype->timestamp_to_date_array($event->timestart); - // Set end date = start date if no duration - if ($event->timeduration) { - $enddate = $calendartype->timestamp_to_date_array($event->timestart + $event->timeduration - 1); - } else { - $enddate = $startdate; - } - - // Simple arithmetic: $year * 13 + $month is a distinct integer for each distinct ($year, $month) pair - if(!($startdate['year'] * 13 + $startdate['mon'] <= $year * 13 + $month) && ($enddate['year'] * 13 + $enddate['mon'] >= $year * 13 + $month)) { - // Out of bounds - continue; - } - - $eventdaystart = intval($startdate['mday']); - - if($startdate['mon'] == $month && $startdate['year'] == $year) { - // Give the event to its day - $eventsbyday[$eventdaystart][] = $event->id; - - // Mark the day as having such an event - if($event->courseid == SITEID && $event->groupid == 0) { - $typesbyday[$eventdaystart]['startglobal'] = true; - // Set event class for global event - $events[$event->id]->class = 'calendar_event_global'; - } - else if($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) { - $typesbyday[$eventdaystart]['startcourse'] = true; - // Set event class for course event - $events[$event->id]->class = 'calendar_event_course'; - } - else if($event->groupid) { - $typesbyday[$eventdaystart]['startgroup'] = true; - // Set event class for group event - $events[$event->id]->class = 'calendar_event_group'; - } - else if($event->userid) { - $typesbyday[$eventdaystart]['startuser'] = true; - // Set event class for user event - $events[$event->id]->class = 'calendar_event_user'; - } - } - - if($event->timeduration == 0) { - // Proceed with the next - continue; - } - - // The event starts on $month $year or before. So... - $lowerbound = $startdate['mon'] == $month && $startdate['year'] == $year ? intval($startdate['mday']) : 0; - - // Also, it ends on $month $year or later... - $upperbound = $enddate['mon'] == $month && $enddate['year'] == $year ? intval($enddate['mday']) : calendar_days_in_month($month, $year); - - // Mark all days between $lowerbound and $upperbound (inclusive) as duration - for($i = $lowerbound + 1; $i <= $upperbound; ++$i) { - $durationbyday[$i][] = $event->id; - if($event->courseid == SITEID && $event->groupid == 0) { - $typesbyday[$i]['durationglobal'] = true; - } - else if($event->courseid != 0 && $event->courseid != SITEID && $event->groupid == 0) { - $typesbyday[$i]['durationcourse'] = true; - } - else if($event->groupid) { - $typesbyday[$i]['durationgroup'] = true; - } - else if($event->userid) { - $typesbyday[$i]['durationuser'] = true; - } - } - - } - return; -} - -/** - * Get current module cache - * - * @param array $coursecache list of course cache - * @param string $modulename name of the module - * @param int $instance module instance number - * @return stdClass|bool $module information - */ -function calendar_get_module_cached(&$coursecache, $modulename, $instance) { - $module = get_coursemodule_from_instance($modulename, $instance); - - if($module === false) return false; - if(!calendar_get_course_cached($coursecache, $module->course)) { - return false; - } - return $module; -} - -/** - * Get current course cache - * - * @param array $coursecache list of course cache - * @param int $courseid id of the course - * @return stdClass $coursecache[$courseid] return the specific course cache - */ -function calendar_get_course_cached(&$coursecache, $courseid) { - if (!isset($coursecache[$courseid])) { - $coursecache[$courseid] = get_course($courseid); - } - return $coursecache[$courseid]; -} - -/** - * Get group from groupid for calendar display - * - * @param int $groupid - * @return stdClass group object with fields 'id', 'name' and 'courseid' - */ -function calendar_get_group_cached($groupid) { - static $groupscache = array(); - if (!isset($groupscache[$groupid])) { - $groupscache[$groupid] = groups_get_group($groupid, 'id,name,courseid'); - } - return $groupscache[$groupid]; -} - -/** - * Returns the courses to load events for, the - * - * @param array $courseeventsfrom An array of courses to load calendar events for - * @param bool $ignorefilters specify the use of filters, false is set as default - * @return array An array of courses, groups, and user to load calendar events for based upon filters - */ -function calendar_set_filters(array $courseeventsfrom, $ignorefilters = false) { - global $USER, $CFG, $DB; - - // For backwards compatability we have to check whether the courses array contains - // just id's in which case we need to load course objects. - $coursestoload = array(); - foreach ($courseeventsfrom as $id => $something) { - if (!is_object($something)) { - $coursestoload[] = $id; - unset($courseeventsfrom[$id]); - } - } - if (!empty($coursestoload)) { - // TODO remove this in 2.2 - debugging('calendar_set_filters now preferes an array of course objects with preloaded contexts', DEBUG_DEVELOPER); - $courseeventsfrom = array_merge($courseeventsfrom, $DB->get_records_list('course', 'id', $coursestoload)); - } - - $courses = array(); - $user = false; - $group = false; - - // capabilities that allow seeing group events from all groups - // TODO: rewrite so that moodle/calendar:manageentries is not necessary here - $allgroupscaps = array('moodle/site:accessallgroups', 'moodle/calendar:manageentries'); - - $isloggedin = isloggedin(); - - if ($ignorefilters || calendar_show_event_type(CALENDAR_EVENT_COURSE)) { - $courses = array_keys($courseeventsfrom); - } - if ($ignorefilters || calendar_show_event_type(CALENDAR_EVENT_GLOBAL)) { - $courses[] = SITEID; - } - $courses = array_unique($courses); - sort($courses); - - if (!empty($courses) && in_array(SITEID, $courses)) { - // Sort courses for consistent colour highlighting - // Effectively ignoring SITEID as setting as last course id - $key = array_search(SITEID, $courses); - unset($courses[$key]); - $courses[] = SITEID; - } - - if ($ignorefilters || ($isloggedin && calendar_show_event_type(CALENDAR_EVENT_USER))) { - $user = $USER->id; - } - - if (!empty($courseeventsfrom) && (calendar_show_event_type(CALENDAR_EVENT_GROUP) || $ignorefilters)) { - - if (count($courseeventsfrom)==1) { - $course = reset($courseeventsfrom); - if (has_any_capability($allgroupscaps, context_course::instance($course->id))) { - $coursegroups = groups_get_all_groups($course->id, 0, 0, 'g.id'); - $group = array_keys($coursegroups); - } - } - if ($group === false) { - if (!empty($CFG->calendar_adminseesall) && has_any_capability($allgroupscaps, context_system::instance())) { - $group = true; - } else if ($isloggedin) { - $groupids = array(); - - // We already have the courses to examine in $courses - // For each course... - foreach ($courseeventsfrom as $courseid => $course) { - // If the user is an editing teacher in there, - if (!empty($USER->groupmember[$course->id])) { - // We've already cached the users groups for this course so we can just use that - $groupids = array_merge($groupids, $USER->groupmember[$course->id]); - } else if ($course->groupmode != NOGROUPS || !$course->groupmodeforce) { - // If this course has groups, show events from all of those related to the current user - $coursegroups = groups_get_user_groups($course->id, $USER->id); - $groupids = array_merge($groupids, $coursegroups['0']); - } - } - if (!empty($groupids)) { - $group = $groupids; - } - } - } - } - if (empty($courses)) { - $courses = false; - } - - return array($courses, $group, $user); -} - -/** - * Return the capability for editing calendar event - * - * @param \core_calendar\event $event event object - * @return bool capability to edit event - */ -function calendar_edit_event_allowed($event) { - global $USER, $DB; - - // Must be logged in - if (!isloggedin()) { - return false; - } - - // can not be using guest account - if (isguestuser()) { - return false; - } - - // You cannot edit URL based calendar subscription events presently. - if (isset($event->subscriptionid)) { - if (!empty($event->subscription->url)) { - // This event can be updated externally, so it cannot be edited. - return false; - } - } - - $sitecontext = context_system::instance(); - // if user has manageentries at site level, return true - if (has_capability('moodle/calendar:manageentries', $sitecontext)) { - return true; - } - - // if groupid is set, it's definitely a group event - if (!empty($event->groupid)) { - // Allow users to add/edit group events if: - // 1) They have manageentries (= entries for whole course) - // 2) They have managegroupentries AND are in the group - $group = $DB->get_record('groups', array('id'=>$event->groupid)); - return $group && ( - has_capability('moodle/calendar:manageentries', $event->context) || - (has_capability('moodle/calendar:managegroupentries', $event->context) - && groups_is_member($event->groupid))); - } else if (!empty($event->courseid)) { - // if groupid is not set, but course is set, - // it's definiely a course event - return has_capability('moodle/calendar:manageentries', $event->context); - } else if (!empty($event->userid) && $event->userid == $USER->id) { - // if course is not set, but userid id set, it's a user event - return (has_capability('moodle/calendar:manageownentries', $event->context)); - } else if (!empty($event->userid)) { - return (has_capability('moodle/calendar:manageentries', $event->context)); - } - return false; -} - -/** - * Returns the default courses to display on the calendar when there isn't a specific - * course to display. - * - * @return array $courses Array of courses to display - */ -function calendar_get_default_courses() { - global $CFG, $DB; - - if (!isloggedin()) { - return array(); - } - - $courses = array(); - if (!empty($CFG->calendar_adminseesall) && has_capability('moodle/calendar:manageentries', context_system::instance())) { - $select = ', ' . context_helper::get_preload_record_columns_sql('ctx'); - $join = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)"; - $sql = "SELECT c.* $select - FROM {course} c - $join - WHERE EXISTS (SELECT 1 FROM {event} e WHERE e.courseid = c.id) - "; - $courses = $DB->get_records_sql($sql, array('contextlevel' => CONTEXT_COURSE), 0, 20); - foreach ($courses as $course) { - context_helper::preload_from_record($course); - } - return $courses; - } - - $courses = enrol_get_my_courses(); - - return $courses; -} - -/** - * Display calendar preference button - * - * @param stdClass $course course object - * @deprecated since Moodle 3.2 - * @todo MDL-55875 This will be deleted in Moodle 3.6. - * @return string return preference button in html - */ -function calendar_preferences_button(stdClass $course) { - global $OUTPUT; - - // Guests have no preferences - if (!isloggedin() || isguestuser()) { - return ''; - } - debugging('This should no longer be used, the calendar preferences are now linked to the user preferences page'); - - return $OUTPUT->single_button(new moodle_url('/user/calendar.php'), get_string("preferences", "calendar")); -} - -/** - * Get event format time - * - * @param \core_calendar\event $event event object - * @param int $now current time in gmt - * @param array $linkparams list of params for event link - * @param bool $usecommonwords the words as formatted date/time. - * @param int $showtime determine the show time GMT timestamp - * @return string $eventtime link/string for event time - */ -function calendar_format_event_time($event, $now, $linkparams = null, $usecommonwords = true, $showtime = 0) { - $starttime = $event->timestart; - $endtime = $event->timestart + $event->timeduration; - - if (empty($linkparams) || !is_array($linkparams)) { - $linkparams = array(); - } - - $linkparams['view'] = 'day'; - - // OK, now to get a meaningful display... - // Check if there is a duration for this event. - if ($event->timeduration) { - // Get the midnight of the day the event will start. - $usermidnightstart = usergetmidnight($starttime); - // Get the midnight of the day the event will end. - $usermidnightend = usergetmidnight($endtime); - // Check if we will still be on the same day. - if ($usermidnightstart == $usermidnightend) { - // Check if we are running all day. - if ($event->timeduration == DAYSECS) { - $time = get_string('allday', 'calendar'); - } else { // Specify the time we will be running this from. - $datestart = calendar_time_representation($starttime); - $dateend = calendar_time_representation($endtime); - $time = $datestart . ' » ' . $dateend; - } - - // Set printable representation. - if (!$showtime) { - $day = calendar_day_representation($event->timestart, $now, $usecommonwords); - $url = calendar_get_link_href(new moodle_url(CALENDAR_URL . 'view.php', $linkparams), 0, 0, 0, $endtime); - $eventtime = html_writer::link($url, $day) . ', ' . $time; - } else { - $eventtime = $time; - } - } else { // It must spans two or more days. - $daystart = calendar_day_representation($event->timestart, $now, $usecommonwords) . ', '; - if ($showtime == $usermidnightstart) { - $daystart = ''; - } - $timestart = calendar_time_representation($event->timestart); - $dayend = calendar_day_representation($event->timestart + $event->timeduration, $now, $usecommonwords) . ', '; - if ($showtime == $usermidnightend) { - $dayend = ''; - } - $timeend = calendar_time_representation($event->timestart + $event->timeduration); - - // Set printable representation. - if ($now >= $usermidnightstart && $now < strtotime('+1 day', $usermidnightstart)) { - $url = calendar_get_link_href(new moodle_url(CALENDAR_URL . 'view.php', $linkparams), 0, 0, 0, $endtime); - $eventtime = $timestart . ' » ' . html_writer::link($url, $dayend) . $timeend; - } else { - // The event is in the future, print start and end links. - $url = calendar_get_link_href(new moodle_url(CALENDAR_URL . 'view.php', $linkparams), 0, 0, 0, $starttime); - $eventtime = html_writer::link($url, $daystart) . $timestart . ' » '; - - $url = calendar_get_link_href(new moodle_url(CALENDAR_URL . 'view.php', $linkparams), 0, 0, 0, $endtime); - $eventtime .= html_writer::link($url, $dayend) . $timeend; - } - } - } else { // There is no time duration. - $time = calendar_time_representation($event->timestart); - // Set printable representation. - if (!$showtime) { - $day = calendar_day_representation($event->timestart, $now, $usecommonwords); - $url = calendar_get_link_href(new moodle_url(CALENDAR_URL . 'view.php', $linkparams), 0, 0, 0, $starttime); - $eventtime = html_writer::link($url, $day) . ', ' . trim($time); - } else { - $eventtime = $time; - } - } - - // Check if It has expired. - if ($event->timestart + $event->timeduration < $now) { - $eventtime = '' . str_replace(' href=', ' class="dimmed" href=', $eventtime) . ''; - } - - return $eventtime; -} - -/** - * Display month selector options - * - * @param string $name for the select element - * @param string|array $selected options for select elements - */ -function calendar_print_month_selector($name, $selected) { - $months = array(); - for ($i=1; $i<=12; $i++) { - $months[$i] = userdate(gmmktime(12, 0, 0, $i, 15, 2000), '%B'); - } - echo html_writer::label(get_string('months'), 'menu'. $name, false, array('class' => 'accesshide')); - echo html_writer::select($months, $name, $selected, false); -} - -/** - * Checks to see if the requested type of event should be shown for the given user. - * - * @param CALENDAR_EVENT_GLOBAL|CALENDAR_EVENT_COURSE|CALENDAR_EVENT_GROUP|CALENDAR_EVENT_USER $type - * The type to check the display for (default is to display all) - * @param stdClass|int|null $user The user to check for - by default the current user - * @return bool True if the tyep should be displayed false otherwise - */ -function calendar_show_event_type($type, $user = null) { - $default = CALENDAR_EVENT_GLOBAL + CALENDAR_EVENT_COURSE + CALENDAR_EVENT_GROUP + CALENDAR_EVENT_USER; - if (get_user_preferences('calendar_persistflt', 0, $user) === 0) { - global $SESSION; - if (!isset($SESSION->calendarshoweventtype)) { - $SESSION->calendarshoweventtype = $default; - } - return $SESSION->calendarshoweventtype & $type; - } else { - return get_user_preferences('calendar_savedflt', $default, $user) & $type; - } -} - -/** - * Sets the display of the event type given $display. - * - * If $display = true the event type will be shown. - * If $display = false the event type will NOT be shown. - * If $display = null the current value will be toggled and saved. - * - * @param CALENDAR_EVENT_GLOBAL|CALENDAR_EVENT_COURSE|CALENDAR_EVENT_GROUP|CALENDAR_EVENT_USER $type object of CALENDAR_EVENT_XXX - * @param bool $display option to display event type - * @param stdClass|int $user moodle user object or id, null means current user - */ -function calendar_set_event_type_display($type, $display = null, $user = null) { - $persist = get_user_preferences('calendar_persistflt', 0, $user); - $default = CALENDAR_EVENT_GLOBAL + CALENDAR_EVENT_COURSE + CALENDAR_EVENT_GROUP + CALENDAR_EVENT_USER; - if ($persist === 0) { - global $SESSION; - if (!isset($SESSION->calendarshoweventtype)) { - $SESSION->calendarshoweventtype = $default; - } - $preference = $SESSION->calendarshoweventtype; - } else { - $preference = get_user_preferences('calendar_savedflt', $default, $user); - } - $current = $preference & $type; - if ($display === null) { - $display = !$current; - } - if ($display && !$current) { - $preference += $type; - } else if (!$display && $current) { - $preference -= $type; - } - if ($persist === 0) { - $SESSION->calendarshoweventtype = $preference; - } else { - if ($preference == $default) { - unset_user_preference('calendar_savedflt', $user); - } else { - set_user_preference('calendar_savedflt', $preference, $user); - } - } -} - -/** - * Get calendar's allowed types - * - * @param stdClass $allowed list of allowed edit for event type - * @param stdClass|int $course object of a course or course id - */ -function calendar_get_allowed_types(&$allowed, $course = null) { - global $USER, $CFG, $DB; - $allowed = new stdClass(); - $allowed->user = has_capability('moodle/calendar:manageownentries', context_system::instance()); - $allowed->groups = false; // This may change just below - $allowed->courses = false; // This may change just below - $allowed->site = has_capability('moodle/calendar:manageentries', context_course::instance(SITEID)); - - if (!empty($course)) { - if (!is_object($course)) { - $course = $DB->get_record('course', array('id' => $course), '*', MUST_EXIST); - } - if ($course->id != SITEID) { - $coursecontext = context_course::instance($course->id); - $allowed->user = has_capability('moodle/calendar:manageownentries', $coursecontext); - - if (has_capability('moodle/calendar:manageentries', $coursecontext)) { - $allowed->courses = array($course->id => 1); - - if ($course->groupmode != NOGROUPS || !$course->groupmodeforce) { - if (has_capability('moodle/site:accessallgroups', $coursecontext)) { - $allowed->groups = groups_get_all_groups($course->id); - } else { - $allowed->groups = groups_get_all_groups($course->id, $USER->id); - } - } - } else if (has_capability('moodle/calendar:managegroupentries', $coursecontext)) { - if($course->groupmode != NOGROUPS || !$course->groupmodeforce) { - if (has_capability('moodle/site:accessallgroups', $coursecontext)) { - $allowed->groups = groups_get_all_groups($course->id); - } else { - $allowed->groups = groups_get_all_groups($course->id, $USER->id); - } - } - } - } - } -} - -/** - * See if user can add calendar entries at all - * used to print the "New Event" button - * - * @param stdClass $course object of a course or course id - * @return bool has the capability to add at least one event type - */ -function calendar_user_can_add_event($course) { - if (!isloggedin() || isguestuser()) { - return false; - } - calendar_get_allowed_types($allowed, $course); - return (bool)($allowed->user || $allowed->groups || $allowed->courses || $allowed->site); -} - -/** - * Check wether the current user is permitted to add events - * - * @param stdClass $event object of event - * @return bool has the capability to add event - */ -function calendar_add_event_allowed($event) { - global $USER, $DB; - - // can not be using guest account - if (!isloggedin() or isguestuser()) { - return false; - } - - $sitecontext = context_system::instance(); - // if user has manageentries at site level, always return true - if (has_capability('moodle/calendar:manageentries', $sitecontext)) { - return true; - } - - switch ($event->eventtype) { - case 'course': - return has_capability('moodle/calendar:manageentries', $event->context); - - case 'group': - // Allow users to add/edit group events if: - // 1) They have manageentries (= entries for whole course) - // 2) They have managegroupentries AND are in the group - $group = $DB->get_record('groups', array('id'=>$event->groupid)); - return $group && ( - has_capability('moodle/calendar:manageentries', $event->context) || - (has_capability('moodle/calendar:managegroupentries', $event->context) - && groups_is_member($event->groupid))); - - case 'user': - if ($event->userid == $USER->id) { - return (has_capability('moodle/calendar:manageownentries', $event->context)); - } - //there is no 'break;' intentionally - - case 'site': - return has_capability('moodle/calendar:manageentries', $event->context); - - default: - return has_capability('moodle/calendar:manageentries', $event->context); - } -} - /** * Calendar information class * @@ -2117,7 +204,7 @@ class calendar_information { public function prepare_for_view(stdClass $course, array $coursestoload, $ignorefilters = false) { $this->courseid = $course->id; $this->course = $course; - list($courses, $group, $user) = calendar_set_filters($coursestoload, $ignorefilters); + list($courses, $group, $user) = \core_calendar\api::set_filters($coursestoload, $ignorefilters); $this->courses = $courses; $this->groups = $group; $this->users = $user; @@ -2185,486 +272,6 @@ class calendar_information { } } -/** - * Returns option list for the poll interval setting. - * - * @return array An array of poll interval options. Interval => description. - */ -function calendar_get_pollinterval_choices() { - return array( - '0' => new lang_string('never', 'calendar'), - HOURSECS => new lang_string('hourly', 'calendar'), - DAYSECS => new lang_string('daily', 'calendar'), - WEEKSECS => new lang_string('weekly', 'calendar'), - '2628000' => new lang_string('monthly', 'calendar'), - YEARSECS => new lang_string('annually', 'calendar') - ); -} - -/** - * Returns option list of available options for the calendar event type, given the current user and course. - * - * @param int $courseid The id of the course - * @return array An array containing the event types the user can create. - */ -function calendar_get_eventtype_choices($courseid) { - $choices = array(); - $allowed = new stdClass; - calendar_get_allowed_types($allowed, $courseid); - - if ($allowed->user) { - $choices['user'] = get_string('userevents', 'calendar'); - } - if ($allowed->site) { - $choices['site'] = get_string('siteevents', 'calendar'); - } - if (!empty($allowed->courses)) { - $choices['course'] = get_string('courseevents', 'calendar'); - } - if (!empty($allowed->groups) and is_array($allowed->groups)) { - $choices['group'] = get_string('group'); - } - - return array($choices, $allowed->groups); -} - -/** - * Add an iCalendar subscription to the database. - * - * @param stdClass $sub The subscription object (e.g. from the form) - * @return int The insert ID, if any. - */ -function calendar_add_subscription($sub) { - global $DB, $USER, $SITE; - - if ($sub->eventtype === 'site') { - $sub->courseid = $SITE->id; - } else if ($sub->eventtype === 'group' || $sub->eventtype === 'course') { - $sub->courseid = $sub->course; - } else { - // User events. - $sub->courseid = 0; - } - $sub->userid = $USER->id; - - // File subscriptions never update. - if (empty($sub->url)) { - $sub->pollinterval = 0; - } - - if (!empty($sub->name)) { - if (empty($sub->id)) { - $id = $DB->insert_record('event_subscriptions', $sub); - // we cannot cache the data here because $sub is not complete. - $sub->id = $id; - // Trigger event, calendar subscription added. - $eventparams = array('objectid' => $sub->id, - 'context' => calendar_get_calendar_context($sub), - 'other' => array('eventtype' => $sub->eventtype, 'courseid' => $sub->courseid) - ); - $event = \core\event\calendar_subscription_created::create($eventparams); - $event->trigger(); - return $id; - } else { - // Why are we doing an update here? - calendar_update_subscription($sub); - return $sub->id; - } - } else { - print_error('errorbadsubscription', 'importcalendar'); - } -} - -/** - * Add an iCalendar event to the Moodle calendar. - * - * @param stdClass $event The RFC-2445 iCalendar event - * @param int $courseid The course ID - * @param int $subscriptionid The iCalendar subscription ID - * @param string $timezone The X-WR-TIMEZONE iCalendar property if provided - * @throws dml_exception A DML specific exception is thrown for invalid subscriptionids. - * @return int Code: CALENDAR_IMPORT_EVENT_UPDATED = updated, CALENDAR_IMPORT_EVENT_INSERTED = inserted, 0 = error - */ -function calendar_add_icalendar_event($event, $courseid, $subscriptionid, $timezone='UTC') { - global $DB; - - // Probably an unsupported X-MICROSOFT-CDO-BUSYSTATUS event. - if (empty($event->properties['SUMMARY'])) { - return 0; - } - - $name = $event->properties['SUMMARY'][0]->value; - $name = str_replace('\n', '
', $name); - $name = str_replace('\\', '', $name); - $name = preg_replace('/\s+/u', ' ', $name); - - $eventrecord = new stdClass; - $eventrecord->name = clean_param($name, PARAM_NOTAGS); - - if (empty($event->properties['DESCRIPTION'][0]->value)) { - $description = ''; - } else { - $description = $event->properties['DESCRIPTION'][0]->value; - $description = clean_param($description, PARAM_NOTAGS); - $description = str_replace('\n', '
', $description); - $description = str_replace('\\', '', $description); - $description = preg_replace('/\s+/u', ' ', $description); - } - $eventrecord->description = $description; - - // Probably a repeating event with RRULE etc. TODO: skip for now. - if (empty($event->properties['DTSTART'][0]->value)) { - return 0; - } - - $tz = isset($event->properties['DTSTART'][0]->parameters['TZID']) ? $event->properties['DTSTART'][0]->parameters['TZID'] : - $timezone; - $tz = core_date::normalise_timezone($tz); - $eventrecord->timestart = strtotime($event->properties['DTSTART'][0]->value . ' ' . $tz); - if (empty($event->properties['DTEND'])) { - $eventrecord->timeduration = 0; // no duration if no end time specified - } else { - $endtz = isset($event->properties['DTEND'][0]->parameters['TZID']) ? $event->properties['DTEND'][0]->parameters['TZID'] : - $timezone; - $endtz = core_date::normalise_timezone($endtz); - $eventrecord->timeduration = strtotime($event->properties['DTEND'][0]->value . ' ' . $endtz) - $eventrecord->timestart; - } - - // Check to see if it should be treated as an all day event. - if ($eventrecord->timeduration == DAYSECS) { - // Check to see if the event started at Midnight on the imported calendar. - date_default_timezone_set($timezone); - if (date('H:i:s', $eventrecord->timestart) === "00:00:00") { - // This event should be an all day event. - $eventrecord->timeduration = 0; - } - core_date::set_default_server_timezone(); - } - - $eventrecord->uuid = $event->properties['UID'][0]->value; - $eventrecord->timemodified = time(); - - // Add the iCal subscription details if required. - // We should never do anything with an event without a subscription reference. - $sub = calendar_get_subscription($subscriptionid); - $eventrecord->subscriptionid = $subscriptionid; - $eventrecord->userid = $sub->userid; - $eventrecord->groupid = $sub->groupid; - $eventrecord->courseid = $sub->courseid; - $eventrecord->eventtype = $sub->eventtype; - - if ($updaterecord = $DB->get_record('event', array('uuid' => $eventrecord->uuid, 'subscriptionid' => $eventrecord->subscriptionid))) { - $eventrecord->id = $updaterecord->id; - $return = CALENDAR_IMPORT_EVENT_UPDATED; // Update. - } else { - $return = CALENDAR_IMPORT_EVENT_INSERTED; // Insert. - } - if ($createdevent = \core_calendar\event::create($eventrecord, false)) { - if (!empty($event->properties['RRULE'])) { - // Repeating events. - date_default_timezone_set($tz); // Change time zone to parse all events. - $rrule = new \core_calendar\rrule_manager($event->properties['RRULE'][0]->value); - $rrule->parse_rrule(); - $rrule->create_events($createdevent); - core_date::set_default_server_timezone(); // Change time zone back to what it was. - } - return $return; - } else { - return 0; - } -} - -/** - * Update a subscription from the form data in one of the rows in the existing subscriptions table. - * - * @param int $subscriptionid The ID of the subscription we are acting upon. - * @param int $pollinterval The poll interval to use. - * @param int $action The action to be performed. One of update or remove. - * @throws dml_exception if invalid subscriptionid is provided - * @return string A log of the import progress, including errors - */ -function calendar_process_subscription_row($subscriptionid, $pollinterval, $action) { - - // Fetch the subscription from the database making sure it exists. - $sub = calendar_get_subscription($subscriptionid); - - // Update or remove the subscription, based on action. - switch ($action) { - case CALENDAR_SUBSCRIPTION_UPDATE: - // Skip updating file subscriptions. - if (empty($sub->url)) { - break; - } - $sub->pollinterval = $pollinterval; - calendar_update_subscription($sub); - - // Update the events. - return "

".get_string('subscriptionupdated', 'calendar', $sub->name)."

" . calendar_update_subscription_events($subscriptionid); - - case CALENDAR_SUBSCRIPTION_REMOVE: - calendar_delete_subscription($subscriptionid); - return get_string('subscriptionremoved', 'calendar', $sub->name); - break; - - default: - break; - } - return ''; -} - -/** - * Delete subscription and all related events. - * - * @param int|stdClass $subscription subscription or it's id, which needs to be deleted. - */ -function calendar_delete_subscription($subscription) { - global $DB; - - if (!is_object($subscription)) { - $subscription = $DB->get_record('event_subscriptions', array('id' => $subscription), '*', MUST_EXIST); - } - // Delete subscription and related events. - $DB->delete_records('event', array('subscriptionid' => $subscription->id)); - $DB->delete_records('event_subscriptions', array('id' => $subscription->id)); - cache_helper::invalidate_by_definition('core', 'calendar_subscriptions', array(), array($subscription->id)); - - // Trigger event, calendar subscription deleted. - $eventparams = array('objectid' => $subscription->id, - 'context' => calendar_get_calendar_context($subscription), - 'other' => array('courseid' => $subscription->courseid) - ); - $event = \core\event\calendar_subscription_deleted::create($eventparams); - $event->trigger(); -} -/** - * From a URL, fetch the calendar and return an iCalendar object. - * - * @param string $url The iCalendar URL - * @return stdClass The iCalendar object - */ -function calendar_get_icalendar($url) { - global $CFG; - - require_once($CFG->libdir.'/filelib.php'); - - $curl = new curl(); - $curl->setopt(array('CURLOPT_FOLLOWLOCATION' => 1, 'CURLOPT_MAXREDIRS' => 5)); - $calendar = $curl->get($url); - // Http code validation should actually be the job of curl class. - if (!$calendar || $curl->info['http_code'] != 200 || !empty($curl->errorno)) { - throw new moodle_exception('errorinvalidicalurl', 'calendar'); - } - - $ical = new iCalendar(); - $ical->unserialize($calendar); - return $ical; -} - -/** - * Import events from an iCalendar object into a course calendar. - * - * @param stdClass $ical The iCalendar object. - * @param int $courseid The course ID for the calendar. - * @param int $subscriptionid The subscription ID. - * @return string A log of the import progress, including errors. - */ -function calendar_import_icalendar_events($ical, $courseid, $subscriptionid = null) { - global $DB; - $return = ''; - $eventcount = 0; - $updatecount = 0; - - // Large calendars take a while... - if (!CLI_SCRIPT) { - core_php_time_limit::raise(300); - } - - // Mark all events in a subscription with a zero timestamp. - if (!empty($subscriptionid)) { - $sql = "UPDATE {event} SET timemodified = :time WHERE subscriptionid = :id"; - $DB->execute($sql, array('time' => 0, 'id' => $subscriptionid)); - } - // Grab the timezone from the iCalendar file to be used later. - if (isset($ical->properties['X-WR-TIMEZONE'][0]->value)) { - $timezone = $ical->properties['X-WR-TIMEZONE'][0]->value; - } else { - $timezone = 'UTC'; - } - foreach ($ical->components['VEVENT'] as $event) { - $res = calendar_add_icalendar_event($event, $courseid, $subscriptionid, $timezone); - switch ($res) { - case CALENDAR_IMPORT_EVENT_UPDATED: - $updatecount++; - break; - case CALENDAR_IMPORT_EVENT_INSERTED: - $eventcount++; - break; - case 0: - $return .= '

'.get_string('erroraddingevent', 'calendar').': '.(empty($event->properties['SUMMARY'])?'('.get_string('notitle', 'calendar').')':$event->properties['SUMMARY'][0]->value)."

\n"; - break; - } - } - $return .= "

".get_string('eventsimported', 'calendar', $eventcount)."

"; - $return .= "

".get_string('eventsupdated', 'calendar', $updatecount)."

"; - - // Delete remaining zero-marked events since they're not in remote calendar. - if (!empty($subscriptionid)) { - $deletecount = $DB->count_records('event', array('timemodified' => 0, 'subscriptionid' => $subscriptionid)); - if (!empty($deletecount)) { - $sql = "DELETE FROM {event} WHERE timemodified = :time AND subscriptionid = :id"; - $DB->execute($sql, array('time' => 0, 'id' => $subscriptionid)); - $return .= "

".get_string('eventsdeleted', 'calendar').": {$deletecount}

\n"; - } - } - - return $return; -} - -/** - * Fetch a calendar subscription and update the events in the calendar. - * - * @param int $subscriptionid The course ID for the calendar. - * @return string A log of the import progress, including errors. - */ -function calendar_update_subscription_events($subscriptionid) { - global $DB; - - $sub = calendar_get_subscription($subscriptionid); - // Don't update a file subscription. TODO: Update from a new uploaded file. - if (empty($sub->url)) { - return 'File subscription not updated.'; - } - $ical = calendar_get_icalendar($sub->url); - $return = calendar_import_icalendar_events($ical, $sub->courseid, $subscriptionid); - $sub->lastupdated = time(); - calendar_update_subscription($sub); - return $return; -} - -/** - * Update a calendar subscription. Also updates the associated cache. - * - * @param stdClass|array $subscription Subscription record. - * @throws coding_exception If something goes wrong - * @since Moodle 2.5 - */ -function calendar_update_subscription($subscription) { - global $DB; - - if (is_array($subscription)) { - $subscription = (object)$subscription; - } - if (empty($subscription->id) || !$DB->record_exists('event_subscriptions', array('id' => $subscription->id))) { - throw new coding_exception('Cannot update a subscription without a valid id'); - } - - $DB->update_record('event_subscriptions', $subscription); - // Update cache. - $cache = cache::make('core', 'calendar_subscriptions'); - $cache->set($subscription->id, $subscription); - // Trigger event, calendar subscription updated. - $eventparams = array('userid' => $subscription->userid, - 'objectid' => $subscription->id, - 'context' => calendar_get_calendar_context($subscription), - 'other' => array('eventtype' => $subscription->eventtype, 'courseid' => $subscription->courseid) - ); - $event = \core\event\calendar_subscription_updated::create($eventparams); - $event->trigger(); -} - -/** - * Checks to see if the user can edit a given subscription feed. - * - * @param mixed $subscriptionorid Subscription object or id - * @return bool true if current user can edit the subscription else false - */ -function calendar_can_edit_subscription($subscriptionorid) { - global $DB; - - if (is_array($subscriptionorid)) { - $subscription = (object)$subscriptionorid; - } else if (is_object($subscriptionorid)) { - $subscription = $subscriptionorid; - } else { - $subscription = calendar_get_subscription($subscriptionorid); - } - $allowed = new stdClass; - $courseid = $subscription->courseid; - $groupid = $subscription->groupid; - calendar_get_allowed_types($allowed, $courseid); - switch ($subscription->eventtype) { - case 'user': - return $allowed->user; - case 'course': - if (isset($allowed->courses[$courseid])) { - return $allowed->courses[$courseid]; - } else { - return false; - } - case 'site': - return $allowed->site; - case 'group': - if (isset($allowed->groups[$groupid])) { - return $allowed->groups[$groupid]; - } else { - return false; - } - default: - return false; - } -} - -/** - * Update calendar subscriptions. - * - * @return bool - */ -function calendar_cron() { - global $CFG, $DB; - - // In order to execute this we need bennu. - require_once($CFG->libdir.'/bennu/bennu.inc.php'); - - mtrace('Updating calendar subscriptions:'); - cron_trace_time_and_memory(); - - $time = time(); - $subscriptions = $DB->get_records_sql('SELECT * FROM {event_subscriptions} WHERE pollinterval > 0 AND lastupdated + pollinterval < ?', array($time)); - foreach ($subscriptions as $sub) { - mtrace("Updating calendar subscription {$sub->name} in course {$sub->courseid}"); - try { - $log = calendar_update_subscription_events($sub->id); - mtrace(trim(strip_tags($log))); - } catch (moodle_exception $ex) { - mtrace('Error updating calendar subscription: ' . $ex->getMessage()); - } - } - - mtrace('Finished updating calendar subscriptions.'); - - return true; -} - -/** - * Helper function to determine the context of a calendar subscription. - * Subscriptions can be created in two contexts COURSE, or USER. - * - * @param stdClass $subscription - * @return context instance - */ -function calendar_get_calendar_context($subscription) { - - // Determine context based on calendar type. - if ($subscription->eventtype === 'site') { - $context = context_course::instance(SITEID); - } else if ($subscription->eventtype === 'group' || $subscription->eventtype === 'course') { - $context = context_course::instance($subscription->courseid); - } else { - $context = context_user::instance($subscription->userid); - } - return $context; -} - /** * Implements callback user_preferences, whitelists preferences that users are allowed to update directly * diff --git a/calendar/managesubscriptions.php b/calendar/managesubscriptions.php index 3e4e0e98f69..91dea50ed4f 100644 --- a/calendar/managesubscriptions.php +++ b/calendar/managesubscriptions.php @@ -49,10 +49,10 @@ if ($courseid != SITEID && !empty($courseid)) { $courses = array($course->id => $course); } else { $course = get_site(); - $courses = calendar_get_default_courses(); + $courses = \core_calendar\api::get_default_courses(); } require_course_login($course); -if (!calendar_user_can_add_event($course)) { +if (!\core_calendar\api::can_add_event_to_course($course)) { print_error('errorcannotimport', 'calendar'); } @@ -66,20 +66,20 @@ $importresults = ''; $formdata = $form->get_data(); if (!empty($formdata)) { require_sesskey(); // Must have sesskey for all actions. - $subscriptionid = calendar_add_subscription($formdata); + $subscriptionid = \core_calendar\api::add_subscription($formdata); if ($formdata->importfrom == CALENDAR_IMPORT_FROM_FILE) { // Blank the URL if it's a file import. $formdata->url = ''; $calendar = $form->get_file_content('importfile'); $ical = new iCalendar(); $ical->unserialize($calendar); - $importresults = calendar_import_icalendar_events($ical, $courseid, $subscriptionid); + $importresults = \core_calendar\api::import_icalendar_events($ical, $courseid, $subscriptionid); } else { try { - $importresults = calendar_update_subscription_events($subscriptionid); + $importresults = \core_calendar\api::update_subscription_events($subscriptionid); } catch (moodle_exception $e) { // Delete newly added subscription and show invalid url error. - calendar_delete_subscription($subscriptionid); + \core_calendar\api::delete_subscription($subscriptionid); print_error($e->errorcode, $e->module, $PAGE->url); } } @@ -88,9 +88,9 @@ if (!empty($formdata)) { } else if (!empty($subscriptionid)) { // The user is wanting to perform an action upon an existing subscription. require_sesskey(); // Must have sesskey for all actions. - if (calendar_can_edit_subscription($subscriptionid)) { + if (\core_calendar\api::can_edit_subscription($subscriptionid)) { try { - $importresults = calendar_process_subscription_row($subscriptionid, $pollinterval, $action); + $importresults = \core_calendar\api::process_subscription_row($subscriptionid, $pollinterval, $action); } catch (moodle_exception $e) { // If exception caught, then user should be redirected to page where he/she came from. print_error($e->errorcode, $e->module, $PAGE->url); @@ -117,7 +117,7 @@ echo $OUTPUT->header(); // Filter subscriptions which user can't edit. foreach($subscriptions as $subscription) { - if (!calendar_can_edit_subscription($subscription)) { + if (!\core_calendar\api::can_edit_subscription($subscription)) { unset($subscriptions[$subscription->id]); } } diff --git a/calendar/managesubscriptions_form.php b/calendar/managesubscriptions_form.php index 12ab3ac5b74..ea2df718782 100644 --- a/calendar/managesubscriptions_form.php +++ b/calendar/managesubscriptions_form.php @@ -63,7 +63,7 @@ class calendar_addsubscription_form extends moodleform { $mform->setForceLtr('url'); // Poll interval - $choices = calendar_get_pollinterval_choices(); + $choices = \core_calendar\api::get_poll_interval_choices(); $mform->addElement('select', 'pollinterval', get_string('pollinterval', 'calendar'), $choices); $mform->setDefault('pollinterval', 604800); $mform->addHelpButton('pollinterval', 'pollinterval', 'calendar'); @@ -78,7 +78,7 @@ class calendar_addsubscription_form extends moodleform { $mform->disabledIf('importfile', 'importfrom', 'eq', CALENDAR_IMPORT_FROM_URL); // Eventtype: 0 = user, 1 = global, anything else = course ID. - list($choices, $groups) = calendar_get_eventtype_choices($courseid); + list($choices, $groups) = \core_calendar\api::get_event_type_choices($courseid); $mform->addElement('select', 'eventtype', get_string('eventkind', 'calendar'), $choices); $mform->addRule('eventtype', get_string('required'), 'required'); $mform->setType('eventtype', PARAM_ALPHA); diff --git a/calendar/renderer.php b/calendar/renderer.php index 527a1585a47..42721b912d9 100644 --- a/calendar/renderer.php +++ b/calendar/renderer.php @@ -64,7 +64,8 @@ class core_calendar_renderer extends plugin_renderer_base { public function fake_block_filters($courseid, $day, $month, $year, $view, $courses) { $returnurl = $this->page->url; $returnurl->param('course', $courseid); - return html_writer::tag('div', calendar_filter_controls($returnurl), array('class'=>'calendar_filters filters')); + return html_writer::tag('div', \core_calendar\api::get_filter_controls($returnurl), + array('class' => 'calendar_filters filters')); } /** @@ -81,24 +82,27 @@ class core_calendar_renderer extends plugin_renderer_base { $date = $calendartype->timestamp_to_date_array($calendar->time); - $prevmonth = calendar_sub_month($date['mon'], $date['year']); + $prevmonth = \core_calendar\api::get_prev_month($date['mon'], $date['year']); $prevmonthtime = $calendartype->convert_to_gregorian($prevmonth[1], $prevmonth[0], 1); $prevmonthtime = make_timestamp($prevmonthtime['year'], $prevmonthtime['month'], $prevmonthtime['day'], $prevmonthtime['hour'], $prevmonthtime['minute']); - $nextmonth = calendar_add_month($date['mon'], $date['year']); + $nextmonth = \core_calendar\api::get_next_month($date['mon'], $date['year']); $nextmonthtime = $calendartype->convert_to_gregorian($nextmonth[1], $nextmonth[0], 1); $nextmonthtime = make_timestamp($nextmonthtime['year'], $nextmonthtime['month'], $nextmonthtime['day'], $nextmonthtime['hour'], $nextmonthtime['minute']); $content = html_writer::start_tag('div', array('class' => 'minicalendarblock')); - $content .= calendar_get_mini($calendar->courses, $calendar->groups, $calendar->users, false, false, 'display', $calendar->courseid, $prevmonthtime); + $content .= \core_calendar\api::get_mini_calendar($calendar->courses, $calendar->groups, $calendar->users, + false, false, 'display', $calendar->courseid, $prevmonthtime); $content .= html_writer::end_tag('div'); $content .= html_writer::start_tag('div', array('class' => 'minicalendarblock')); - $content .= calendar_get_mini($calendar->courses, $calendar->groups, $calendar->users, false, false, 'display', $calendar->courseid, $calendar->time); + $content .= \core_calendar\api::get_mini_calendar($calendar->courses, $calendar->groups, $calendar->users, + false, false, 'display', $calendar->courseid, $calendar->time); $content .= html_writer::end_tag('div'); $content .= html_writer::start_tag('div', array('class' => 'minicalendarblock')); - $content .= calendar_get_mini($calendar->courses, $calendar->groups, $calendar->users, false, false, 'display', $calendar->courseid, $nextmonthtime); + $content .= \core_calendar\api::get_mini_calendar($calendar->courses, $calendar->groups, $calendar->users, + false, false, 'display', $calendar->courseid, $nextmonthtime); $content .= html_writer::end_tag('div'); return $content; } @@ -164,16 +168,18 @@ class core_calendar_renderer extends plugin_renderer_base { $returnurl = $this->page->url; } - $events = calendar_get_upcoming($calendar->courses, $calendar->groups, $calendar->users, 1, 100, $calendar->timestamp_today()); + $events = \core_calendar\api::get_upcoming($calendar->courses, $calendar->groups, $calendar->users, + 1, 100, $calendar->timestamp_today()); $output = html_writer::start_tag('div', array('class'=>'header')); $output .= $this->course_filter_selector($returnurl, get_string('dayviewfor', 'calendar')); - if (calendar_user_can_add_event($calendar->course)) { + if (\core_calendar\api::can_add_event_to_course($calendar->course)) { $output .= $this->add_event_button($calendar->course->id, 0, 0, 0, $calendar->time); } $output .= html_writer::end_tag('div'); // Controls - $output .= html_writer::tag('div', calendar_top_controls('day', array('id' => $calendar->courseid, 'time' => $calendar->time)), array('class'=>'controls')); + $output .= html_writer::tag('div', \core_calendar\api::get_top_controls('day', array('id' => $calendar->courseid, + 'time' => $calendar->time)), array('class' => 'controls')); if (empty($events)) { // There is nothing to display today. @@ -186,7 +192,8 @@ class core_calendar_renderer extends plugin_renderer_base { $event = new \core_calendar\event($event); $event->calendarcourseid = $calendar->courseid; if ($event->timestart >= $calendar->timestamp_today() && $event->timestart <= $calendar->timestamp_tomorrow()-1) { // Print it now - $event->time = calendar_format_event_time($event, time(), null, false, $calendar->timestamp_today()); + $event->time = \core_calendar\api::get_format_event_time($event, time(), null, false, + $calendar->timestamp_today()); $output .= $this->event($event); } else { // Save this for later $underway[] = $event; @@ -198,7 +205,8 @@ class core_calendar_renderer extends plugin_renderer_base { $output .= html_writer::span(get_string('spanningevents', 'calendar'), 'calendar-information calendar-span-multiple-days'); foreach ($underway as $event) { - $event->time = calendar_format_event_time($event, time(), null, false, $calendar->timestamp_today()); + $event->time = \core_calendar\api::get_format_event_time($event, time(), null, false, + $calendar->timestamp_today()); $output .= $this->event($event); } } @@ -219,12 +227,12 @@ class core_calendar_renderer extends plugin_renderer_base { public function event(\core_calendar\event $event, $showactions=true) { global $CFG; - $event = calendar_add_event_metadata($event); + $event = \core_calendar\api::add_event_metadata($event); $context = $event->context; $output = ''; $output .= $this->output->box_start('card-header clearfix'); - if (calendar_edit_event_allowed($event) && $showactions) { + if (\core_calendar\api::can_edit_event($event) && $showactions) { if (empty($event->cmid)) { $editlink = new moodle_url(CALENDAR_URL.'event.php', array('action' => 'edit', 'id' => $event->id)); $deletelink = new moodle_url(CALENDAR_URL.'delete.php', array('id' => $event->id)); @@ -281,7 +289,7 @@ class core_calendar_renderer extends plugin_renderer_base { $output .= html_writer::tag('span', $event->time, array('class' => 'date pull-xs-right m-r-1')); } else { $attrs = array('class' => 'date pull-xs-right m-r-1'); - $output .= html_writer::tag('span', calendar_time_representation($event->timestart), $attrs); + $output .= html_writer::tag('span', \core_calendar\api::get_time_representation($event->timestart), $attrs); } if (!empty($event->courselink)) { $output .= html_writer::tag('div', $event->courselink, array('class' => 'course')); @@ -344,13 +352,13 @@ class core_calendar_renderer extends plugin_renderer_base { // Get the starting week day for this month. $startwday = dayofweek(1, $date['mon'], $date['year']); // Get the days in a week. - $daynames = calendar_get_days(); + $daynames = \core_calendar\api::get_days(); // Store the number of days in a week. $numberofdaysinweek = $calendartype->get_num_weekdays(); - $display->minwday = calendar_get_starting_weekday(); + $display->minwday = \core_calendar\api::get_starting_weekday(); $display->maxwday = $display->minwday + ($numberofdaysinweek - 1); - $display->maxdays = calendar_days_in_month($date['mon'], $date['year']); + $display->maxdays = \core_calendar\api::get_days_in_month($date['mon'], $date['year']); // These are used for DB queries, so we want unixtime, so we need to use Gregorian dates. $display->tstart = make_timestamp($gy, $gm, $gd, $gh, $gmin, 0); @@ -363,7 +371,8 @@ class core_calendar_renderer extends plugin_renderer_base { } // Get events from database - $events = calendar_get_events($display->tstart, $display->tend, $calendar->users, $calendar->groups, $calendar->courses); + $events = \core_calendar\api::get_events($display->tstart, $display->tend, $calendar->users, $calendar->groups, + $calendar->courses); if (!empty($events)) { foreach($events as $eventid => $event) { $event = new \core_calendar\event($event); @@ -377,16 +386,18 @@ class core_calendar_renderer extends plugin_renderer_base { } // Extract information: events vs. time - calendar_events_by_day($events, $date['mon'], $date['year'], $eventsbyday, $durationbyday, $typesbyday, $calendar->courses); + \core_calendar\api::get_events_by_day($events, $date['mon'], $date['year'], $eventsbyday, $durationbyday, + $typesbyday, $calendar->courses); $output = html_writer::start_tag('div', array('class'=>'header')); $output .= $this->course_filter_selector($returnurl, get_string('detailedmonthviewfor', 'calendar')); - if (calendar_user_can_add_event($calendar->course)) { + if (\core_calendar\api::can_add_event_to_course($calendar->course)) { $output .= $this->add_event_button($calendar->course->id, 0, 0, 0, $calendar->time); } $output .= html_writer::end_tag('div', array('class'=>'header')); // Controls - $output .= html_writer::tag('div', calendar_top_controls('month', array('id' => $calendar->courseid, 'time' => $calendar->time)), array('class' => 'controls')); + $output .= html_writer::tag('div', \core_calendar\api::get_top_controls('month', array('id' => $calendar->courseid, + 'time' => $calendar->time)), array('class' => 'controls')); $table = new html_table(); $table->attributes = array('class'=>'calendarmonth calendartable'); @@ -432,7 +443,8 @@ class core_calendar_renderer extends plugin_renderer_base { // Reset vars $cell = new html_table_cell(); - $dayhref = calendar_get_link_href(new moodle_url(CALENDAR_URL.'view.php', array('view' => 'day', 'course' => $calendar->courseid)), 0, 0, 0, $daytime); + $dayhref = \core_calendar\api::get_link_href(new moodle_url(CALENDAR_URL . 'view.php', + array('view' => 'day', 'course' => $calendar->courseid)), 0, 0, 0, $daytime); $cellclasses = array(); @@ -527,11 +539,12 @@ class core_calendar_renderer extends plugin_renderer_base { $returnurl = $this->page->url; } - $events = calendar_get_upcoming($calendar->courses, $calendar->groups, $calendar->users, $futuredays, $maxevents); + $events = \core_calendar\api::get_upcoming($calendar->courses, $calendar->groups, $calendar->users, + $futuredays, $maxevents); $output = html_writer::start_tag('div', array('class'=>'header')); $output .= $this->course_filter_selector($returnurl, get_string('upcomingeventsfor', 'calendar')); - if (calendar_user_can_add_event($calendar->course)) { + if (\core_calendar\api::can_add_event_to_course($calendar->course)) { $output .= $this->add_event_button($calendar->course->id); } $output .= html_writer::end_tag('div'); @@ -673,7 +686,7 @@ class core_calendar_renderer extends plugin_renderer_base { // Assemble pollinterval control. $html .= html_writer::start_tag('div', array('style' => 'float:left;')); $html .= html_writer::start_tag('select', array('name' => 'pollinterval', 'class' => 'custom-select')); - foreach (calendar_get_pollinterval_choices() as $k => $v) { + foreach (\core_calendar\api::get_poll_interval_choices() as $k => $v) { $attributes = array(); if ($k == $subscription->pollinterval) { $attributes['selected'] = 'selected'; diff --git a/calendar/set.php b/calendar/set.php index fab8aa54c02..1e95ab8d6cc 100644 --- a/calendar/set.php +++ b/calendar/set.php @@ -61,16 +61,16 @@ $PAGE->set_context(context_system::instance()); switch($var) { case 'showgroups': - calendar_set_event_type_display(CALENDAR_EVENT_GROUP); + \core_calendar\api::set_event_type_display(CALENDAR_EVENT_GROUP); break; case 'showcourses': - calendar_set_event_type_display(CALENDAR_EVENT_COURSE); + \core_calendar\api::set_event_type_display(CALENDAR_EVENT_COURSE); break; case 'showglobal': - calendar_set_event_type_display(CALENDAR_EVENT_GLOBAL); + \core_calendar\api::set_event_type_display(CALENDAR_EVENT_GLOBAL); break; case 'showuser': - calendar_set_event_type_display(CALENDAR_EVENT_USER); + \core_calendar\api::set_event_type_display(CALENDAR_EVENT_USER); break; } diff --git a/calendar/tests/lib_test.php b/calendar/tests/api_test.php similarity index 68% rename from calendar/tests/lib_test.php rename to calendar/tests/api_test.php index 7b9b3bcf101..656f0c666a1 100644 --- a/calendar/tests/lib_test.php +++ b/calendar/tests/api_test.php @@ -15,31 +15,32 @@ // along with Moodle. If not, see . /** - * Calendar lib unit tests + * Contains the class containing unit tests for the calendar API. * * @package core_calendar - * @copyright 2013 Dan Poltawski + * @copyright 2017 Mark Nelson * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); -global $CFG; -require_once($CFG->dirroot . '/calendar/lib.php'); /** - * Unit tests for calendar lib + * Class contaning unit tests for the calendar API. * * @package core_calendar - * @copyright 2013 Dan Poltawski + * @copyright 2017 Mark Nelson * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class core_calendar_lib_testcase extends advanced_testcase { +class core_calendar_api_testcase extends advanced_testcase { + /** + * Tests set up + */ protected function setUp() { - $this->resetAfterTest(true); + $this->resetAfterTest(); } - public function test_calendar_get_course_cached() { + public function test_get_course_cached() { // Setup some test courses. $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); @@ -47,9 +48,9 @@ class core_calendar_lib_testcase extends advanced_testcase { // Load courses into cache. $coursecache = null; - calendar_get_course_cached($coursecache, $course1->id); - calendar_get_course_cached($coursecache, $course2->id); - calendar_get_course_cached($coursecache, $course3->id); + \core_calendar\api::get_course_cached($coursecache, $course1->id); + \core_calendar\api::get_course_cached($coursecache, $course2->id); + \core_calendar\api::get_course_cached($coursecache, $course3->id); // Verify the cache. $this->assertArrayHasKey($course1->id, $coursecache); @@ -72,51 +73,9 @@ class core_calendar_lib_testcase extends advanced_testcase { } /** - * Test calendar cron with a working subscription URL. + * Test that the get_events() function only returns activity events that are enabled. */ - public function test_calendar_cron_working_url() { - global $CFG; - require_once($CFG->dirroot . '/lib/cronlib.php'); - - // ICal URL from external test repo. - $subscriptionurl = $this->getExternalTestFileUrl('/ical.ics'); - - $subscription = new stdClass(); - $subscription->eventtype = 'site'; - $subscription->name = 'test'; - $subscription->url = $subscriptionurl; - $subscription->pollinterval = 86400; - $subscription->lastupdated = 0; - calendar_add_subscription($subscription); - - $this->expectOutputRegex('/Events imported: .* Events updated:/'); - calendar_cron(); - } - - /** - * Test calendar cron with a broken subscription URL. - */ - public function test_calendar_cron_broken_url() { - global $CFG; - require_once($CFG->dirroot . '/lib/cronlib.php'); - - $subscription = new stdClass(); - $subscription->eventtype = 'site'; - $subscription->name = 'test'; - $subscription->url = 'brokenurl'; - $subscription->pollinterval = 86400; - $subscription->lastupdated = 0; - calendar_add_subscription($subscription); - - $this->expectOutputRegex('/Error updating calendar subscription: The given iCal URL is invalid/'); - calendar_cron(); - } - - /** - * Test the calendar_get_events() function only returns activity - * events that are enabled. - */ - public function test_calendar_get_events_with_disabled_module() { + public function test_get_events_with_disabled_module() { global $DB; $generator = $this->getDataGenerator(); @@ -124,7 +83,6 @@ class core_calendar_lib_testcase extends advanced_testcase { $student = $generator->create_user(); $generator->enrol_user($student->id, $course->id, 'student'); $this->setUser($student); - $events = [ [ 'name' => 'Start of assignment', @@ -140,7 +98,6 @@ class core_calendar_lib_testcase extends advanced_testcase { 'timeduration' => 86400, 'visible' => 1 ], [ - 'name' => 'Start of lesson', 'description' => '', 'format' => 1, @@ -155,25 +112,20 @@ class core_calendar_lib_testcase extends advanced_testcase { 'visible' => 1 ] ]; - foreach ($events as $event) { \core_calendar\event::create($event, false); } - $timestart = time() - 60; $timeend = time() + 60; - // Get all events. - $events = calendar_get_events($timestart, $timeend, true, 0, true); + $events = \core_calendar\api::get_events($timestart, $timeend, true, 0, true); $this->assertCount(2, $events); - // Disable the lesson module. $modulerecord = $DB->get_record('modules', ['name' => 'lesson']); $modulerecord->visible = 0; $DB->update_record('modules', $modulerecord); - // Check that we only return the assign event. - $events = calendar_get_events($timestart, $timeend, true, 0, true); + $events = \core_calendar\api::get_events($timestart, $timeend, true, 0, true); $this->assertCount(1, $events); $event = reset($events); $this->assertEquals('assign', $event->modulename); @@ -184,7 +136,6 @@ class core_calendar_lib_testcase extends advanced_testcase { */ public function test_calendar_get_events_with_overrides() { global $DB; - $generator = $this->getDataGenerator(); $course = $generator->create_course(); $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign'); @@ -192,31 +143,26 @@ class core_calendar_lib_testcase extends advanced_testcase { $params['course'] = $course->id; } $instance = $plugingenerator->create_instance($params); - // Create users. $useroverridestudent = $generator->create_user(); $group1student = $generator->create_user(); $group2student = $generator->create_user(); $group12student = $generator->create_user(); $nogroupstudent = $generator->create_user(); - // Enrol users. $generator->enrol_user($useroverridestudent->id, $course->id, 'student'); $generator->enrol_user($group1student->id, $course->id, 'student'); $generator->enrol_user($group2student->id, $course->id, 'student'); $generator->enrol_user($group12student->id, $course->id, 'student'); $generator->enrol_user($nogroupstudent->id, $course->id, 'student'); - // Create groups. $group1 = $generator->create_group(['courseid' => $course->id]); $group2 = $generator->create_group(['courseid' => $course->id]); - // Add members to groups. $generator->create_group_member(['groupid' => $group1->id, 'userid' => $group1student->id]); $generator->create_group_member(['groupid' => $group2->id, 'userid' => $group2student->id]); $generator->create_group_member(['groupid' => $group1->id, 'userid' => $group12student->id]); $generator->create_group_member(['groupid' => $group2->id, 'userid' => $group12student->id]); - $now = time(); // Events with the same module name, instance and event type. $events = [ @@ -277,48 +223,39 @@ class core_calendar_lib_testcase extends advanced_testcase { 'priority' => 2, ], ]; - foreach ($events as $event) { - calendar_event::create($event, false); + \core_calendar\event::create($event, false); } - $timestart = $now - 100; $timeend = $now + (3 * 86400); - $groups = [$group1->id, $group2->id]; - // Get user override events. $this->setUser($useroverridestudent); - $events = calendar_get_events($timestart, $timeend, $useroverridestudent->id, $groups, $course->id); + $events = \core_calendar\api::get_events($timestart, $timeend, $useroverridestudent->id, $groups, $course->id); $this->assertCount(1, $events); $event = reset($events); $this->assertEquals('Assignment 1 due date - User override', $event->name); - // Get event for user with override but with the timestart and timeend parameters only covering the original event. - $events = calendar_get_events($timestart, $now, $useroverridestudent->id, $groups, $course->id); + $events = \core_calendar\api::get_events($timestart, $now, $useroverridestudent->id, $groups, $course->id); $this->assertCount(0, $events); - // Get events for user that does not belong to any group and has no user override events. $this->setUser($nogroupstudent); - $events = calendar_get_events($timestart, $timeend, $nogroupstudent->id, $groups, $course->id); + $events = \core_calendar\api::get_events($timestart, $timeend, $nogroupstudent->id, $groups, $course->id); $this->assertCount(1, $events); $event = reset($events); $this->assertEquals('Assignment 1 due date', $event->name); - // Get events for user that belongs to groups A and B and has no user override events. $this->setUser($group12student); - $events = calendar_get_events($timestart, $timeend, $group12student->id, $groups, $course->id); + $events = \core_calendar\api::get_events($timestart, $timeend, $group12student->id, $groups, $course->id); $this->assertCount(1, $events); $event = reset($events); $this->assertEquals('Assignment 1 due date - Group B override', $event->name); - // Get events for user that belongs to group A and has no user override events. $this->setUser($group1student); - $events = calendar_get_events($timestart, $timeend, $group1student->id, $groups, $course->id); + $events = \core_calendar\api::get_events($timestart, $timeend, $group1student->id, $groups, $course->id); $this->assertCount(1, $events); $event = reset($events); $this->assertEquals('Assignment 1 due date - Group A override', $event->name); - // Add repeating events. $repeatingevents = [ [ @@ -353,11 +290,100 @@ class core_calendar_lib_testcase extends advanced_testcase { ], ]; foreach ($repeatingevents as $event) { - calendar_event::create($event, false); + \core_calendar\event::create($event, false); } - // Make sure repeating events are not filtered out. - $events = calendar_get_events($timestart, $timeend, true, true, true); + $events = \core_calendar\api::get_events($timestart, $timeend, true, true, true); $this->assertCount(3, $events); } + + /** + * Test the update_subscription() function. + */ + public function test_update_subscription() { + $this->resetAfterTest(true); + + $subscription = new stdClass(); + $subscription->eventtype = 'site'; + $subscription->name = 'test'; + $id = \core_calendar\api::add_subscription($subscription); + + $subscription = \core_calendar\api::get_subscription($id); + $subscription->name = 'awesome'; + \core_calendar\api::update_subscription($subscription); + $sub = \core_calendar\api::get_subscription($id); + $this->assertEquals($subscription->name, $sub->name); + + $subscription = \core_calendar\api::get_subscription($id); + $subscription->name = 'awesome2'; + $subscription->pollinterval = 604800; + \core_calendar\api::update_subscription($subscription); + $sub = \core_calendar\api::get_subscription($id); + $this->assertEquals($subscription->name, $sub->name); + $this->assertEquals($subscription->pollinterval, $sub->pollinterval); + + $subscription = new stdClass(); + $subscription->name = 'awesome4'; + $this->expectException('coding_exception'); + \core_calendar\api::update_subscription($subscription); + } + + public function test_add_subscription() { + global $DB, $CFG; + + require_once($CFG->dirroot . '/lib/bennu/bennu.inc.php'); + + $this->resetAfterTest(true); + + // Test for Microsoft Outlook 2010. + $subscription = new stdClass(); + $subscription->name = 'Microsoft Outlook 2010'; + $subscription->importfrom = CALENDAR_IMPORT_FROM_FILE; + $subscription->eventtype = 'site'; + $id = \core_calendar\api::add_subscription($subscription); + + $calendar = file_get_contents($CFG->dirroot . '/lib/tests/fixtures/ms_outlook_2010.ics'); + $ical = new iCalendar(); + $ical->unserialize($calendar); + $this->assertEquals($ical->parser_errors, array()); + + $sub = \core_calendar\api::get_subscription($id); + \core_calendar\api::import_icalendar_events($ical, $sub->courseid, $sub->id); + $count = $DB->count_records('event', array('subscriptionid' => $sub->id)); + $this->assertEquals($count, 1); + + // Test for OSX Yosemite. + $subscription = new stdClass(); + $subscription->name = 'OSX Yosemite'; + $subscription->importfrom = CALENDAR_IMPORT_FROM_FILE; + $subscription->eventtype = 'site'; + $id = \core_calendar\api::add_subscription($subscription); + + $calendar = file_get_contents($CFG->dirroot . '/lib/tests/fixtures/osx_yosemite.ics'); + $ical = new iCalendar(); + $ical->unserialize($calendar); + $this->assertEquals($ical->parser_errors, array()); + + $sub = \core_calendar\api::get_subscription($id); + \core_calendar\api::import_icalendar_events($ical, $sub->courseid, $sub->id); + $count = $DB->count_records('event', array('subscriptionid' => $sub->id)); + $this->assertEquals($count, 1); + + // Test for Google Gmail. + $subscription = new stdClass(); + $subscription->name = 'Google Gmail'; + $subscription->importfrom = CALENDAR_IMPORT_FROM_FILE; + $subscription->eventtype = 'site'; + $id = \core_calendar\api::add_subscription($subscription); + + $calendar = file_get_contents($CFG->dirroot . '/lib/tests/fixtures/google_gmail.ics'); + $ical = new iCalendar(); + $ical->unserialize($calendar); + $this->assertEquals($ical->parser_errors, array()); + + $sub = \core_calendar\api::get_subscription($id); + \core_calendar\api::import_icalendar_events($ical, $sub->courseid, $sub->id); + $count = $DB->count_records('event', array('subscriptionid' => $sub->id)); + $this->assertEquals($count, 1); + } } diff --git a/calendar/tests/calendartype_test.php b/calendar/tests/calendartype_test.php index c097cfd0759..f9fd7e52558 100644 --- a/calendar/tests/calendartype_test.php +++ b/calendar/tests/calendartype_test.php @@ -33,9 +33,6 @@ require_once($CFG->dirroot . '/calendar/tests/calendartype_test_example.php'); require_once($CFG->libdir . '/form/dateselector.php'); require_once($CFG->libdir . '/form/datetimeselector.php'); -// Used to test the calendar/lib.php functions. -require_once($CFG->dirroot . '/calendar/lib.php'); - // Used to test the user datetime profile field. require_once($CFG->dirroot . '/user/profile/lib.php'); require_once($CFG->dirroot . '/user/profile/definelib.php'); @@ -108,8 +105,6 @@ class core_calendar_type_testcase extends advanced_testcase { * different calendar types. */ public function test_calendar_type_dateselector_elements() { - global $CFG; - // We want to reset the test data after this run. $this->resetAfterTest(); @@ -200,11 +195,11 @@ class core_calendar_type_testcase extends advanced_testcase { userdate($this->user->timecreated)); // Test the calendar/lib.php functions. - $this->assertEquals($calendar->get_weekdays(), calendar_get_days()); - $this->assertEquals($calendar->get_starting_weekday(), calendar_get_starting_weekday()); - $this->assertEquals($calendar->get_num_days_in_month('1986', '9'), calendar_days_in_month('9', '1986')); - $this->assertEquals($calendar->get_next_month('1986', '9'), calendar_add_month('9', '1986')); - $this->assertEquals($calendar->get_prev_month('1986', '9'), calendar_sub_month('9', '1986')); + $this->assertEquals($calendar->get_weekdays(), \core_calendar\api::get_days()); + $this->assertEquals($calendar->get_starting_weekday(), \core_calendar\api::get_starting_weekday()); + $this->assertEquals($calendar->get_num_days_in_month('1986', '9'), \core_calendar\api::get_days_in_month('9', '1986')); + $this->assertEquals($calendar->get_next_month('1986', '9'), \core_calendar\api::get_next_month('9', '1986')); + $this->assertEquals($calendar->get_prev_month('1986', '9'), \core_calendar\api::get_prev_month('9', '1986')); // Test the lib/moodle.php functions. $this->assertEquals($calendar->get_num_days_in_month('1986', '9'), days_in_month('9', '1986')); diff --git a/calendar/tests/events_test.php b/calendar/tests/events_test.php index bc103192c94..3059d6be0b3 100644 --- a/calendar/tests/events_test.php +++ b/calendar/tests/events_test.php @@ -438,7 +438,7 @@ class core_calendar_events_testcase extends advanced_testcase { // Trigger and capture the event. $sink = $this->redirectEvents(); - $id = calendar_add_subscription($subscription); + $id = \core_calendar\api::add_subscription($subscription); $events = $sink->get_events(); $event = reset($events); @@ -465,13 +465,13 @@ class core_calendar_events_testcase extends advanced_testcase { $subscription->eventtype = 'site'; $subscription->name = 'test'; $subscription->courseid = $this->course->id; - $subscription->id = calendar_add_subscription($subscription); + $subscription->id = \core_calendar\api::add_subscription($subscription); // Now edit it. $subscription->name = 'awesome'; // Trigger and capture the event. $sink = $this->redirectEvents(); - calendar_update_subscription($subscription); + \core_calendar\api::update_subscription($subscription); $events = $sink->get_events(); $event = reset($events); // Check that the event data is valid. @@ -497,11 +497,11 @@ class core_calendar_events_testcase extends advanced_testcase { $subscription->eventtype = 'site'; $subscription->name = 'test'; $subscription->courseid = $this->course->id; - $subscription->id = calendar_add_subscription($subscription); + $subscription->id = \core_calendar\api::add_subscription($subscription); // Trigger and capture the event. $sink = $this->redirectEvents(); - calendar_delete_subscription($subscription); + \core_calendar\api::delete_subscription($subscription); $events = $sink->get_events(); $event = reset($events); // Check that the event data is valid. diff --git a/calendar/tests/ical_test.php b/calendar/tests/ical_test.php deleted file mode 100644 index d2223b538c2..00000000000 --- a/calendar/tests/ical_test.php +++ /dev/null @@ -1,138 +0,0 @@ -. - -/** - * Calendar Ical unit tests - * - * @package core_calendar - * @copyright 2013 Ankit Agarwal - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -global $CFG; - - -/** - * Unit tests for ical APIs. - * - * @package core_calendar - * @copyright 2013 Ankit Agarwal - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @since Moodle 2.5 - */ -class core_calendar_ical_testcase extends advanced_testcase { - - /** - * Tests set up - */ - protected function setUp() { - global $CFG; - require_once($CFG->dirroot . '/calendar/lib.php'); - } - - /** - * @expectedException coding_exception - */ - public function test_calendar_update_subscription() { - $this->resetAfterTest(true); - - $subscription = new stdClass(); - $subscription->eventtype = 'site'; - $subscription->name = 'test'; - $id = calendar_add_subscription($subscription); - - $subscription = new stdClass(); - $subscription = calendar_get_subscription($id); - $subscription->name = 'awesome'; - calendar_update_subscription($subscription); - $sub = calendar_get_subscription($id); - $this->assertEquals($subscription->name, $sub->name); - - $subscription = new stdClass(); - $subscription = calendar_get_subscription($id); - $subscription->name = 'awesome2'; - $subscription->pollinterval = 604800; - calendar_update_subscription($subscription); - $sub = calendar_get_subscription($id); - $this->assertEquals($subscription->name, $sub->name); - $this->assertEquals($subscription->pollinterval, $sub->pollinterval); - - $subscription = new stdClass(); - $subscription->name = 'awesome4'; - calendar_update_subscription($subscription); - } - - public function test_calendar_add_subscription() { - global $DB, $CFG; - - require_once($CFG->dirroot . '/lib/bennu/bennu.inc.php'); - - $this->resetAfterTest(true); - - // Test for Microsoft Outlook 2010. - $subscription = new stdClass(); - $subscription->name = 'Microsoft Outlook 2010'; - $subscription->importfrom = CALENDAR_IMPORT_FROM_FILE; - $subscription->eventtype = 'site'; - $id = calendar_add_subscription($subscription); - - $calendar = file_get_contents($CFG->dirroot . '/lib/tests/fixtures/ms_outlook_2010.ics'); - $ical = new iCalendar(); - $ical->unserialize($calendar); - $this->assertEquals($ical->parser_errors, array()); - - $sub = calendar_get_subscription($id); - $result = calendar_import_icalendar_events($ical, $sub->courseid, $sub->id); - $count = $DB->count_records('event', array('subscriptionid' => $sub->id)); - $this->assertEquals($count, 1); - - // Test for OSX Yosemite. - $subscription = new stdClass(); - $subscription->name = 'OSX Yosemite'; - $subscription->importfrom = CALENDAR_IMPORT_FROM_FILE; - $subscription->eventtype = 'site'; - $id = calendar_add_subscription($subscription); - - $calendar = file_get_contents($CFG->dirroot . '/lib/tests/fixtures/osx_yosemite.ics'); - $ical = new iCalendar(); - $ical->unserialize($calendar); - $this->assertEquals($ical->parser_errors, array()); - - $sub = calendar_get_subscription($id); - $result = calendar_import_icalendar_events($ical, $sub->courseid, $sub->id); - $count = $DB->count_records('event', array('subscriptionid' => $sub->id)); - $this->assertEquals($count, 1); - - // Test for Google Gmail. - $subscription = new stdClass(); - $subscription->name = 'Google Gmail'; - $subscription->importfrom = CALENDAR_IMPORT_FROM_FILE; - $subscription->eventtype = 'site'; - $id = calendar_add_subscription($subscription); - - $calendar = file_get_contents($CFG->dirroot . '/lib/tests/fixtures/google_gmail.ics'); - $ical = new iCalendar(); - $ical->unserialize($calendar); - $this->assertEquals($ical->parser_errors, array()); - - $sub = calendar_get_subscription($id); - $result = calendar_import_icalendar_events($ical, $sub->courseid, $sub->id); - $count = $DB->count_records('event', array('subscriptionid' => $sub->id)); - $this->assertEquals($count, 1); - } -} diff --git a/calendar/upgrade.txt b/calendar/upgrade.txt index bcec3ba3bb9..9a7d79164ac 100644 --- a/calendar/upgrade.txt +++ b/calendar/upgrade.txt @@ -4,9 +4,59 @@ information provided here is intended especially for developers. === 3.3 === * calendar_event_hook() has been removed. Developers should be using the Moodle events system to achieve this behaviour, rather than using a hacky calendar specific implementation. +* calendar_get_days() is deprecated, please use \core_calendar\api::get_days() instead. +* calendar_get_subscription() is deprecated, please use \core_calendar\api::get_subscription() instead. +* calendar_get_starting_weekday() is deprecated, please use \core_calendar\api::get_starting_weekday() instead. +* calendar_get_mini() is deprecated, please use \core_calendar\api::get_mini_calendar() instead. +* calendar_get_popup() is deprecated, please use \core_calendar\api::get_popup() instead. +* calendar_get_upcoming() is deprecated, please use \core_calendar\api::get_upcoming() instead. +* calendar_get_courselink() is deprecated, please use \core_calendar\api::get_courselink() instead. +* calendar_get_module_cached() is deprecated, please use \core_calendar\api::get_module_cached() instead. +* calendar_get_course_cached() is deprecated, please use \core_calendar\api::get_course_cached() instead. +* calendar_get_group_cached() is deprecated, please use \core_calendar\api::get_group_cached() instead. +* calendar_add_event_metadata() is deprecated, please use \core_calendar\api::add_event_metadata() instead. +* calendar_get_events() is deprecated, please use \core_calendar\api::get_events() instead. +* calendar_get_events_by_id() is deprecated, please use \core_calendar\api::get_events_by_id() instead. +* calendar_top_controls() is deprecated, please use \core_calendar\api::get_top_controls() instead. +* calendar_filter_controls_element() is deprecated, please use \core_calendar\api::get_filter_controls_element() instead. +* calendar_filter_controls() is deprecated, please use \core_calendar\api::get_filter_controls() instead. +* calendar_day_representation() is deprecated, please use \core_calendar\api::get_day_representation() instead. +* calendar_time_representation() is deprecated, please use \core_calendar\api::get_time_representation() instead. +* calendar_get_link_href() is deprecated, please use \core_calendar\api::get_link_href() instead. +* calendar_get_link_previous() is deprecated, please use \core_calendar\api::get_link_previous() instead. +* calendar_get_link_next() is deprecated, please use \core_calendar\api::get_link_next() instead. +* calendar_wday_name() is deprecated and no longer used in core. +* calendar_days_in_month() is deprecated, please use \core_calendar\api::get_days_in_month() instead. +* calendar_get_block_upcoming() is deprecated, please use block_calendar_upcoming::get_upcoming_content() instead. +* calendar_add_month() is deprecated, please use \core_calendar\api::get_next_month() instead. +* calendar_sub_month() is deprecated, please use \core_calendar\api::get_prev_month() instead. +* calendar_events_by_day() is deprecated, please use \core_calendar\api::get_events_by_day() instead. +* calendar_set_filters() is deprecated, please use \core_calendar\api::set_filters() instead. +* calendar_edit_event_allowed() is deprecated, please use \core_calendar\api::can_edit_event() instead. +* calendar_get_default_courses() is deprecated, please use \core_calendar\api::get_default_courses() instead. +* calendar_format_event_time() is deprecated, please use \core_calendar\api::get_format_event_time() instead. +* calendar_print_month_selector() is deprecated and no longer used in core. +* calendar_show_event_type() is deprecated, please use \core_calendar\api::show_event_type() instead. +* calendar_set_event_type_display() is deprecated, please use \core_calendar\api::set_event_type_display() instead. +* calendar_get_allowed_types() is deprecated, please use \core_calendar\api::get_allowed_types() instead. +* calendar_user_can_add_event() is deprecated, please use \core_calendar\api::can_add_event_to_course() instead. +* calendar_add_event_allowed() is deprecated, please use \core_calendar\api::can_add_event() instead. +* calendar_get_pollinterval_choices() is deprecated, please use \core_calendar\api::get_poll_interval_choices() instead. +* calendar_get_eventtype_choices() is deprecated, please use \core_calendar\api::get_event_type_choices() instead. +* calendar_add_subscription() is deprecated, please use \core_calendar\api::add_subscription() instead. +* calendar_add_icalendar_event() is deprecated, please use \core_calendar\api::add_icalendar_event() instead. +* calendar_process_subscription_row() is deprecated, please use \core_calendar\api::process_subscription_row() instead. +* calendar_delete_subscription() is deprecated, please use \core_calendar\api::delete_subscription() instead. +* calendar_get_icalendar() is deprecated, please use \core_calendar\api::get_icalendar() instead. +* calendar_import_icalendar_events() is deprecated, please use \core_calendar\api::import_icalendar_events() instead. +* calendar_update_subscription_events() is deprecated, please use \core_calendar\api::update_subscription_events() instead. +* calendar_update_subscription() is deprecated, please use \core_calendar\api::update_subscription() instead. +* calendar_can_edit_subscription() is deprecated, please use \core_calendar\api::can_edit_subscription() instead. +* calendar_get_calendar_context() is deprecated, please use \core_calendar\api::get_calendar_context() instead. +* calendar_cron() is deprecated and should not be used. Please use the core\task\calendar_cron_task instead. === 3.2 === -* calendar_preferences_button() is now depreciated. Calendar preferences have been moved to the user preferences page. +* calendar_preferences_button() is now deprecated. Calendar preferences have been moved to the user preferences page. === 2.9 === default values changes in code: diff --git a/calendar/view.php b/calendar/view.php index a55aee6e195..8df030f1046 100644 --- a/calendar/view.php +++ b/calendar/view.php @@ -91,7 +91,7 @@ if ($courseid != SITEID && !empty($courseid)) { navigation_node::override_active_url(new moodle_url('/course/view.php', array('id' => $course->id))); } else { $course = get_site(); - $courses = calendar_get_default_courses(); + $courses = \core_calendar\api::get_default_courses(); $issite = true; } @@ -158,7 +158,7 @@ switch($view) { echo $OUTPUT->container_start('bottom'); if (!empty($CFG->enablecalendarexport)) { echo $OUTPUT->single_button(new moodle_url('export.php', array('course'=>$courseid)), get_string('exportcalendar', 'calendar')); - if (calendar_user_can_add_event($course)) { + if (\core_calendar\api::can_add_event_to_course($course)) { echo $OUTPUT->single_button(new moodle_url('/calendar/managesubscriptions.php', array('course'=>$courseid)), get_string('managesubscriptions', 'calendar')); } if (isloggedin()) { diff --git a/lib/classes/task/calendar_cron_task.php b/lib/classes/task/calendar_cron_task.php index 54516f61a30..f387ad572a1 100644 --- a/lib/classes/task/calendar_cron_task.php +++ b/lib/classes/task/calendar_cron_task.php @@ -23,6 +23,10 @@ */ namespace core\task; +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir . '/cronlib.php'); + /** * Simple task to run the calendar cron. */ @@ -42,11 +46,32 @@ class calendar_cron_task extends scheduled_task { * Throw exceptions on errors (the job will be retried). */ public function execute() { - global $CFG; + global $CFG, $DB; - // Run calendar cron. - require_once("{$CFG->dirroot}/calendar/lib.php"); - calendar_cron(); + require_once($CFG->libdir . '/bennu/bennu.inc.php'); + + mtrace('Updating calendar subscriptions:'); + cron_trace_time_and_memory(); + + $time = time(); + $sql = "SELECT * + FROM {event_subscriptions} + WHERE pollinterval > 0 + AND lastupdated + pollinterval < :time"; + $subscriptions = $DB->get_records_sql($sql, array('time' => $time)); + foreach ($subscriptions as $sub) { + mtrace("Updating calendar subscription {$sub->name} in course {$sub->courseid}"); + try { + $log = \core_calendar\api::update_subscription_events($sub->id); + mtrace(trim(strip_tags($log))); + } catch (\moodle_exception $ex) { + mtrace('Error updating calendar subscription: ' . $ex->getMessage()); + } + } + + mtrace('Finished updating calendar subscriptions.'); + + return true; } } diff --git a/lib/deprecatedlib.php b/lib/deprecatedlib.php index 81486e999ff..0bf21ad7641 100644 --- a/lib/deprecatedlib.php +++ b/lib/deprecatedlib.php @@ -6457,14 +6457,14 @@ function message_get_recent_conversations($userorid, $limitfrom = 0, $limitto = WHERE otheruser.deleted = 0 AND message.notification = 0 ORDER BY message.timecreated DESC"; $params = array( - 'userid1' => $user->id, - 'userid2' => $user->id, - 'userid3' => $user->id, - 'userid4' => $user->id, - 'userid5' => $user->id, - 'userid6' => $user->id, - 'userid7' => $user->id - ); + 'userid1' => $user->id, + 'userid2' => $user->id, + 'userid3' => $user->id, + 'userid4' => $user->id, + 'userid5' => $user->id, + 'userid6' => $user->id, + 'userid7' => $user->id + ); $read = $DB->get_records_sql($sql, $params, $limitfrom, $limitto); // We want to get the messages that have not been read. These are stored in the 'message' table. It is the @@ -6526,3 +6526,790 @@ function message_get_recent_conversations($userorid, $limitfrom = 0, $limitto = return $conversations; } + +/** + * Display calendar preference button. + * + * @param stdClass $course course object + * @deprecated since Moodle 3.2 + * @return string return preference button in html + */ +function calendar_preferences_button(stdClass $course) { + debugging('This should no longer be used, the calendar preferences are now linked to the user preferences page.'); + + global $OUTPUT; + + // Guests have no preferences. + if (!isloggedin() || isguestuser()) { + return ''; + } + + return $OUTPUT->single_button(new moodle_url('/user/calendar.php'), get_string("preferences", "calendar")); +} + +/** + * Return the days of the week. + * + * @deprecated since 3.3 + * @return array array of days + */ +function calendar_get_days() { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_days() instead.', DEBUG_DEVELOPER); + return \core_calendar\api::get_days(); +} + +/** + * Get the subscription from a given id. + * + * @deprecated since 3.3 + * @since Moodle 2.5 + * @param int $id id of the subscription + * @return stdClass Subscription record from DB + * @throws moodle_exception for an invalid id + */ +function calendar_get_subscription($id) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_subscription() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_subscription($id); +} + +/** + * Gets the first day of the week. + * + * Used to be define('CALENDAR_STARTING_WEEKDAY', blah); + * + * @deprecated since 3.3 + * @return int + */ +function calendar_get_starting_weekday() { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_starting_weekday() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_starting_weekday(); +} + +/** + * Generates the HTML for a miniature calendar. + * + * @deprecated since 3.3 + * @param array $courses list of course to list events from + * @param array $groups list of group + * @param array $users user's info + * @param int|bool $calmonth calendar month in numeric, default is set to false + * @param int|bool $calyear calendar month in numeric, default is set to false + * @param string|bool $placement the place/page the calendar is set to appear - passed on the the controls function + * @param int|bool $courseid id of the course the calendar is displayed on - passed on the the controls function + * @param int $time the unixtimestamp representing the date we want to view, this is used instead of $calmonth + * and $calyear to support multiple calendars + * @return string $content return html table for mini calendar + */ +function calendar_get_mini($courses, $groups, $users, $calmonth = false, $calyear = false, $placement = false, + $courseid = false, $time = 0) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_mini_calendar() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_mini_calendar($courses, $groups, $users, $calmonth, $calyear, $placement, + $courseid, $time); +} + +/** + * Gets the calendar popup. + * + * It called at multiple points in from calendar_get_mini. + * Copied and modified from calendar_get_mini. + * + * @deprecated since 3.3 + * @param bool $today false except when called on the current day. + * @param mixed $timestart $events[$eventid]->timestart, OR false if there are no events. + * @param string $popupcontent content for the popup window/layout. + * @return string eventid for the calendar_tooltip popup window/layout. + */ +function calendar_get_popup($today = false, $timestart, $popupcontent = '') { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_popup() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_popup($today, $timestart, $popupcontent); +} + +/** + * Gets the calendar upcoming event. + * + * @deprecated since 3.3 + * @param array $courses array of courses + * @param array|int|bool $groups array of groups, group id or boolean for all/no group events + * @param array|int|bool $users array of users, user id or boolean for all/no user events + * @param int $daysinfuture number of days in the future we 'll look + * @param int $maxevents maximum number of events + * @param int $fromtime start time + * @return array $output array of upcoming events + */ +function calendar_get_upcoming($courses, $groups, $users, $daysinfuture, $maxevents, $fromtime=0) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_upcoming() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_upcoming($courses, $groups, $users, $daysinfuture, $maxevents, $fromtime); +} + +/** + * Get a HTML link to a course. + * + * @deprecated since 3.3 + * @param int $courseid the course id + * @return string a link to the course (as HTML); empty if the course id is invalid + */ +function calendar_get_courselink($courseid) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_courselink() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_courselink($courseid); +} + +/** + * Get current module cache. + * + * @deprecated since 3.3 + * @param array $coursecache list of course cache + * @param string $modulename name of the module + * @param int $instance module instance number + * @return stdClass|bool $module information + */ +function calendar_get_module_cached(&$coursecache, $modulename, $instance) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_module_cached() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_module_cached($coursecache, $modulename, $instance); +} + +/** + * Get current course cache. + * + * @deprecated since 3.3 + * @param array $coursecache list of course cache + * @param int $courseid id of the course + * @return stdClass $coursecache[$courseid] return the specific course cache + */ +function calendar_get_course_cached(&$coursecache, $courseid) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_course_cached() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_course_cached($coursecache, $courseid); +} + +/** + * Get group from groupid for calendar display + * + * @deprecated since 3.3 + * @param int $groupid + * @return stdClass group object with fields 'id', 'name' and 'courseid' + */ +function calendar_get_group_cached($groupid) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_group_cached() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_group_cached($groupid); +} + +/** + * Add calendar event metadata + * + * @deprecated since 3.3 + * @param stdClass $event event info + * @return stdClass $event metadata + */ +function calendar_add_event_metadata($event) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::add_event_metadata() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::add_event_metadata($event); +} + +/** + * Get calendar events. + * + * @deprecated since 3.3 + * @param int $tstart Start time of time range for events + * @param int $tend End time of time range for events + * @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 boolean $withduration whether only events starting within time range selected + * or events in progress/already started selected as well + * @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) + */ +function calendar_get_events($tstart, $tend, $users, $groups, $courses, $withduration=true, $ignorehidden=true) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_events() instead.', DEBUG_DEVELOPER); + return \core_calendar\api::get_events($tstart, $tend, $users, $groups, $courses, $withduration, $ignorehidden); +} + +/** + * Get calendar events by id. + * + * @deprecated since 3.3 + * @since Moodle 2.5 + * @param array $eventids list of event ids + * @return array Array of event entries, empty array if nothing found + */ +function calendar_get_events_by_id($eventids) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_events_by_id() instead.', DEBUG_DEVELOPER); + return \core_calendar\api::get_events_by_id($eventids); +} + +/** + * Get control options for calendar. + * + * @deprecated since 3.3 + * @param string $type of calendar + * @param array $data calendar information + * @return string $content return available control for the calender in html + */ +function calendar_top_controls($type, $data) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_top_controls() instead.', DEBUG_DEVELOPER); + return \core_calendar\api::get_top_controls($type, $data); +} + +/** + * Formats a filter control element. + * + * @deprecated since 3.3 + * @param moodle_url $url of the filter + * @param int $type constant defining the type filter + * @return string html content of the element + */ +function calendar_filter_controls_element(moodle_url $url, $type) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_filter_controls_element() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_filter_controls_element($url, $type); +} + +/** + * Get the controls filter for calendar. + * + * Filter is used to hide calendar info from the display page. + * + * @deprecated since 3.3 + * @param moodle_url $returnurl return-url for filter controls + * @return string $content return filter controls in html + */ +function calendar_filter_controls(moodle_url $returnurl) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_filter_controls() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_filter_controls($returnurl); +} + +/** + * Return the representation day. + * + * @deprecated since 3.3 + * @param int $tstamp Timestamp in GMT + * @param int|bool $now current Unix timestamp + * @param bool $usecommonwords + * @return string the formatted date/time + */ +function calendar_day_representation($tstamp, $now = false, $usecommonwords = true) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_day_representation() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_day_representation($tstamp, $now, $usecommonwords); +} + +/** + * return the formatted representation time. + * + * @deprecated since 3.3 + * @param int $time the timestamp in UTC, as obtained from the database + * @return string the formatted date/time + */ +function calendar_time_representation($time) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_time_representation() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_time_representation($time); +} + +/** + * Adds day, month, year arguments to a URL and returns a moodle_url object. + * + * @deprecated since 3.3 + * @param string|moodle_url $linkbase + * @param int $d The number of the day. + * @param int $m The number of the month. + * @param int $y The number of the year. + * @param int $time the unixtime, used for multiple calendar support. The values $d, + * $m and $y are kept for backwards compatibility. + * @return moodle_url|null $linkbase + */ +function calendar_get_link_href($linkbase, $d, $m, $y, $time = 0) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_link_href() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_link_href($linkbase, $d, $m, $y, $time); +} + +/** + * Build and return a previous month HTML link, with an arrow. + * + * @deprecated since 3.3 + * @param string $text The text label. + * @param string|moodle_url $linkbase The URL stub. + * @param int $d The number of the date. + * @param int $m The number of the month. + * @param int $y year The number of the year. + * @param bool $accesshide Default visible, or hide from all except screenreaders. + * @param int $time the unixtime, used for multiple calendar support. The values $d, + * $m and $y are kept for backwards compatibility. + * @return string HTML string. + */ +function calendar_get_link_previous($text, $linkbase, $d, $m, $y, $accesshide = false, $time = 0) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_link_previous() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_link_previous($text, $linkbase, $d, $m, $y, $accesshide, $time); +} + +/** + * Build and return a next month HTML link, with an arrow. + * + * @deprecated since 3.3 + * @param string $text The text label. + * @param string|moodle_url $linkbase The URL stub. + * @param int $d the number of the Day + * @param int $m The number of the month. + * @param int $y The number of the year. + * @param bool $accesshide Default visible, or hide from all except screenreaders. + * @param int $time the unixtime, used for multiple calendar support. The values $d, + * $m and $y are kept for backwards compatibility. + * @return string HTML string. + */ +function calendar_get_link_next($text, $linkbase, $d, $m, $y, $accesshide = false, $time = 0) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_link_next() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_link_next($text, $linkbase, $d, $m, $y, $accesshide, $time); +} + +/** + * Return the name of the weekday + * + * @deprecated since 3.3 + * @param string $englishname + * @return string of the weekeday + */ +function calendar_wday_name($englishname) { + debugging(__FUNCTION__ . '() is deprecated and no longer used in core.', DEBUG_DEVELOPER); + return get_string(strtolower($englishname), 'calendar'); +} + +/** + * Return the number of days in month. + * + * @deprecated since 3.3 + * @param int $month the number of the month. + * @param int $year the number of the year + * @return int + */ +function calendar_days_in_month($month, $year) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_days_in_month() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_days_in_month($month, $year); +} + +/** + * Get the upcoming event block. + * + * @deprecated since 3.3 + * @param array $events list of events + * @param moodle_url|string $linkhref link to event referer + * @param boolean $showcourselink whether links to courses should be shown + * @return string|null $content html block content + */ +function calendar_get_block_upcoming($events, $linkhref = null, $showcourselink = false) { + global $CFG; + + debugging(__FUNCTION__ . '() is deprecated, please use block_calendar_upcoming::get_upcoming_content() instead.', + DEBUG_DEVELOPER); + + require_once($CFG->dirroot . '/blocks/moodleblock.class.php'); + require_once($CFG->dirroot . '/blocks/calendar_upcoming/block_calendar_upcoming.php'); + return block_calendar_upcoming::get_upcoming_content($events, $linkhref, $showcourselink); +} + +/** + * Get the next following month. + * + * @deprecated since 3.3 + * @param int $month the number of the month. + * @param int $year the number of the year. + * @return array the following month + */ +function calendar_add_month($month, $year) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_next_month() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_next_month($month, $year); +} + +/** + * Get the previous month. + * + * @deprecated since 3.3 + * @param int $month the number of the month. + * @param int $year the number of the year. + * @return array previous month + */ +function calendar_sub_month($month, $year) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_prev_month() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_prev_month($month, $year); +} + +/** + * Get per-day basis events + * + * @deprecated since 3.3 + * @param array $events list of events + * @param int $month the number of the month + * @param int $year the number of the year + * @param array $eventsbyday event on specific day + * @param array $durationbyday duration of the event in days + * @param array $typesbyday event type (eg: global, course, user, or group) + * @param array $courses list of courses + * @return void + */ +function calendar_events_by_day($events, $month, $year, &$eventsbyday, &$durationbyday, &$typesbyday, &$courses) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_events_by_day() instead.', + DEBUG_DEVELOPER); + \core_calendar\api::get_events_by_day($events, $month, $year, $eventsbyday, $durationbyday, $typesbyday, $courses); +} + +/** + * Returns the courses to load events for. + * + * @deprecated since 3.3 + * @param array $courseeventsfrom An array of courses to load calendar events for + * @param bool $ignorefilters specify the use of filters, false is set as default + * @return array An array of courses, groups, and user to load calendar events for based upon filters + */ +function calendar_set_filters(array $courseeventsfrom, $ignorefilters = false) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::set_filters() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::set_filters($courseeventsfrom, $ignorefilters); +} + +/** + * Return the capability for editing calendar event. + * + * @deprecated since 3.3 + * @param \core_calendar\event $event event object + * @return bool capability to edit event + */ +function calendar_edit_event_allowed($event) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::can_edit_event() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::can_edit_event($event); +} + +/** + * Returns the default courses to display on the calendar when there isn't a specific + * course to display. + * + * @deprecated since 3.3 + * @return array $courses Array of courses to display + */ +function calendar_get_default_courses() { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_default_courses() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_default_courses(); +} + + +/** + * Get event format time. + * + * @deprecated since 3.3 + * @param \core_calendar\event $event event object + * @param int $now current time in gmt + * @param array $linkparams list of params for event link + * @param bool $usecommonwords the words as formatted date/time. + * @param int $showtime determine the show time GMT timestamp + * @return string $eventtime link/string for event time + */ +function calendar_format_event_time($event, $now, $linkparams = null, $usecommonwords = true, $showtime = 0) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_format_event_time() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_format_event_time($event, $now, $linkparams, $usecommonwords, $showtime); +} + +/** + * Display month selector options. + * + * @deprecated since 3.3 + * @param string $name for the select element + * @param string|array $selected options for select elements + */ +function calendar_print_month_selector($name, $selected) { + debugging(__FUNCTION__ . '() is deprecated and no longer used in core.', DEBUG_DEVELOPER); + $months = array(); + for ($i = 1; $i <= 12; $i++) { + $months[$i] = userdate(gmmktime(12, 0, 0, $i, 15, 2000), '%B'); + } + echo html_writer::label(get_string('months'), 'menu'. $name, false, array('class' => 'accesshide')); + echo html_writer::select($months, $name, $selected, false); +} + +/** + * Checks to see if the requested type of event should be shown for the given user. + * + * @deprecated since 3.3 + * @param int $type The type to check the display for (default is to display all) + * @param stdClass|int|null $user The user to check for - by default the current user + * @return bool True if the tyep should be displayed false otherwise + */ +function calendar_show_event_type($type, $user = null) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::show_event_type() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::show_event_type($type, $user); +} + +/** + * Sets the display of the event type given $display. + * + * If $display = true the event type will be shown. + * If $display = false the event type will NOT be shown. + * If $display = null the current value will be toggled and saved. + * + * @deprecated since 3.3 + * @param int $type object of CALENDAR_EVENT_XXX + * @param bool $display option to display event type + * @param stdClass|int $user moodle user object or id, null means current user + */ +function calendar_set_event_type_display($type, $display = null, $user = null) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::set_event_type_display() instead.', + DEBUG_DEVELOPER); + \core_calendar\api::set_event_type_display($type, $display, $user); +} + +/** + * Get calendar's allowed types. + * + * @deprecated since 3.3 + * @param stdClass $allowed list of allowed edit for event type + * @param stdClass|int $course object of a course or course id + */ +function calendar_get_allowed_types(&$allowed, $course = null) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_allowed_types() instead.', + DEBUG_DEVELOPER); + \core_calendar\api::get_allowed_types($allowed, $course); +} + +/** + * See if user can add calendar entries at all used to print the "New Event" button. + * + * @deprecated since 3.3 + * @param stdClass $course object of a course or course id + * @return bool has the capability to add at least one event type + */ +function calendar_user_can_add_event($course) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::can_add_event_to_course() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::can_add_event_to_course($course); +} + +/** + * Check wether the current user is permitted to add events. + * + * @deprecated since 3.3 + * @param stdClass $event object of event + * @return bool has the capability to add event + */ +function calendar_add_event_allowed($event) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::can_add_event() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::can_add_event($event); +} + +/** + * Returns option list for the poll interval setting. + * + * @deprecated since 3.3 + * @return array An array of poll interval options. Interval => description. + */ +function calendar_get_pollinterval_choices() { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_poll_interval_choices() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_poll_interval_choices(); +} + +/** + * Returns option list of available options for the calendar event type, given the current user and course. + * + * @deprecated since 3.3 + * @param int $courseid The id of the course + * @return array An array containing the event types the user can create. + */ +function calendar_get_eventtype_choices($courseid) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_event_type_choices() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_event_type_choices($courseid); +} + +/** + * Add an iCalendar subscription to the database. + * + * @deprecated since 3.3 + * @param stdClass $sub The subscription object (e.g. from the form) + * @return int The insert ID, if any. + */ +function calendar_add_subscription($sub) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::add_subscription() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::add_subscription($sub); +} + +/** + * Add an iCalendar event to the Moodle calendar. + * + * @deprecated since 3.3 + * @param stdClass $event The RFC-2445 iCalendar event + * @param int $courseid The course ID + * @param int $subscriptionid The iCalendar subscription ID + * @param string $timezone The X-WR-TIMEZONE iCalendar property if provided + * @throws dml_exception A DML specific exception is thrown for invalid subscriptionids. + * @return int Code: CALENDAR_IMPORT_EVENT_UPDATED = updated, CALENDAR_IMPORT_EVENT_INSERTED = inserted, 0 = error + */ +function calendar_add_icalendar_event($event, $courseid, $subscriptionid, $timezone='UTC') { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::add_icalendar_event() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::add_icalendar_event($event, $courseid, $subscriptionid, $timezone); +} + +/** + * Update a subscription from the form data in one of the rows in the existing subscriptions table. + * + * @deprecated since 3.3 + * @param int $subscriptionid The ID of the subscription we are acting upon. + * @param int $pollinterval The poll interval to use. + * @param int $action The action to be performed. One of update or remove. + * @throws dml_exception if invalid subscriptionid is provided + * @return string A log of the import progress, including errors + */ +function calendar_process_subscription_row($subscriptionid, $pollinterval, $action) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::process_subscription_row() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::process_subscription_row($subscriptionid, $pollinterval, $action); +} + +/** + * Delete subscription and all related events. + * + * @deprecated since 3.3 + * @param int|stdClass $subscription subscription or it's id, which needs to be deleted. + */ +function calendar_delete_subscription($subscription) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::delete_subscription() instead.', + DEBUG_DEVELOPER); + \core_calendar\api::delete_subscription($subscription); +} + +/** + * From a URL, fetch the calendar and return an iCalendar object. + * + * @deprecated since 3.3 + * @param string $url The iCalendar URL + * @return iCalendar The iCalendar object + */ +function calendar_get_icalendar($url) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_icalendar() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_icalendar($url); +} + +/** + * Import events from an iCalendar object into a course calendar. + * + * @deprecated since 3.3 + * @param iCalendar $ical The iCalendar object. + * @param int $courseid The course ID for the calendar. + * @param int $subscriptionid The subscription ID. + * @return string A log of the import progress, including errors. + */ +function calendar_import_icalendar_events($ical, $courseid, $subscriptionid = null) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::import_icalendar_events() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::import_icalendar_events($ical, $courseid, $subscriptionid); +} + +/** + * Fetch a calendar subscription and update the events in the calendar. + * + * @deprecated since 3.3 + * @param int $subscriptionid The course ID for the calendar. + * @return string A log of the import progress, including errors. + */ +function calendar_update_subscription_events($subscriptionid) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::update_subscription_events() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::update_subscription_events($subscriptionid); +} + +/** + * Update a calendar subscription. Also updates the associated cache. + * + * @deprecated since 3.3 + * @param stdClass|array $subscription Subscription record. + * @throws coding_exception If something goes wrong + * @since Moodle 2.5 + */ +function calendar_update_subscription($subscription) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::update_subscription() instead.', + DEBUG_DEVELOPER); + \core_calendar\api::update_subscription($subscription); +} + +/** + * Checks to see if the user can edit a given subscription feed. + * + * @deprecated since 3.3 + * @param mixed $subscriptionorid Subscription object or id + * @return bool true if current user can edit the subscription else false + */ +function calendar_can_edit_subscription($subscriptionorid) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::can_edit_subscription() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::can_edit_subscription($subscriptionorid); +} + +/** + * Helper function to determine the context of a calendar subscription. + * Subscriptions can be created in two contexts COURSE, or USER. + * + * @deprecated since 3.3 + * @param stdClass $subscription + * @return context instance + */ +function calendar_get_calendar_context($subscription) { + debugging(__FUNCTION__ . '() is deprecated, please use \core_calendar\api::get_calendar_context() instead.', + DEBUG_DEVELOPER); + return \core_calendar\api::get_calendar_context($subscription); +} + +/** + * Update calendar subscriptions. + * + * @deprecated since 3.3 + * @return bool + */ +function calendar_cron() { + debugging(__FUNCTION__ . '() is deprecated and should not be used. Please use the core\task\calendar_cron_task instead.', + DEBUG_DEVELOPER); + + global $CFG, $DB; + + // In order to execute this we need bennu. + require_once($CFG->libdir.'/bennu/bennu.inc.php'); + + mtrace('Updating calendar subscriptions:'); + cron_trace_time_and_memory(); + + $time = time(); + $subscriptions = $DB->get_records_sql('SELECT * FROM {event_subscriptions} WHERE pollinterval > 0 + AND lastupdated + pollinterval < ?', array($time)); + foreach ($subscriptions as $sub) { + mtrace("Updating calendar subscription {$sub->name} in course {$sub->courseid}"); + try { + $log = calendar_update_subscription_events($sub->id); + mtrace(trim(strip_tags($log))); + } catch (moodle_exception $ex) { + mtrace('Error updating calendar subscription: ' . $ex->getMessage()); + } + } + + mtrace('Finished updating calendar subscriptions.'); + + return true; +} diff --git a/lib/tests/calendar_cron_task_test.php b/lib/tests/calendar_cron_task_test.php new file mode 100644 index 00000000000..0e86025b5df --- /dev/null +++ b/lib/tests/calendar_cron_task_test.php @@ -0,0 +1,79 @@ +. + +/** + * Contains the class containing unit tests for the calendar cron task. + * + * @package core + * @copyright 2017 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Class containing unit tests for the calendar cron task. + * + * @package core + * @copyright 2017 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class core_calendar_cron_task_testcase extends advanced_testcase { + + /** + * Tests set up + */ + protected function setUp() { + $this->resetAfterTest(); + } + + /** + * Test calendar cron task with a working subscription URL. + */ + public function test_cron_working_url() { + // ICal URL from external test repo. + $subscriptionurl = $this->getExternalTestFileUrl('/ical.ics'); + + $subscription = new stdClass(); + $subscription->eventtype = 'site'; + $subscription->name = 'test'; + $subscription->url = $subscriptionurl; + $subscription->pollinterval = 86400; + $subscription->lastupdated = 0; + \core_calendar\api::add_subscription($subscription); + + $this->expectOutputRegex('/Events imported: .* Events updated:/'); + $task = new \core\task\calendar_cron_task(); + $task->execute(); + } + + /** + * Test calendar cron task with a broken subscription URL. + */ + public function test_cron_broken_url() { + $subscription = new stdClass(); + $subscription->eventtype = 'site'; + $subscription->name = 'test'; + $subscription->url = 'brokenurl'; + $subscription->pollinterval = 86400; + $subscription->lastupdated = 0; + \core_calendar\api::add_subscription($subscription); + + $this->expectOutputRegex('/Error updating calendar subscription: The given iCal URL is invalid/'); + $task = new \core\task\calendar_cron_task(); + $task->execute(); + } +} diff --git a/user/calendar.php b/user/calendar.php index ef810f4dd54..23b733d2afd 100644 --- a/user/calendar.php +++ b/user/calendar.php @@ -46,7 +46,7 @@ if (isset($CFG->calendar_maxevents)) { $calendarform = new core_user\form\calendar_form(null, array('userid' => $user->id)); $user->timeformat = get_user_preferences('calendar_timeformat', ''); -$user->startwday = calendar_get_starting_weekday(); +$user->startwday = \core_calendar\api::get_starting_weekday(); $user->maxevents = get_user_preferences('calendar_maxevents', $defaultmaxevents); $user->lookahead = get_user_preferences('calendar_lookahead', $defaultlookahead); $user->persistflt = get_user_preferences('calendar_persistflt', 0);