diff --git a/calendar/lib.php b/calendar/lib.php index 9037cbbae2a..96aa0ce970a 100644 --- a/calendar/lib.php +++ b/calendar/lib.php @@ -1890,6 +1890,51 @@ function calendar_add_event_allowed($event) { } } +/** + * Convert region timezone to php supported timezone + * + * @param string $tz value from ical file + * @return string $tz php supported timezone + */ +function calendar_normalize_tz($tz) { + switch ($tz) { + case('CST'): + case('Central Time'): + case('Central Standard Time'): + $tz = 'America/Chicago'; + break; + case('CET'): + case('Central European Time'): + $tz = 'Europe/Berlin'; + break; + case('EST'): + case('Eastern Time'): + case('Eastern Standard Time'): + $tz = 'America/New_York'; + break; + case('PST'): + case('Pacific Time'): + case('Pacific Standard Time'): + $tz = 'America/Los_Angeles'; + break; + case('China Time'): + case('China Standard Time'): + $tz = 'Asia/Beijing'; + break; + case('IST'): + case('India Time'): + case('India Standard Time'): + $tz = 'Asia/New_Delhi'; + break; + case('JST'); + case('Japan Time'): + case('Japan Standard Time'): + $tz = 'Asia/Tokyo'; + break; + } + return $tz; +} + /** * Manage calendar events * @@ -2919,6 +2964,7 @@ function calendar_add_icalendar_event($event, $courseid, $subscriptionid, $timez $defaulttz = date_default_timezone_get(); $tz = isset($event->properties['DTSTART'][0]->parameters['TZID']) ? $event->properties['DTSTART'][0]->parameters['TZID'] : $timezone; + $tz = calendar_normalize_tz($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 @@ -3065,7 +3111,9 @@ function calendar_import_icalendar_events($ical, $courseid, $subscriptionid = nu $updatecount = 0; // Large calendars take a while... - core_php_time_limit::raise(300); + if (!CLI_SCRIPT) { + core_php_time_limit::raise(300); + } // Mark all events in a subscription with a zero timestamp. if (!empty($subscriptionid)) { diff --git a/calendar/tests/ical_test.php b/calendar/tests/ical_test.php index 7b4e1175fec..cfc5bb3523d 100644 --- a/calendar/tests/ical_test.php +++ b/calendar/tests/ical_test.php @@ -74,4 +74,63 @@ public function test_calendar_update_subscription() { $this->setExpectedException('coding_exception'); 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/lib/bennu/iCalendar_components.php b/lib/bennu/iCalendar_components.php index 1f6bf3da326..9b9e313572c 100644 --- a/lib/bennu/iCalendar_components.php +++ b/lib/bennu/iCalendar_components.php @@ -287,8 +287,10 @@ function unserialize($string) { if($parent_component == null) { $parent_component = $this; // If there's no components on the stack, use the iCalendar object } - if ($parent_component->add_component($component) === false) { - $this->parser_error("Failed to add component on line $key"); + if ($component !== null) { + if ($parent_component->add_component($component) === false) { + $this->parser_error("Failed to add component on line $key"); + } } if ($parent_component != $this) { // If we're not using the iCalendar array_push($components, $parent_component); // Put the component back on the stack diff --git a/lib/bennu/iCalendar_parameters.php b/lib/bennu/iCalendar_parameters.php index d2640834e40..af3f5ad802e 100644 --- a/lib/bennu/iCalendar_parameters.php +++ b/lib/bennu/iCalendar_parameters.php @@ -104,9 +104,7 @@ static function is_valid_value(&$parent_property, $parameter, $value) { 'VIDEO' => array('MPEG', 'QUICKTIME', 'VND.VIVO', 'VND.MOTOROLA.VIDEO', 'VND.MOTOROLA.VIDEOP') ); $value = strtoupper($value); - if(rfc2445_is_xname($value)) { - return true; - } + // Mimetype is enumerated above and anything else results in false. @list($type, $subtype) = explode('/', $value); if(empty($type) || empty($subtype)) { return false; @@ -178,7 +176,7 @@ static function is_valid_value(&$parent_property, $parameter, $value) { if(empty($value)) { return false; } - return (strcspn($value, '";:,') == strlen($value)); + return (strcspn($value, ';:,') == strlen($value)); break; case 'VALUE': @@ -227,7 +225,7 @@ static function do_value_formatting($parameter, $value) { // Parameters we shouldn't be messing with case 'TZID': - return $value; + return str_replace('"', '', $value); break; } } diff --git a/lib/bennu/iCalendar_properties.php b/lib/bennu/iCalendar_properties.php index 8a557a49621..260c42a4a9f 100644 --- a/lib/bennu/iCalendar_properties.php +++ b/lib/bennu/iCalendar_properties.php @@ -1227,6 +1227,8 @@ class iCalendar_property_x extends iCalendar_property { function __construct() { parent::__construct(); $this->valid_parameters = array( + // X-ALT-DESC (Description) can have FMTTYPE declaration of text/html is a property for HTML content. + 'FMTTYPE' => RFC2445_OPTIONAL | RFC2445_ONCE, 'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE, RFC2445_XNAME => RFC2445_OPTIONAL ); diff --git a/lib/bennu/readme_moodle.txt b/lib/bennu/readme_moodle.txt index 783f27e408a..c27a1720e0b 100644 --- a/lib/bennu/readme_moodle.txt +++ b/lib/bennu/readme_moodle.txt @@ -5,3 +5,5 @@ modifications: 2/ replaced mbstring functions with moodle core_text (28 Nov 2011) 3/ replaced explode in iCalendar_component::unserialize() with preg_split to support various line breaks (20 Nov 2012) 4/ updated rfc2445_is_valid_value() to accept single part rrule as a valid value (16 Jun 2014) +5/ updated DTEND;TZID and DTSTAR;TZID values to support quotations (7 Nov 2014) +6/ added calendar_normalize_tz function to convert region timezone to php supported timezone (7 Nov 2014) diff --git a/lib/tests/fixtures/google_gmail.ics b/lib/tests/fixtures/google_gmail.ics new file mode 100644 index 00000000000..e9d7d080502 --- /dev/null +++ b/lib/tests/fixtures/google_gmail.ics @@ -0,0 +1,21 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VEVENT +DTSTART:20141028T213000Z +DTEND:20141028T223000Z +DTSTAMP:20141028T211302Z +ORGANIZER;CN=John Smith:mailto:john.smith@gmail.com +UID:hjlv3v1lcerpi629s5gpfuijk0@google.com +CREATED:20141028T210927Z +DESCRIPTION:Lorem ipsum dolor sit amet\, consectetur adipisicing elit. +LAST-MODIFIED:20141028T211302Z +LOCATION:150 Willis St\, Te Aro\, Wellington 6011\, New Zealand +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:Google calendar +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR diff --git a/lib/tests/fixtures/ms_outlook_2010.ics b/lib/tests/fixtures/ms_outlook_2010.ics new file mode 100644 index 00000000000..69d85ab93c2 --- /dev/null +++ b/lib/tests/fixtures/ms_outlook_2010.ics @@ -0,0 +1,42 @@ +BEGIN:VCALENDAR +PRODID:-//Microsoft Corporation//Outlook 14.0 MIMEDIR//EN +VERSION:2.0 +METHOD:PUBLISH +X-MS-OLK-FORCEINSPECTOROPEN:TRUE +BEGIN:VTIMEZONE +TZID:Central Standard Time +BEGIN:STANDARD +DTSTART:16011104T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0600 +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:16010311T020000 +RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3 +TZOFFSETFROM:-0600 +TZOFFSETTO:-0500 +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +CLASS:PUBLIC +CREATED:20140916T144857Z +DESCRIPTION:\n +DTSTART;TZID="Central Standard Time":20140916T090000 +DTSTAMP:20140916T144857Z +DTEND;TZID="Central Standard Time":20140916T093000 +LAST-MODIFIED:20140916T144857Z +PRIORITY:5 +SEQUENCE:0 +SUMMARY;LANGUAGE=en-us:test +TRANSP:OPAQUE +UID:040000008200E00074C5B7101A82E0080000000000D9F06393D1CF010000000000000000100000007B710E58C9CE8146B1403BF7E84162FB +X-ALT-DESC;FMTTYPE=text/html:\n\n\n\n\n\n\n\n

\n\n\n +X-MICROSOFT-CDO-BUSYSTATUS:BUSY +X-MICROSOFT-CDO-IMPORTANCE:1 +X-MICROSOFT-DISALLOW-COUNTER:FALSE +X-MS-OLK-AUTOFILLLOCATION:TRUE +X-MS-OLK-AUTOSTARTCHECK:FALSE +X-MS-OLK-CONFTYPE:0 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/lib/tests/fixtures/osx_yosemite.ics b/lib/tests/fixtures/osx_yosemite.ics new file mode 100644 index 00000000000..fe0a2e74c0f --- /dev/null +++ b/lib/tests/fixtures/osx_yosemite.ics @@ -0,0 +1,36 @@ +BEGIN:VCALENDAR +CALSCALE:GREGORIAN +VERSION:2.0 +METHOD:PUBLISH +X-WR-CALNAME:pokus +X-WR-TIMEZONE:Pacific/Auckland +X-APPLE-CALENDAR-COLOR:#1BADF8 +BEGIN:VTIMEZONE +TZID:Pacific/Auckland +BEGIN:DAYLIGHT +TZOFFSETFROM:+1200 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1SU +DTSTART:20070930T020000 +TZNAME:NZDT +TZOFFSETTO:+1300 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+1300 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU +DTSTART:20080406T030000 +TZNAME:NZST +TZOFFSETTO:+1200 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20141028T204253Z +UID:5571BBDC-AC05-4486-9E57-118EBDFFA385 +DTEND;TZID=Pacific/Auckland:20140917T023000 +TRANSP:OPAQUE +SUMMARY:Test event +DTSTART;TZID=Pacific/Auckland:20140917T020000 +DTSTAMP:20141028T204253Z +SEQUENCE:0 +DESCRIPTION:This is a note. +END:VEVENT +END:VCALENDAR