/** * Test recurrence rules for yearly frequency. */ public function test_yearly_events() { global $DB; $rrule = 'FREQ=YEARLY;COUNT=3;BYMONTH=12'; // This should generate 3 events in total. $mang = new \core_calendar\rrule_manager($rrule); $mang->parse_rrule(); $mang->create_events($this->event); $count = $DB->count_records('event', array('repeatid' => $this->event->id)); $this->assertEquals(3, $count); for ($i = 0, $time = $this->event->timestart; $i < 3; $i++, $time = strtotime("+{$i} years", $this->event->timestart)) { $result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time)); $this->assertTrue($result); } // Create an event every december, until the time limit is hit. $until = strtotime('+20 day +10 years', $this->event->timestart); $until = date('YmdTHis', $until); $rrule = "FREQ=YEARLY;BYMONTH=12;UNTIL={$until}"; // Forever event. $mang = new \core_calendar\rrule_manager($rrule); $until = time() + YEARSECS * $mang::TIME_UNLIMITED_YEARS; $mang->parse_rrule(); $mang->create_events($this->event); $count = $DB->count_records('event', array('repeatid' => $this->event->id)); $this->assertEquals(11, $count); for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $yoffset = $i * 2, $time = strtotime("+{$yoffset} years", $this->event->timestart)) { $result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time)); $this->assertTrue($result); } // This should generate 5 events in total, every second year in the month of december. $rrule = 'FREQ=YEARLY;BYMONTH=12;INTERVAL=2;COUNT=5'; $mang = new \core_calendar\rrule_manager($rrule); $mang->parse_rrule(); $mang->create_events($this->event); $count = $DB->count_records('event', array('repeatid' => $this->event->id)); $this->assertEquals(5, $count); for ($i = 0, $time = $this->event->timestart; $i < 5; $i++, $yoffset = $i * 2, $time = strtotime("+{$yoffset} years", $this->event->timestart)) { $result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time)); $this->assertTrue($result); } $rrule = 'FREQ=YEARLY;BYMONTH=12;INTERVAL=2'; // Forever event. $mang = new \core_calendar\rrule_manager($rrule); $until = time() + YEARSECS * $mang::TIME_UNLIMITED_YEARS; $mang->parse_rrule(); $mang->create_events($this->event); $count = $DB->count_records('event', array('repeatid' => $this->event->id)); $this->assertEquals(6, $count); for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $yoffset = $i * 2, $time = strtotime("+{$yoffset} years", $this->event->timestart)) { $result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time)); $this->assertTrue($result); } // This much seconds after the start of the day. $offset = $this->event->timestart - mktime(0, 0, 0, date("n", $this->event->timestart), date("j", $this->event->timestart), date("Y", $this->event->timestart)); $yearstart = mktime(0, 0, 0, 1, 1, date("Y", $this->event->timestart)); $rrule = 'FREQ=YEARLY;COUNT=3;BYMONTH=12;BYDAY=1MO'; // This should generate 3 events in total. $mang = new \core_calendar\rrule_manager($rrule); $mang->parse_rrule(); $mang->create_events($this->event); $count = $DB->count_records('event', array('repeatid' => $this->event->id)); $this->assertEquals(3, $count); for ($i = 0; $i < 3; $i++) { $time = strtotime("+11 months +{$i} years", $yearstart); $time = strtotime("+1 Monday", $time) + $offset; $result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time)); $this->assertTrue($result); } // Create an event every december, until the time limit is hit. $until = strtotime('+20 day +10 years', $this->event->timestart); $until = date('YmdTHis', $until); $rrule = "FREQ=YEARLY;BYMONTH=12;UNTIL={$until};BYDAY=1MO"; $mang = new \core_calendar\rrule_manager($rrule); $until = time() + YEARSECS * $mang::TIME_UNLIMITED_YEARS; $mang->parse_rrule(); $mang->create_events($this->event); $count = $DB->count_records('event', array('repeatid' => $this->event->id)); $this->assertEquals(11, $count); for ($i = 0, $time = $this->event->timestart; $time < $until; $i++) { $result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time)); $this->assertTrue($result); $time = strtotime("+11 months +{$i} years", $yearstart); $time = strtotime("+1 Monday", $time) + $offset; } // This should generate 5 events in total, every second year in the month of december. $rrule = 'FREQ=YEARLY;BYMONTH=12;INTERVAL=2;COUNT=5;BYDAY=1MO'; $mang = new \core_calendar\rrule_manager($rrule); $mang->parse_rrule(); $mang->create_events($this->event); $count = $DB->count_records('event', array('repeatid' => $this->event->id)); $this->assertEquals(5, $count); for ($i = $yoffset = 0, $time = $this->event->timestart; $i < 5; $i++, $yoffset = $i * 2) { $result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time)); $this->assertTrue($result); $time = strtotime("+11 months +{$yoffset} years", $yearstart); $time = strtotime("+1 Monday", $time) + $offset; } $rrule = 'FREQ=YEARLY;BYMONTH=12;INTERVAL=2;BYDAY=1MO'; // Forever event. $mang = new \core_calendar\rrule_manager($rrule); $until = time() + YEARSECS * $mang::TIME_UNLIMITED_YEARS; $mang->parse_rrule(); $mang->create_events($this->event); $count = $DB->count_records('event', array('repeatid' => $this->event->id)); $this->assertEquals(6, $count); for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $yoffset = $i * 2) { $result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time)); $this->assertTrue($result); $time = strtotime("+11 months +{$yoffset} years", $yearstart); $time = strtotime("+1 Monday", $time) + $offset; } $rrule = 'FREQ=YEARLY;INTERVAL=2'; // Forever event. $mang = new \core_calendar\rrule_manager($rrule); $until = time() + YEARSECS * $mang::TIME_UNLIMITED_YEARS; $mang->parse_rrule(); $mang->create_events($this->event); $count = $DB->count_records('event', array('repeatid' => $this->event->id)); $this->assertEquals(6, $count); for ($i = 0, $time = $this->event->timestart; $time < $until; $i++, $yoffset = $i * 2) { $result = $DB->record_exists('event', array('repeatid' => $this->event->id, 'timestart' => $time)); $this->assertTrue($result); $time = strtotime("+11 months +{$yoffset} years", $yearstart); $time = strtotime("+1 Monday", $time) + $offset; } }
/** * 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', '<br />', $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', '<br />', $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))) { $eventrecord->id = $updaterecord->id; $return = CALENDAR_IMPORT_EVENT_UPDATED; // Update. } else { $return = CALENDAR_IMPORT_EVENT_INSERTED; // Insert. } if ($createdevent = 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; } }