Exemple #1
0
 /**
  * @depends testValues
  */
 function testRDATE()
 {
     $vcal = new VCalendar();
     $ev = $vcal->createComponent('VEVENT');
     $ev->UID = 'bla';
     $ev->RDATE = array(new DateTime('2014-08-07', new DateTimeZone('UTC')), new DateTime('2014-08-08', new DateTimeZone('UTC')));
     $dtStart = $vcal->createProperty('DTSTART');
     $dtStart->setDateTime(new DateTime('2011-10-07', new DateTimeZone('UTC')));
     $ev->add($dtStart);
     $vcal->add($ev);
     $it = new EventIterator($vcal, $ev->uid);
     // Max is to prevent overflow
     $max = 12;
     $result = array();
     foreach ($it as $item) {
         $result[] = $item;
         $max--;
         if (!$max) {
             break;
         }
     }
     $tz = new DateTimeZone('UTC');
     $this->assertEquals(array(new DateTime('2011-10-07', $tz), new DateTime('2014-08-07', $tz), new DateTime('2014-08-08', $tz)), $result);
 }
Exemple #2
0
 public function timeRangeTestData()
 {
     $tests = array();
     $calendar = new VCalendar();
     // Hard date and time
     $valarm1 = $calendar->createComponent('VALARM');
     $valarm1->add($calendar->createProperty('TRIGGER', '20120312T130000Z', array('VALUE' => 'DATE-TIME')));
     $tests[] = array($valarm1, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-04-01 01:00:00'), true);
     $tests[] = array($valarm1, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-03-10 01:00:00'), false);
     // Relation to start time of event
     $valarm2 = $calendar->createComponent('VALARM');
     $valarm2->add($calendar->createProperty('TRIGGER', '-P1D', array('VALUE' => 'DURATION')));
     $vevent2 = $calendar->createComponent('VEVENT');
     $vevent2->DTSTART = '20120313T130000Z';
     $vevent2->add($valarm2);
     $tests[] = array($valarm2, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-04-01 01:00:00'), true);
     $tests[] = array($valarm2, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-03-10 01:00:00'), false);
     // Relation to end time of event
     $valarm3 = $calendar->createComponent('VALARM');
     $valarm3->add($calendar->createProperty('TRIGGER', '-P1D', array('VALUE' => 'DURATION', 'RELATED' => 'END')));
     $vevent3 = $calendar->createComponent('VEVENT');
     $vevent3->DTSTART = '20120301T130000Z';
     $vevent3->DTEND = '20120401T130000Z';
     $vevent3->add($valarm3);
     $tests[] = array($valarm3, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), false);
     $tests[] = array($valarm3, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), true);
     // Relation to end time of todo
     $valarm4 = $calendar->createComponent('VALARM');
     $valarm4->TRIGGER = '-P1D';
     $valarm4->TRIGGER['VALUE'] = 'DURATION';
     $valarm4->TRIGGER['RELATED'] = 'END';
     $vtodo4 = $calendar->createComponent('VTODO');
     $vtodo4->DTSTART = '20120301T130000Z';
     $vtodo4->DUE = '20120401T130000Z';
     $vtodo4->add($valarm4);
     $tests[] = array($valarm4, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), false);
     $tests[] = array($valarm4, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), true);
     // Relation to start time of event + repeat
     $valarm5 = $calendar->createComponent('VALARM');
     $valarm5->TRIGGER = '-P1D';
     $valarm5->TRIGGER['VALUE'] = 'DURATION';
     $valarm5->REPEAT = 10;
     $valarm5->DURATION = 'P1D';
     $vevent5 = $calendar->createComponent('VEVENT');
     $vevent5->DTSTART = '20120301T130000Z';
     $vevent5->add($valarm5);
     $tests[] = array($valarm5, new DateTime('2012-03-09 01:00:00'), new DateTime('2012-03-10 01:00:00'), true);
     // Relation to start time of event + duration, but no repeat
     $valarm6 = $calendar->createComponent('VALARM');
     $valarm6->TRIGGER = '-P1D';
     $valarm6->TRIGGER['VALUE'] = 'DURATION';
     $valarm6->DURATION = 'P1D';
     $vevent6 = $calendar->createComponent('VEVENT');
     $vevent6->DTSTART = '20120313T130000Z';
     $vevent6->add($valarm6);
     $tests[] = array($valarm6, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-04-01 01:00:00'), true);
     $tests[] = array($valarm6, new DateTime('2012-03-01 01:00:00'), new DateTime('2012-03-10 01:00:00'), false);
     // Relation to end time of event (DURATION instead of DTEND)
     $valarm7 = $calendar->createComponent('VALARM');
     $valarm7->TRIGGER = '-P1D';
     $valarm7->TRIGGER['VALUE'] = 'DURATION';
     $valarm7->TRIGGER['RELATED'] = 'END';
     $vevent7 = $calendar->createComponent('VEVENT');
     $vevent7->DTSTART = '20120301T130000Z';
     $vevent7->DURATION = 'P30D';
     $vevent7->add($valarm7);
     $tests[] = array($valarm7, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), false);
     $tests[] = array($valarm7, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), true);
     // Relation to end time of event (No DTEND or DURATION)
     $valarm7 = $calendar->createComponent('VALARM');
     $valarm7->TRIGGER = '-P1D';
     $valarm7->TRIGGER['VALUE'] = 'DURATION';
     $valarm7->TRIGGER['RELATED'] = 'END';
     $vevent7 = $calendar->createComponent('VEVENT');
     $vevent7->DTSTART = '20120301T130000Z';
     $vevent7->add($valarm7);
     $tests[] = array($valarm7, new DateTime('2012-02-25 01:00:00'), new DateTime('2012-03-05 01:00:00'), true);
     $tests[] = array($valarm7, new DateTime('2012-03-25 01:00:00'), new DateTime('2012-04-05 01:00:00'), false);
     return $tests;
 }
 /**
  * ElementList should reject this.
  *
  * @expectedException \LogicException
  */
 public function testArrayAccessUnsetInt()
 {
     $calendar = new VCalendar();
     $property = $calendar->createProperty("X-PROP", null);
     $calendar->add($property);
     unset($calendar->{'X-PROP'}[0]);
 }
 /**
  * Parses the input data and returns a correct VFREEBUSY object, wrapped in
  * a VCALENDAR.
  *
  * @return Component
  */
 function getResult()
 {
     $busyTimes = [];
     foreach ($this->objects as $key => $object) {
         foreach ($object->getBaseComponents() as $component) {
             switch ($component->name) {
                 case 'VEVENT':
                     $FBTYPE = 'BUSY';
                     if (isset($component->TRANSP) && strtoupper($component->TRANSP) === 'TRANSPARENT') {
                         break;
                     }
                     if (isset($component->STATUS)) {
                         $status = strtoupper($component->STATUS);
                         if ($status === 'CANCELLED') {
                             break;
                         }
                         if ($status === 'TENTATIVE') {
                             $FBTYPE = 'BUSY-TENTATIVE';
                         }
                     }
                     $times = [];
                     if ($component->RRULE) {
                         try {
                             $iterator = new EventIterator($object, (string) $component->uid, $this->timeZone);
                         } catch (NoInstancesException $e) {
                             // This event is recurring, but it doesn't have a single
                             // instance. We are skipping this event from the output
                             // entirely.
                             unset($this->objects[$key]);
                             continue;
                         }
                         if ($this->start) {
                             $iterator->fastForward($this->start);
                         }
                         $maxRecurrences = 200;
                         while ($iterator->valid() && --$maxRecurrences) {
                             $startTime = $iterator->getDTStart();
                             if ($this->end && $startTime > $this->end) {
                                 break;
                             }
                             $times[] = [$iterator->getDTStart(), $iterator->getDTEnd()];
                             $iterator->next();
                         }
                     } else {
                         $startTime = $component->DTSTART->getDateTime($this->timeZone);
                         if ($this->end && $startTime > $this->end) {
                             break;
                         }
                         $endTime = null;
                         if (isset($component->DTEND)) {
                             $endTime = $component->DTEND->getDateTime($this->timeZone);
                         } elseif (isset($component->DURATION)) {
                             $duration = DateTimeParser::parseDuration((string) $component->DURATION);
                             $endTime = clone $startTime;
                             $endTime = $endTime->add($duration);
                         } elseif (!$component->DTSTART->hasTime()) {
                             $endTime = clone $startTime;
                             $endTime = $endTime->modify('+1 day');
                         } else {
                             // The event had no duration (0 seconds)
                             break;
                         }
                         $times[] = [$startTime, $endTime];
                     }
                     foreach ($times as $time) {
                         if ($this->end && $time[0] > $this->end) {
                             break;
                         }
                         if ($this->start && $time[1] < $this->start) {
                             break;
                         }
                         $busyTimes[] = [$time[0], $time[1], $FBTYPE];
                     }
                     break;
                 case 'VFREEBUSY':
                     foreach ($component->FREEBUSY as $freebusy) {
                         $fbType = isset($freebusy['FBTYPE']) ? strtoupper($freebusy['FBTYPE']) : 'BUSY';
                         // Skipping intervals marked as 'free'
                         if ($fbType === 'FREE') {
                             continue;
                         }
                         $values = explode(',', $freebusy);
                         foreach ($values as $value) {
                             list($startTime, $endTime) = explode('/', $value);
                             $startTime = DateTimeParser::parseDateTime($startTime);
                             if (substr($endTime, 0, 1) === 'P' || substr($endTime, 0, 2) === '-P') {
                                 $duration = DateTimeParser::parseDuration($endTime);
                                 $endTime = clone $startTime;
                                 $endTime = $endTime->add($duration);
                             } else {
                                 $endTime = DateTimeParser::parseDateTime($endTime);
                             }
                             if ($this->start && $this->start > $endTime) {
                                 continue;
                             }
                             if ($this->end && $this->end < $startTime) {
                                 continue;
                             }
                             $busyTimes[] = [$startTime, $endTime, $fbType];
                         }
                     }
                     break;
             }
         }
     }
     if ($this->baseObject) {
         $calendar = $this->baseObject;
     } else {
         $calendar = new VCalendar();
     }
     $vfreebusy = $calendar->createComponent('VFREEBUSY');
     $calendar->add($vfreebusy);
     if ($this->start) {
         $dtstart = $calendar->createProperty('DTSTART');
         $dtstart->setDateTime($this->start);
         $vfreebusy->add($dtstart);
     }
     if ($this->end) {
         $dtend = $calendar->createProperty('DTEND');
         $dtend->setDateTime($this->end);
         $vfreebusy->add($dtend);
     }
     $dtstamp = $calendar->createProperty('DTSTAMP');
     $dtstamp->setDateTime(new DateTimeImmutable('now', new \DateTimeZone('UTC')));
     $vfreebusy->add($dtstamp);
     foreach ($busyTimes as $busyTime) {
         $busyTime[0] = $busyTime[0]->setTimeZone(new \DateTimeZone('UTC'));
         $busyTime[1] = $busyTime[1]->setTimeZone(new \DateTimeZone('UTC'));
         $prop = $calendar->createProperty('FREEBUSY', $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z'));
         $prop['FBTYPE'] = $busyTime[2];
         $vfreebusy->add($prop);
     }
     return $calendar;
 }
 /**
  * Build a valid iCal format block from the given event
  *
  * @param  array    Hash array with event/task properties from libkolab
  * @param  object   VCalendar object to append event to or false for directly sending data to stdout
  * @param  callable Callback function to fetch attachment contents, false if no attachment export
  * @param  object   RECURRENCE-ID property when serializing a recurrence exception
  */
 private function _to_ical($event, $vcal, $get_attachment, $recurrence_id = null)
 {
     $type = $event['_type'] ?: 'event';
     $vcal_creator = new VObject\Component\VCalendar();
     $ve = $vcal_creator->createComponent($this->type_component_map[$type]);
     $ve->add('UID', $event['uid']);
     // set DTSTAMP according to RFC 5545, 3.8.7.2.
     $dtstamp = !empty($event['changed']) && !empty($this->method) ? $event['changed'] : new DateTime();
     $ve->add('DTSTAMP', $dtstamp);
     if ($event['allday']) {
         $ve->DTSTAMP['VALUE'] = 'DATE';
     }
     if (!empty($event['created'])) {
         $ve->add('CREATED', $event['created']);
     }
     if (!empty($event['changed'])) {
         $ve->add('LAST-MODIFIED', $event['changed']);
     }
     if (!empty($event['start'])) {
         $ve->add('DTSTART', $event['start']);
     }
     if ($event['allday']) {
         $ve->DTSTART['VALUE'] = 'DATE';
     }
     if (!empty($event['end'])) {
         $ve->add('DTEND', $event['end']);
     }
     if ($event['allday']) {
         $ve->DTEND['VALUE'] = 'DATE';
     }
     if (!empty($event['due'])) {
         $ve->add('DUE', $event['due']);
     }
     // we're exporting a recurrence instance only
     if (!$recurrence_id && $event['recurrence_date'] && $event['recurrence_date'] instanceof DateTime) {
         $recurrence_id = $vcal_creator->createProperty('RECURRENCE-ID');
         $recurrence_id->setDateTime($event['recurrence_date']);
         if ($event['allday']) {
             $recurrence_id['VALUE'] = 'DATE';
         }
         if ($event['thisandfuture']) {
             $recurrence_id->add('RANGE', 'THISANDFUTURE');
         }
     }
     if ($recurrence_id) {
         $ve->add($recurrence_id);
     }
     $ve->add('SUMMARY', $event['title']);
     if ($event['location']) {
         $ve->add('LOCATION', $event['location']);
     }
     if ($event['description']) {
         $ve->add('DESCRIPTION', strtr($event['description'], array("\r\n" => "\n", "\r" => "\n")));
     }
     // normalize line endings
     if (isset($event['sequence'])) {
         $ve->add('SEQUENCE', $event['sequence']);
     }
     if ($event['recurrence'] && !$recurrence_id) {
         $exdates = $rdates = null;
         if (isset($event['recurrence']['EXDATE'])) {
             $exdates = $event['recurrence']['EXDATE'];
             unset($event['recurrence']['EXDATE']);
             // don't serialize EXDATEs into RRULE value
         }
         if (isset($event['recurrence']['RDATE'])) {
             $rdates = $event['recurrence']['RDATE'];
             unset($event['recurrence']['RDATE']);
             // don't serialize RDATEs into RRULE value
         }
         if ($event['recurrence']['FREQ']) {
             $ve->add('RRULE', libcalendaring::to_rrule($event['recurrence'], (bool) $event['allday']));
         }
         // add EXDATEs each one per line (for Thunderbird Lightning)
         if (is_array($exdates)) {
             foreach ($exdates as $ex) {
                 $ve->add('EXDATE', $ex);
             }
         }
         // add RDATEs
         if (is_array($rdates) && !empty($rdates)) {
             $ve->RDATE = $rdates;
         }
     }
     if ($event['categories']) {
         $ve->add('CATEGORIES', (array) $event['categories']);
     }
     if (!empty($event['free_busy'])) {
         $ve->add('TRANSP', $event['free_busy'] == 'free' ? 'TRANSPARENT' : 'OPAQUE');
         // for Outlook clients we provide the X-MICROSOFT-CDO-BUSYSTATUS property
         if (stripos($this->agent, 'outlook') !== false) {
             $ve->add('X-MICROSOFT-CDO-BUSYSTATUS', $event['free_busy'] == 'outofoffice' ? 'OOF' : strtoupper($event['free_busy']));
         }
     }
     if ($event['priority']) {
         $ve->add('PRIORITY', $event['priority']);
     }
     if ($event['cancelled']) {
         $ve->add('STATUS', 'CANCELLED');
     } else {
         if ($event['free_busy'] == 'tentative') {
             $ve->add('STATUS', 'TENTATIVE');
         } else {
             if ($event['complete'] == 100) {
                 $ve->add('STATUS', 'COMPLETED');
             } else {
                 if (!empty($event['status'])) {
                     $ve->add('STATUS', $event['status']);
                 }
             }
         }
     }
     if (!empty($event['sensitivity'])) {
         $ve->add('CLASS', strtoupper($event['sensitivity']));
     }
     if (!empty($event['complete'])) {
         $ve->add('PERCENT-COMPLETE', intval($event['complete']));
     }
     // Apple iCal and BusyCal required the COMPLETED date to be set in order to consider a task complete
     if ($event['status'] == 'COMPLETED' || $event['complete'] == 100) {
         $ve->add('COMPLETED', $event['changed'] ?: new DateTime('now - 1 hour'), true);
     }
     if ($event['valarms']) {
         foreach ($event['valarms'] as $alarm) {
             $va = $vcal_creator->createComponent('VALARM');
             $va->ACTION = $alarm['action'];
             if ($alarm['trigger'] instanceof DateTime) {
                 $va->add('TRIGGER', $alarm['trigger']);
             } else {
                 $va->add('TRIGGER', $alarm['trigger']);
                 if (strtoupper($alarm['related']) == 'END') {
                     $va->TRIGGER['RELATED'] = 'END';
                 }
             }
             if ($alarm['action'] == 'EMAIL') {
                 foreach ((array) $alarm['attendees'] as $attendee) {
                     $va->add('ATTENDEE', 'mailto:' . $attendee);
                 }
             }
             if ($alarm['description']) {
                 $va->add('DESCRIPTION', $alarm['description'] ?: $event['title']);
             }
             if ($alarm['summary']) {
                 $va->add('SUMMARY', $alarm['summary']);
             }
             if ($alarm['duration']) {
                 $va->add('DURATION', $alarm['duration']);
                 $va->add('REPEAT', intval($alarm['repeat']));
             }
             if ($alarm['uri']) {
                 $va->add('ATTACH', $alarm['uri'], array('VALUE' => 'URI'));
             }
             $ve->add($va);
         }
     } else {
         if ($event['alarms']) {
             $va = $vcal_creator->createComponent('VALARM');
             list($trigger, $va->action) = explode(':', $event['alarms']);
             $val = libcalendaring::parse_alarm_value($trigger);
             if ($val[3]) {
                 $va->add('TRIGGER', $val[3]);
             } else {
                 if ($val[0] instanceof DateTime) {
                     $va->add('TRIGGER', $val[0]);
                 }
             }
             $ve->add($va);
         }
     }
     foreach ((array) $event['attendees'] as $attendee) {
         if ($attendee['role'] == 'ORGANIZER') {
             if (empty($event['organizer'])) {
                 $event['organizer'] = $attendee;
             }
         } else {
             if (!empty($attendee['email'])) {
                 if (isset($attendee['rsvp'])) {
                     $attendee['rsvp'] = $attendee['rsvp'] ? 'TRUE' : null;
                 }
                 $ve->add('ATTENDEE', 'mailto:' . $attendee['email'], array_filter(self::map_keys($attendee, $this->attendee_keymap)));
             }
         }
     }
     if ($event['organizer']) {
         $ve->add('ORGANIZER', 'mailto:' . $event['organizer']['email'], self::map_keys($event['organizer'], array('name' => 'CN')));
     }
     foreach ((array) $event['url'] as $url) {
         if (!empty($url)) {
             $ve->add('URL', $url);
         }
     }
     if (!empty($event['parent_id'])) {
         $ve->add('RELATED-TO', $event['parent_id'], array('RELTYPE' => 'PARENT'));
     }
     if ($event['comment']) {
         $ve->add('COMMENT', $event['comment']);
     }
     $memory_limit = parse_bytes(ini_get('memory_limit'));
     // export attachments
     if (!empty($event['attachments'])) {
         foreach ((array) $event['attachments'] as $attach) {
             // check available memory and skip attachment export if we can't buffer it
             // @todo: use rcube_utils::mem_check()
             if (is_callable($get_attachment) && $memory_limit > 0 && ($memory_used = function_exists('memory_get_usage') ? memory_get_usage() : 16 * 1024 * 1024) && $attach['size'] && $memory_used + $attach['size'] * 3 > $memory_limit) {
                 continue;
             }
             // embed attachments using the given callback function
             if (is_callable($get_attachment) && ($data = call_user_func($get_attachment, $attach['id'], $event))) {
                 // embed attachments for iCal
                 $ve->add('ATTACH', base64_encode($data), array_filter(array('VALUE' => 'BINARY', 'ENCODING' => 'BASE64', 'FMTTYPE' => $attach['mimetype'], 'X-LABEL' => $attach['name'])));
                 unset($data);
                 // attempt to free memory
             } else {
                 if (!empty($this->attach_uri)) {
                     $ve->add('ATTACH', strtr($this->attach_uri, array('{{id}}' => urlencode($attach['id']), '{{name}}' => urlencode($attach['name']), '{{mimetype}}' => urlencode($attach['mimetype']))), array('FMTTYPE' => $attach['mimetype'], 'VALUE' => 'URI'));
                 }
             }
         }
     }
     foreach ((array) $event['links'] as $uri) {
         $ve->add('ATTACH', $uri);
     }
     // add custom properties
     foreach ((array) $event['x-custom'] as $prop) {
         $ve->add($prop[0], $prop[1]);
     }
     // append to vcalendar container
     if ($vcal) {
         $vcal->add($ve);
     } else {
         // serialize and send to stdout
         echo $ve->serialize();
     }
     // append recurrence exceptions
     if (is_array($event['recurrence']) && $event['recurrence']['EXCEPTIONS']) {
         foreach ($event['recurrence']['EXCEPTIONS'] as $ex) {
             $exdate = $ex['recurrence_date'] ?: $ex['start'];
             $recurrence_id = $vcal_crator->createProperty('RECURRENCE-ID');
             $recurrence_id->setDateTime($exdate);
             if ($event['allday']) {
                 $recurrence_id['VALUE'] = 'DATE';
             }
             if ($ex['thisandfuture']) {
                 $recurrence_id->add('RANGE', 'THISANDFUTURE');
             }
             $this->_to_ical($ex, $vcal, $get_attachment, $recurrence_id);
         }
     }
 }
 /**
  * This method takes a FreeBusyData object and generates the VCALENDAR
  * object associated with it.
  *
  * @return VCalendar
  */
 protected function generateFreeBusyCalendar(FreeBusyData $fbData)
 {
     if ($this->baseObject) {
         $calendar = $this->baseObject;
     } else {
         $calendar = new VCalendar();
     }
     $vfreebusy = $calendar->createComponent('VFREEBUSY');
     $calendar->add($vfreebusy);
     if ($this->start) {
         $dtstart = $calendar->createProperty('DTSTART');
         $dtstart->setDateTime($this->start);
         $vfreebusy->add($dtstart);
     }
     if ($this->end) {
         $dtend = $calendar->createProperty('DTEND');
         $dtend->setDateTime($this->end);
         $vfreebusy->add($dtend);
     }
     $tz = new \DateTimeZone('UTC');
     $dtstamp = $calendar->createProperty('DTSTAMP');
     $dtstamp->setDateTime(new DateTimeImmutable('now', $tz));
     $vfreebusy->add($dtstamp);
     foreach ($fbData->getData() as $busyTime) {
         $busyType = strtoupper($busyTime['type']);
         // Ignoring all the FREE parts, because those are already assumed.
         if ($busyType === 'FREE') {
             continue;
         }
         $busyTime[0] = new \DateTimeImmutable('@' . $busyTime['start'], $tz);
         $busyTime[1] = new \DateTimeImmutable('@' . $busyTime['end'], $tz);
         $prop = $calendar->createProperty('FREEBUSY', $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z'));
         // Only setting FBTYPE if it's not BUSY, because BUSY is the
         // default anyway.
         if ($busyType !== 'BUSY') {
             $prop['FBTYPE'] = $busyType;
         }
         $vfreebusy->add($prop);
     }
     return $calendar;
 }
Exemple #7
0
 /**
  * @expectedException InvalidArgumentException
  */
 function testRemoveNotFound()
 {
     $comp = new VCalendar(array(), false);
     $prop = $comp->createProperty('A', 'B');
     $comp->remove($prop);
 }
 /**
  * @param string $cardData
  * @return null|VCalendar
  */
 public function buildBirthdayFromContact($cardData)
 {
     if (empty($cardData)) {
         return null;
     }
     try {
         $doc = Reader::read($cardData);
     } catch (Exception $e) {
         return null;
     }
     if (!isset($doc->BDAY)) {
         return null;
     }
     $birthday = $doc->BDAY;
     if (!(string) $birthday) {
         return null;
     }
     $title = str_replace('{name}', strtr((string) $doc->FN, array('\\,' => ',', '\\;' => ';')), '{name}');
     try {
         $date = new \DateTime($birthday);
     } catch (Exception $e) {
         return null;
     }
     $vCal = new VCalendar();
     $vCal->VERSION = '2.0';
     $vEvent = $vCal->createComponent('VEVENT');
     $vEvent->add('DTSTART');
     $vEvent->DTSTART->setDateTime($date);
     $vEvent->DTSTART['VALUE'] = 'DATE';
     $vEvent->add('DTEND');
     $date->add(new \DateInterval('P1D'));
     $vEvent->DTEND->setDateTime($date);
     $vEvent->DTEND['VALUE'] = 'DATE';
     $vEvent->{'UID'} = $doc->UID;
     $vEvent->{'RRULE'} = 'FREQ=YEARLY';
     $vEvent->{'SUMMARY'} = $title . ' (*' . $date->format('Y') . ')';
     $vEvent->{'TRANSP'} = 'TRANSPARENT';
     $alarm = $vCal->createComponent('VALARM');
     $alarm->add($vCal->createProperty('TRIGGER', '-PT0M', ['VALUE' => 'DURATION']));
     $alarm->add($vCal->createProperty('ACTION', 'DISPLAY'));
     $alarm->add($vCal->createProperty('DESCRIPTION', $vEvent->{'SUMMARY'}));
     $vEvent->add($alarm);
     $vCal->add($vEvent);
     return $vCal;
 }
Exemple #9
0
 /**
  * test for issue #336
  */
 function testValidateRruleBySecondZero()
 {
     $calendar = new VCalendar();
     $property = $calendar->createProperty('RRULE', 'FREQ=DAILY;BYHOUR=10;BYMINUTE=30;BYSECOND=0;UNTIL=20150616T153000Z');
     $result = $property->validate(Node::REPAIR);
     // There should be 0 warnings and the value should be unchanged
     $this->assertEmpty($result);
     $this->assertEquals('FREQ=DAILY;BYHOUR=10;BYMINUTE=30;BYSECOND=0;UNTIL=20150616T153000Z', $property->getValue());
 }
 /**
  * @depends testValues
  */
 function testComplexExclusions()
 {
     $vcal = new VCalendar();
     $ev = $vcal->createComponent('VEVENT');
     $ev->UID = 'bla';
     $ev->RRULE = 'FREQ=YEARLY;COUNT=10';
     $dtStart = $vcal->createProperty('DTSTART');
     $tz = new DateTimeZone('Canada/Eastern');
     $dtStart->setDateTime(new DateTime('2011-01-01 13:50:20', $tz));
     $exDate1 = $vcal->createProperty('EXDATE');
     $exDate1->setDateTimes(array(new DateTime('2012-01-01 13:50:20', $tz), new DateTime('2014-01-01 13:50:20', $tz)));
     $exDate2 = $vcal->createProperty('EXDATE');
     $exDate2->setDateTimes(array(new DateTime('2016-01-01 13:50:20', $tz)));
     $ev->add($dtStart);
     $ev->add($exDate1);
     $ev->add($exDate2);
     $vcal->add($ev);
     $it = new RecurrenceIterator($vcal, (string) $ev->uid);
     $this->assertEquals('yearly', $it->frequency);
     $this->assertEquals(1, $it->interval);
     $this->assertEquals(10, $it->count);
     $max = 20;
     $result = array();
     foreach ($it as $k => $item) {
         $result[] = $item;
         $max--;
         if (!$max) {
             break;
         }
     }
     $this->assertEquals(array(new DateTime('2011-01-01 13:50:20', $tz), new DateTime('2013-01-01 13:50:20', $tz), new DateTime('2015-01-01 13:50:20', $tz), new DateTime('2017-01-01 13:50:20', $tz), new DateTime('2018-01-01 13:50:20', $tz), new DateTime('2019-01-01 13:50:20', $tz), new DateTime('2020-01-01 13:50:20', $tz)), $result);
 }
 /**
  * convert calendar event to Sabre\VObject\Component
  * 
  * @param  \Sabre\VObject\Component\VCalendar $vcalendar
  * @param  Calendar_Model_Event               $_event
  * @param  Calendar_Model_Event               $_mainEvent
  */
 protected function _convertCalendarModelEvent(\Sabre\VObject\Component\VCalendar $vcalendar, Calendar_Model_Event $_event, Calendar_Model_Event $_mainEvent = null)
 {
     // clone the event and change the timezone
     $event = clone $_event;
     $event->setTimezone($event->originator_tz);
     $lastModifiedDateTime = $_event->last_modified_time ? $_event->last_modified_time : $_event->creation_time;
     if (!$event->creation_time instanceof Tinebase_DateTime) {
         throw new Tinebase_Exception_Record_Validation('creation_time needed for conversion to Sabre\\VObject\\Component');
     }
     $vevent = $vcalendar->create('VEVENT', array('CREATED' => $_event->creation_time->getClone()->setTimezone('UTC'), 'LAST-MODIFIED' => $lastModifiedDateTime->getClone()->setTimezone('UTC'), 'DTSTAMP' => Tinebase_DateTime::now(), 'UID' => $event->uid));
     $vevent->add('SEQUENCE', $event->hasExternalOrganizer() ? $event->external_seq : $event->seq);
     if ($event->isRecurException()) {
         $originalDtStart = $_event->getOriginalDtStart()->setTimezone($_event->originator_tz);
         $recurrenceId = $vevent->add('RECURRENCE-ID', $originalDtStart);
         if ($_mainEvent && $_mainEvent->is_all_day_event == true) {
             $recurrenceId['VALUE'] = 'DATE';
         }
     }
     // dtstart and dtend
     $dtstart = $vevent->add('DTSTART', $_event->dtstart->getClone()->setTimezone($event->originator_tz));
     if ($event->is_all_day_event == true) {
         $dtstart['VALUE'] = 'DATE';
         // whole day events ends at 23:59:(00|59) in Tine 2.0 but 00:00 the next day in vcalendar
         $event->dtend->addSecond($event->dtend->get('s') == 59 ? 1 : 0);
         $event->dtend->addMinute($event->dtend->get('i') == 59 ? 1 : 0);
         $dtend = $vevent->add('DTEND', $event->dtend);
         $dtend['VALUE'] = 'DATE';
     } else {
         $dtend = $vevent->add('DTEND', $event->dtend);
     }
     // auto status for deleted events
     if ($event->is_deleted) {
         $event->status = Calendar_Model_Event::STATUS_CANCELED;
     }
     // event organizer
     if (!empty($event->organizer)) {
         $organizerContact = $event->resolveOrganizer();
         if ($organizerContact instanceof Addressbook_Model_Contact && !empty($organizerContact->email)) {
             $organizer = $vevent->add('ORGANIZER', 'mailto:' . $organizerContact->email, array('CN' => $organizerContact->n_fileas, 'EMAIL' => $organizerContact->email));
         }
     }
     $this->_addEventAttendee($vevent, $event);
     $optionalProperties = array('class', 'status', 'description', 'geo', 'location', 'priority', 'summary', 'transp', 'url');
     foreach ($optionalProperties as $property) {
         if (!empty($event->{$property})) {
             $vevent->add(strtoupper($property), $event->{$property});
         }
     }
     $class = $event->class == Calendar_Model_Event::CLASS_PUBLIC ? 'PUBLIC' : 'CONFIDENTIAL';
     $vcalendar->add('X-CALENDARSERVER-ACCESS', $class);
     $vevent->add('X-CALENDARSERVER-ACCESS', $class);
     // categories
     if (!isset($event->tags)) {
         $event->tags = Tinebase_Tags::getInstance()->getTagsOfRecord($event);
     }
     if (isset($event->tags) && count($event->tags) > 0) {
         $vevent->add('CATEGORIES', (array) $event->tags->name);
     }
     // repeating event properties
     if ($event->rrule) {
         if ($event->is_all_day_event == true) {
             $vevent->add('RRULE', preg_replace_callback('/UNTIL=([\\d :-]{19})(?=;?)/', function ($matches) {
                 $dtUntil = new Tinebase_DateTime($matches[1]);
                 $dtUntil->setTimezone((string) Tinebase_Core::getUserTimezone());
                 return 'UNTIL=' . $dtUntil->format('Ymd');
             }, $event->rrule));
         } else {
             $vevent->add('RRULE', preg_replace('/(UNTIL=)(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2})/', '$1$2$3$4T$5$6$7Z', $event->rrule));
         }
         if ($event->exdate instanceof Tinebase_Record_RecordSet) {
             $event->exdate->addIndices(array('is_deleted'));
             $deletedEvents = $event->exdate->filter('is_deleted', true);
             foreach ($deletedEvents as $deletedEvent) {
                 $dateTime = $deletedEvent->getOriginalDtStart();
                 $exdate = $vevent->add('EXDATE');
                 if ($event->is_all_day_event == true) {
                     $dateTime->setTimezone($event->originator_tz);
                     $exdate['VALUE'] = 'DATE';
                 }
                 $exdate->setValue($dateTime);
             }
         }
     }
     $ownAttendee = Calendar_Model_Attender::getOwnAttender($event->attendee);
     if ($event->alarms instanceof Tinebase_Record_RecordSet) {
         $mozLastAck = NULL;
         $mozSnooze = NULL;
         foreach ($event->alarms as $alarm) {
             $valarm = $vcalendar->create('VALARM');
             $valarm->add('ACTION', 'DISPLAY');
             $valarm->add('DESCRIPTION', $event->summary);
             if ($dtack = Calendar_Controller_Alarm::getAcknowledgeTime($alarm)) {
                 $valarm->add('ACKNOWLEDGED', $dtack->getClone()->setTimezone('UTC')->format('Ymd\\THis\\Z'));
                 $mozLastAck = $dtack > $mozLastAck ? $dtack : $mozLastAck;
             }
             if ($dtsnooze = Calendar_Controller_Alarm::getSnoozeTime($alarm)) {
                 $mozSnooze = $dtsnooze > $mozSnooze ? $dtsnooze : $mozSnooze;
             }
             if (is_numeric($alarm->minutes_before)) {
                 if ($event->dtstart == $alarm->alarm_time) {
                     $periodString = 'PT0S';
                 } else {
                     $interval = $event->dtstart->diff($alarm->alarm_time);
                     $periodString = sprintf('%sP%s%s%s%s', $interval->format('%r'), $interval->format('%d') > 0 ? $interval->format('%dD') : null, $interval->format('%h') > 0 || $interval->format('%i') > 0 ? 'T' : null, $interval->format('%h') > 0 ? $interval->format('%hH') : null, $interval->format('%i') > 0 ? $interval->format('%iM') : null);
                 }
                 # TRIGGER;VALUE=DURATION:-PT1H15M
                 $trigger = $valarm->add('TRIGGER', $periodString);
                 $trigger['VALUE'] = "DURATION";
             } else {
                 # TRIGGER;VALUE=DATE-TIME:...
                 $trigger = $valarm->add('TRIGGER', $alarm->alarm_time->getClone()->setTimezone('UTC')->format('Ymd\\THis\\Z'));
                 $trigger['VALUE'] = "DATE-TIME";
             }
             $vevent->add($valarm);
         }
         if ($mozLastAck instanceof DateTime) {
             $vevent->add('X-MOZ-LASTACK', $mozLastAck->getClone()->setTimezone('UTC'), array('VALUE' => 'DATE-TIME'));
         }
         if ($mozSnooze instanceof DateTime) {
             $vevent->add('X-MOZ-SNOOZE-TIME', $mozSnooze->getClone()->setTimezone('UTC'), array('VALUE' => 'DATE-TIME'));
         }
     }
     $baseUrl = Tinebase_Core::getHostname() . "/webdav/Calendar/records/Calendar_Model_Event/{$event->getId()}/";
     if ($event->attachments instanceof Tinebase_Record_RecordSet) {
         foreach ($event->attachments as $attachment) {
             $filename = rawurlencode($attachment->name);
             $attach = $vcalendar->createProperty('ATTACH', "{$baseUrl}{$filename}", array('MANAGED-ID' => $attachment->hash, 'FMTTYPE' => $attachment->contenttype, 'SIZE' => $attachment->size, 'FILENAME' => $filename), 'TEXT');
             $vevent->add($attach);
         }
     }
     $vcalendar->add($vevent);
 }