/** * add event attendee to VEVENT object * * @param \Sabre\VObject\Component\VEvent $vevent * @param Calendar_Model_Event $event */ protected function _addEventAttendee(\Sabre\VObject\Component\VEvent $vevent, Calendar_Model_Event $event) { parent::_addEventAttendee($vevent, $event); if (empty($event->attendee)) { return; } // add organizer as CHAIR Attendee if he's no organizer, otherwise yosemite would add an attendee // when editing the event again. // NOTE: when the organizer edits the event he becomes attendee anyway, see comments in MSEventFacade::update if (version_compare($this->_version, '10.10', '>=')) { if (!empty($event->organizer)) { $organizerContact = $event->resolveOrganizer(); if ($organizerContact instanceof Addressbook_Model_Contact) { $organizerAttendee = Calendar_Model_Attender::getAttendee($event->attendee, new Calendar_Model_Attender(array('user_id' => $organizerContact->getId(), 'user_type' => Calendar_Model_Attender::USERTYPE_USER))); if (!$organizerAttendee) { $parameters = array('CN' => $organizerContact->n_fileas, 'CUTYPE' => 'INDIVIDUAL', 'PARTSTAT' => 'ACCEPTED', 'ROLE' => 'CHAIR'); $organizerEmail = $organizerContact->email; if (strpos($organizerEmail, '@') !== false) { $parameters['EMAIL'] = $organizerEmail; } $vevent->add('ATTENDEE', (strpos($organizerEmail, '@') !== false ? 'mailto:' : 'urn:uuid:') . $organizerEmail, $parameters); } } } } }
/** * (non-PHPdoc) * @see Sabre\DAV\Collection::getChild() */ public function getChild($_name) { $eventId = $_name instanceof Tinebase_Record_Interface ? $_name->getId() : $this->_getIdFromName($_name); // check if child exists in calendarQuery cache if ($this->_calendarQueryCache && isset($this->_calendarQueryCache[$eventId])) { $child = $this->_calendarQueryCache[$eventId]; // remove entries from cache / they will not be used anymore unset($this->_calendarQueryCache[$eventId]); if (empty($this->_calendarQueryCache)) { $this->_calendarQueryCache = null; } return $child; } $modelName = $this->_application->name . '_Model_' . $this->_model; if ($_name instanceof $modelName) { $object = $_name; } else { $filterClass = $this->_application->name . '_Model_' . $this->_model . 'Filter'; $filter = new $filterClass(array(array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_container->getId()), array('condition' => 'OR', 'filters' => array(array('field' => 'id', 'operator' => 'equals', 'value' => $eventId), array('field' => 'uid', 'operator' => 'equals', 'value' => $eventId))))); $object = $this->_getController()->search($filter, null, false, false, 'sync')->getFirstRecord(); if ($object == null) { throw new Sabre\DAV\Exception\NotFound('Object not found'); } } $httpRequest = new Sabre\HTTP\Request(); // lie about existence of event of request is a PUT request from an ATTENDEE for an already existing event // to prevent ugly (and not helpful) error messages on the client if (isset($_SERVER['REQUEST_METHOD']) && $httpRequest->getMethod() == 'PUT' && $httpRequest->getHeader('If-None-Match') === '*') { if ($object->organizer != Tinebase_Core::getUser()->contact_id && Calendar_Model_Attender::getOwnAttender($object->attendee) !== null) { throw new Sabre\DAV\Exception\NotFound('Object not found'); } } $objectClass = $this->_application->name . '_Frontend_WebDAV_' . $this->_model; return new $objectClass($this->_container, $object); }
protected function _addEventAttendee(Sabre_VObject_Component $_vevent, Calendar_Model_Event $_event) { if (version_compare($this->_version, '1.0b2', '>')) { parent::_addEventAttendee($_vevent, $_event); } else { // special handling for Lightning <= 1.0b2 // attendees get screwed up, if the CN contains commas Calendar_Model_Attender::resolveAttendee($_event->attendee, FALSE, $_event); foreach ($_event->attendee as $eventAttendee) { $attendeeEmail = $eventAttendee->getEmail(); if ($attendeeEmail) { $attendee = new Sabre_VObject_Property('ATTENDEE', (strpos($attendeeEmail, '@') !== false ? 'mailto:' : 'urn:uuid:') . $attendeeEmail); $attendee->add('CN', str_replace(',', null, $eventAttendee->getName())); $attendee->add('CUTYPE', Calendar_Convert_Event_VCalendar_Abstract::$cutypeMap[$eventAttendee->user_type]); $attendee->add('PARTSTAT', $eventAttendee->status); $attendee->add('ROLE', "{$eventAttendee->role}-PARTICIPANT"); $attendee->add('RSVP', 'FALSE'); if (strpos($attendeeEmail, '@') !== false) { $attendee->add('EMAIL', $attendeeEmail); } $_vevent->add($attendee); } } } }
public function testConvert() { $emailArrayFromClient = array(array('userType' => 'user', 'firstName' => 'Users', 'lastName' => '(Group)', 'partStat' => 'NEEDS-ACTION', 'role' => 'REQ', 'email' => 'urn:uuid:principals/intelligroups/cc74c2880f8c5c0eaacc57ea95f4d2571fb8a4b1')); $event = new Calendar_Model_Event(); Calendar_Model_Attender::emailsToAttendee($event, $emailArrayFromClient); $this->assertEquals('cc74c2880f8c5c0eaacc57ea95f4d2571fb8a4b1', $event->attendee->getFirstRecord()->user_id); $this->assertEquals('group', $event->attendee->getFirstRecord()->user_type); }
/** * resolve related event data: attendee, rrule and organizer * * @param Calendar_Model_Event $_record */ public static function resolveRelatedData($_record) { if (!$_record instanceof Calendar_Model_Event) { return; } Calendar_Model_Attender::resolveAttendee($_record->attendee, TRUE, $_record); self::resolveRrule($_record); self::resolveOrganizer($_record); }
/** * (non-PHPdoc) * @see Sabre\DAV\Collection::getChild() */ public function getChild($_name) { try { $event = $_name instanceof Calendar_Model_Event ? $_name : $this->_getController()->get($this->_getIdFromName($_name)); } catch (Tinebase_Exception_NotFound $tenf) { throw new \Sabre\DAV\Exception\NotFound('Object not found'); } $ownAttendee = Calendar_Model_Attender::getOwnAttender($event->attendee); $displayContainer = $ownAttendee->displaycontainer_id instanceof Tinebase_Model_Container ? $ownAttendee->displaycontainer_id : Tinebase_Container::getInstance()->get($ownAttendee->displaycontainer_id); return new Calendar_Frontend_WebDAV_Event($displayContainer, $event); }
/** * resolve records and prepare for export (set user timezone, ...) * * @param Tinebase_Record_RecordSet $_records */ protected function _resolveRecords(Tinebase_Record_RecordSet $_records) { parent::_resolveRecords($_records); Calendar_Model_Attender::resolveAttendee($_records->attendee, false, $_records); foreach ($_records as $record) { $attendee = $record->attendee->getName(); $record->attendee = implode('& ', $attendee); $organizer = $record->resolveOrganizer(); if ($organizer) { $record->organizer = $organizer->n_fileas; } } }
/** * resolve records and prepare for export (set user timezone, ...) * * @param Tinebase_Record_RecordSet $_records */ protected function _resolveRecords(Tinebase_Record_RecordSet $_records) { Calendar_Model_Attender::resolveAttendee($_records->attendee, false, $_records); $organizers = Addressbook_Controller_Contact::getInstance()->getMultiple(array_unique($_records->organizer), TRUE); foreach ($_records as $record) { $attendee = $record->attendee->getName(); $record->attendee = implode('& ', $attendee); $organizer = $organizers->getById($record->organizer); if ($organizer) { $record->organizer = $organizer->n_fileas; } } }
/** * @return void */ public function testEmailsToAttendeeWithGroups() { $event = $this->_getEvent(); $persistentEvent = Calendar_Controller_Event::getInstance()->create($event); $primaryGroup = Tinebase_Group::getInstance()->getGroupById(Tinebase_Core::getUser()->accountPrimaryGroup); $newAttendees = array(array('userType' => Calendar_Model_Attender::USERTYPE_USER, 'firstName' => $this->_testUser->accountFirstName, 'lastName' => $this->_testUser->accountLastName, 'partStat' => Calendar_Model_Attender::STATUS_TENTATIVE, 'role' => Calendar_Model_Attender::ROLE_REQUIRED, 'email' => $this->_testUser->accountEmailAddress), array('userType' => Calendar_Model_Attender::USERTYPE_GROUP, 'displayName' => $primaryGroup->name, 'partStat' => Calendar_Model_Attender::STATUS_NEEDSACTION, 'role' => Calendar_Model_Attender::ROLE_REQUIRED, 'email' => '*****@*****.**')); Calendar_Model_Attender::emailsToAttendee($persistentEvent, $newAttendees, TRUE); $persistentEvent = Calendar_Controller_Event::getInstance()->update($persistentEvent); $attendees = clone $persistentEvent->attendee; Calendar_Model_Attender::resolveAttendee($attendees); $newAttendees = array(); foreach ($attendees as $attendee) { $newAttendees[] = array('userType' => $attendee->user_type == 'group' ? Calendar_Model_Attender::USERTYPE_GROUP : Calendar_Model_Attender::USERTYPE_USER, 'partStat' => Calendar_Model_Attender::STATUS_TENTATIVE, 'role' => Calendar_Model_Attender::ROLE_REQUIRED, 'email' => $attendee->user_type == 'group' ? $attendee->user_id->getId() : $attendee->user_id->email, 'displayName' => $attendee->user_type == 'group' ? $attendee->user_id->name : $attendee->user_id->n_fileas); } Calendar_Model_Attender::emailsToAttendee($persistentEvent, $newAttendees, TRUE); $persistentEvent = Calendar_Controller_Event::getInstance()->update($persistentEvent); //var_dump($persistentEvent->attendee->toArray()); // there must be more than 2 attendees the user, the group + the groupmembers $this->assertGreaterThan(2, count($persistentEvent->attendee)); }
/** * (non-PHPdoc) * @see Calendar_Convert_Event_VCalendar_Abstract::_addEventAttendee() */ protected function _addEventAttendee(\Sabre\VObject\Component\VEvent $vevent, Calendar_Model_Event $event) { if (version_compare($this->_version, '1.0b2', '>')) { parent::_addEventAttendee($vevent, $event); } else { // special handling for Lightning <= 1.0b2 // attendees get screwed up, if the CN contains commas Calendar_Model_Attender::resolveAttendee($event->attendee, FALSE, $event); foreach ($event->attendee as $eventAttendee) { $attendeeEmail = $eventAttendee->getEmail(); if ($attendeeEmail) { $parameters = array('CN' => str_replace(',', null, $eventAttendee->getName()), 'CUTYPE' => Calendar_Convert_Event_VCalendar_Abstract::$cutypeMap[$eventAttendee->user_type], 'PARTSTAT' => $eventAttendee->status, 'ROLE' => "{$eventAttendee->role}-PARTICIPANT", 'RSVP' => 'FALSE'); if (strpos($attendeeEmail, '@') !== false) { $parameters['EMAIL'] = $attendeeEmail; } $vevent->add('ATTENDEE', (strpos($attendeeEmail, '@') !== false ? 'mailto:' : 'urn:uuid:') . $attendeeEmail, $parameters); } } } }
/** * (non-PHPdoc) * @see Sabre_DAV_Collection::getChild() */ public function getChild($_name) { $modelName = $this->_application->name . '_Model_' . $this->_model; if ($_name instanceof $modelName) { $object = $_name; } else { $filterClass = $this->_application->name . '_Model_' . $this->_model . 'Filter'; $filter = new $filterClass(array(array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_container->getId()), array('condition' => 'OR', 'filters' => array(array('field' => 'id', 'operator' => 'equals', 'value' => $this->_getIdFromName($_name)), array('field' => 'uid', 'operator' => 'equals', 'value' => $this->_getIdFromName($_name)))))); $object = $this->_getController()->search($filter, null, false, false, 'sync')->getFirstRecord(); if ($object == null) { throw new Sabre_DAV_Exception_FileNotFound('Object not found'); } } $httpRequest = new Sabre_HTTP_Request(); // lie about existance of event of request is a PUT request from an ATTENDEE for an already existing event // to prevent ugly (and not helpful) error messages on the client if (isset($_SERVER['REQUEST_METHOD']) && $httpRequest->getMethod() == 'PUT' && $httpRequest->getHeader('If-None-Match') === '*') { if ($object->organizer != Tinebase_Core::getUser()->contact_id && Calendar_Model_Attender::getOwnAttender($object->attendee) !== null) { throw new Sabre_DAV_Exception_FileNotFound('Object not found'); } } $objectClass = $this->_application->name . '_Frontend_WebDAV_' . $this->_model; return new $objectClass($this->_container, $object); }
/** * set status of own attender depending on BusyStatus * * @param SimpleXMLElement $xmlData * @param Calendar_Model_Event $event * * @todo move detection of special handling / device type to device library */ protected function _handleBusyStatus($data, $event) { if (!isset($data->busyStatus)) { return; } $ownAttender = Calendar_Model_Attender::getOwnAttender($event->attendee); if ($ownAttender === NULL) { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' No own attender found.'); } return; } $busyStatus = $data->busyStatus; if (in_array(strtolower($this->_device->devicetype), $this->_devicesWithWrongBusyStatusDefault)) { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Device uses a bad default setting. BUSY and FREE are mapped to ACCEPTED.'); } $busyStatusMapping = array(Syncroton_Model_Event::BUSY_STATUS_BUSY => Calendar_Model_Attender::STATUS_ACCEPTED, Syncroton_Model_Event::BUSY_STATUS_TENATTIVE => Calendar_Model_Attender::STATUS_TENTATIVE, Syncroton_Model_Event::BUSY_STATUS_FREE => Calendar_Model_Attender::STATUS_ACCEPTED); } else { $busyStatusMapping = $this->_busyStatusMapping; } if (isset($busyStatusMapping[$busyStatus])) { $ownAttender->status = $busyStatusMapping[$busyStatus]; } else { $ownAttender->status = Calendar_Model_Attender::STATUS_NEEDSACTION; } }
/** * calculate event permissions and remove events that don't match * * @param Tinebase_Record_RecordSet $events * @param Tinebase_Model_Filter_AclFilter $grantsFilter */ protected function _checkGrants($events, $grantsFilter) { $currentContact = Tinebase_Core::getUser()->contact_id; $containerGrants = Tinebase_Container::getInstance()->getContainerGrantsOfRecords($events, Tinebase_Core::getUser()); $resolvedAttendees = Calendar_Model_Attender::getResolvedAttendees($events->attendee, true); $toRemove = array(); $inheritableGrants = array(Tinebase_Model_Grants::GRANT_FREEBUSY, Tinebase_Model_Grants::GRANT_READ, Tinebase_Model_Grants::GRANT_SYNC, Tinebase_Model_Grants::GRANT_EXPORT, Tinebase_Model_Grants::GRANT_PRIVATE); if ($grantsFilter instanceof Calendar_Model_GrantFilter) { $requiredGrants = $grantsFilter->getRequiredGrants(); if (is_array($requiredGrants)) { $requiredGrants = array_intersect($requiredGrants, $this->_recordBasedGrants); } else { // TODO throw exception here? if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) { Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Required grants not set in grants filter: ' . print_r($grantsFilter->toArray(), true)); } } } foreach ($events as $event) { $containerId = $event->container_id instanceof Tinebase_Model_Container ? $event->container_id->getId() : $event->container_id; // either current user is organizer or has admin right on container if ($event->organizer === $currentContact || isset($containerGrants[$containerId]) && $containerGrants[$containerId]->account_grants[Tinebase_Model_Grants::GRANT_ADMIN]) { foreach ($this->_recordBasedGrants as $grant) { $event->{$grant} = true; } // has all rights => no need to filter continue; } // grants to original container if (isset($containerGrants[$containerId])) { foreach ($this->_recordBasedGrants as $grant) { $event->{$grant} = $containerGrants[$containerId]->account_grants[$grant]; } } // check grant inheritance if ($event->attendee instanceof Tinebase_Record_RecordSet) { foreach ($inheritableGrants as $grant) { if (!$event->{$grant}) { foreach ($event->attendee as $attendee) { $attendee = $resolvedAttendees->getById($attendee->getId()); if (!$attendee) { continue; } if ($attendee->displaycontainer_id instanceof Tinebase_Model_Container && $attendee->displaycontainer_id->account_grants && ($attendee->displaycontainer_id->account_grants[$grant] || $attendee->displaycontainer_id->account_grants[Tinebase_Model_Grants::GRANT_ADMIN])) { $event->{$grant} = true; break; } } } } } // check if one of the grants is set ... if (isset($requiredGrants) && is_array($requiredGrants)) { foreach ($requiredGrants as $requiredGrant) { if ($event->{$requiredGrant}) { continue 2; } } // ... otherwise mark for removal $toRemove[] = $event; } } // remove records with non matching grants foreach ($toRemove as $event) { $events->removeRecord($event); } }
/** * assert current user as attender * * @param Calendar_Model_Event $exdate */ protected function _assertCurrentUserAttender($event) { $this->assertTrue($event->attendee instanceof Tinebase_Record_RecordSet, 'attendee is no recordset: ' . print_r($event->toArray(), true)); $this->assertEquals(2, $event->attendee->count(), 'exdate should have 2 attendee: ' . print_r($event->toArray(), true)); $currentUser = Calendar_Model_Attender::getAttendee($event->attendee, new Calendar_Model_Attender(array('user_id' => Tinebase_Core::getUser()->contact_id, 'user_type' => Calendar_Model_Attender::USERTYPE_USER))); $this->assertNotNull($currentUser, 'currentUser not found in attendee'); $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $currentUser->status, print_r($currentUser->toArray(), true)); }
public function testMissingEmailAttendee() { $noMailContact = Addressbook_Controller_Contact::getInstance()->create(new Addressbook_Model_Contact(array('org_name' => 'nomail'))); $noMailAttendee = new Calendar_Model_Attender(array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $noMailContact->getId(), 'organizer' => Tinebase_Core::getUser()->contact_id)); $event = $this->_getEvent(); $event->attendee->addRecord($noMailAttendee); $persistentEvent = Calendar_Controller_Event::getInstance()->create($event); $loadedEvent = $this->_uit->get($persistentEvent->getId()); $this->assertTrue((bool) Calendar_Model_Attender::getAttendee($persistentEvent->attendee, $noMailAttendee)); $this->assertFalse((bool) Calendar_Model_Attender::getAttendee($loadedEvent->attendee, $noMailAttendee)); $loadedEvent->summary = 'update'; $update = $this->_uit->update($loadedEvent); $loadedEvent = Calendar_Controller_Event::getInstance()->get($persistentEvent->getId()); $this->assertEquals('update', $loadedEvent->summary); $this->assertTrue((bool) Calendar_Model_Attender::getAttendee($loadedEvent->attendee, $noMailAttendee)); }
protected function _getEventAttendee($_egwEventData) { $tineAttendee = new Tinebase_Record_RecordSet('Calendar_Model_Attender'); foreach ($_egwEventData['attendee'] as $idx => $egwAttender) { try { $tineAttenderArray = array('quantity' => $egwAttender['cal_quantity'], 'role' => Calendar_Model_Attender::ROLE_REQUIRED, 'status' => isset($this->_attenderStatusMap[$egwAttender['cal_status']]) || array_key_exists($egwAttender['cal_status'], $this->_attenderStatusMap) ? $this->_attenderStatusMap[$egwAttender['cal_status']] : Calendar_Model_Attender::STATUS_NEEDSACTION, 'status_authkey' => Calendar_Model_Attender::generateUID()); switch ($egwAttender['cal_user_type']) { case 'u': // user and group if ($egwAttender['cal_user_id'] > 0) { $tineAttenderArray['user_type'] = Calendar_Model_Attender::USERTYPE_USER; $tineAttenderArray['user_id'] = Tinebase_User::getInstance()->getUserById($this->mapAccountIdEgw2Tine($egwAttender['cal_user_id']))->contact_id; $tineAttenderArray['displaycontainer_id'] = $this->getPersonalContainer($this->mapAccountIdEgw2Tine($egwAttender['cal_user_id']))->getId(); } else { $tineAttenderArray['user_type'] = Calendar_Model_Attender::USERTYPE_GROUP; $tineAttenderArray['user_id'] = $this->mapAccountIdEgw2Tine($egwAttender['cal_user_id']); } break; case 'c': // try to find contact in tine (NOTE: id is useless, as contacts get new ids during migration) $contact_id = $this->_getContactIdByEmail($egwAttender['email'], $this->mapAccountIdEgw2Tine($_egwEventData['cal_owner'])); if (!$contact_id) { continue 2; } $tineAttenderArray['user_type'] = Calendar_Model_Attender::USERTYPE_USER; $tineAttenderArray['user_id'] = $contact_id; break; case 'r': $resource_id = $this->_getResourceId($egwAttender['cal_user_id']); if (!$resource_id) { continue 2; } $tineAttenderArray['user_type'] = Calendar_Model_Attender::USERTYPE_RESOURCE; $tineAttenderArray['user_id'] = $resource_id; break; default: throw new Exception("unsupported attender type: {$egwAttender['cal_user_type']}"); break; } $tineAttendee->addRecord(new Calendar_Model_Attender($tineAttenderArray)); } catch (Exception $e) { $this->_log->warn(__METHOD__ . '::' . __LINE__ . ' skipping attender for event "' . $_egwEventData['cal_id'] . '"cause: ' . $e->getMessage()); // skip attender } } // resolve groupmembers Calendar_Model_Attender::resolveGroupMembers($tineAttendee); $groupMembers = $tineAttendee->filter('user_type', Calendar_Model_Attender::USERTYPE_GROUPMEMBER); foreach ($groupMembers as $groupMember) { $groupMember->status_authkey = Calendar_Model_Attender::generateUID(); $contact = Addressbook_Controller_Contact::getInstance()->get($groupMember->user_id); $groupMember->displaycontainer_id = $this->getPersonalContainer($contact->account_id)->getId(); } return $tineAttendee; }
/** * checks event for given grant * * @param string $_grant * @return bool */ public function hasGrant($_grant) { $hasGrant = (isset($this->_properties[$_grant]) || array_key_exists($_grant, $this->_properties)) && (bool) $this->{$_grant}; if ($this->class !== Calendar_Model_Event::CLASS_PUBLIC) { $hasGrant &= $this->{Tinebase_Model_Grants::GRANT_PRIVATE} || Tinebase_Core::getUser()->contact_id == ($this->organizer instanceof Addressbook_Model_Contact ? $this->organizer->getId() : $this->organizer) || Calendar_Model_Attender::getOwnAttender($this->attendee); } return $hasGrant; }
public function eventToIcal($_event) { if ($_event instanceof Tinebase_Record_RecordSet) { foreach ($_event as $event) { $this->eventToIcal($event); } return $this->_vcalendar; } // NOTE: we deliver events in originators tz $_event->setTimezone($_event->originator_tz); if (!in_array($_event->originator_tz, $this->_attachedTimezones)) { $this->_vcalendar->attach(self::getVtimezone($_event->originator_tz)); $this->_attachedTimezones[] = $_event->originator_tz; } if ($_event->is_all_day_event) { $dtstart = new qCal_Property_Dtstart($_event->dtstart->format('Ymd'), array('VALUE' => 'DATE')); $dtend = new qCal_Property_Dtend($_event->dtend->format('Ymd'), array('VALUE' => 'DATE')); } else { $dtstart = new qCal_Property_Dtstart(qCal_DateTime::factory($_event->dtstart->format('Ymd\\THis'), $_event->originator_tz), array('TZID' => $_event->originator_tz)); $dtend = new qCal_Property_Dtend(qCal_DateTime::factory($_event->dtend->format('Ymd\\THis'), $_event->originator_tz), array('TZID' => $_event->originator_tz)); } $vevent = new qCal_Component_Vevent(array('uid' => $_event->uid, 'sequence' => $_event->seq, 'summary' => $_event->summary, 'dtstart' => $dtstart, 'dtend' => $dtend)); foreach (self::$veventMap as $icalProp => $tineField) { if (isset($_event[$tineField])) { $vevent->addProperty($icalProp, $_event->{$tineField}); } } // rrule if ($_event->rrule) { $vevent->addProperty('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 ($exdateArray = $_event->exdate) { // use multiple EXDATE for the moment, as apple ical uses them foreach ($_event->exdate as $exdate) { $exdates = new qCal_Property_Exdate(qCal_DateTime::factory($exdate->format('Ymd\\THis'), $_event->originator_tz), array('TZID' => $_event->originator_tz)); $vevent->addProperty($exdates); } // $exdates = new qCal_Property_Exdate(qCal_DateTime::factory(array_shift($exdateArray)->format('Ymd\THis'), $_event->originator_tz), array('TZID' => $_event->originator_tz)); // foreach($exdateArray as $exdate) { // $exdates->addValue(qCal_DateTime::factory($exdate->format('Ymd\THis'), $_event->originator_tz)); // } // // $vevent->addProperty($exdates); } } // recurid if ($_event->isRecurException()) { $originalDtStart = $_event->getOriginalDtStart(); $originalDtStart->setTimezone($_event->originator_tz); $vevent->addProperty(new qCal_Property_RecurrenceId(qCal_DateTime::factory($originalDtStart->format('Ymd\\THis'), $_event->originator_tz), array('TZID' => $_event->originator_tz))); } // organizer $organizerId = $_event->organizer instanceof Addressbook_Model_Contact ? array($_event->organizer->getId()) : array($_event->organizer); $organizer = Addressbook_Controller_Contact::getInstance()->getMultiple($organizerId, TRUE)->getFirstRecord(); if ($organizer && ($organizerEmail = $organizer->getPreferedEmailAddress())) { $vevent->addProperty(new qCal_Property_Organizer("mailto:{$organizerEmail}", array('CN' => $organizer->n_fileas))); } // attendee if ($_event->attendee) { Calendar_Model_Attender::resolveAttendee($_event->attendee, FALSE); foreach ($_event->attendee as $attender) { $attenderEmail = $attender->getEmail(); if ($attenderEmail) { $vevent->addProperty(new qCal_Property_Attendee("mailto:{$attenderEmail}", array('CN' => $attender->getName(), 'CUTYPE' => self::$cutypeMap[$attender->user_type], 'EMAIL' => $attenderEmail, 'PARTSTAT' => $attender->status, 'ROLE' => "{$attender->role}-PARTICIPANT", 'RSVP' => 'FALSE'))); } } } // alarms if ($_event->alarms) { foreach ($_event->alarms as $alarm) { $valarm = new qCal_Component_Valarm(array('ACTION' => 'DISPLAY', 'DESCRIPTION' => $_event->summary)); // qCal only support DURATION ;-( $diffSeconds = $_event->dtstart->php52compat_diff($alarm->alarm_time); $valarm->addProperty(new qCal_Property_Trigger($diffSeconds)); // if (is_numeric($alarm->minutes_before)) { // $valarm->addProperty(new qCal_Property_Trigger("-PT{$alarm->minutes_before}M")); // } else { // $valarm->addProperty(new qCal_Property_Trigger(qCal_DateTime::factory($alarm->alarm_time->format('Ymd\THis'), $_event->originator_tz)), array('TZID' => $_event->originator_tz)); // } $vevent->attach($valarm); } } // @todo status $this->_vcalendar->attach($vevent); return $this->_vcalendar; }
/** * process request * * @param Calendar_Model_iMIP $_iMIP * @param string $_status * @throws Tinebase_Exception_NotImplemented * * @todo handle external organizers * @todo create event in the organizers context */ protected function _processRequest($_iMIP, $_status) { $existingEvent = $_iMIP->getExistingEvent(); $ownAttender = Calendar_Model_Attender::getOwnAttender($existingEvent ? $existingEvent->attendee : $_iMIP->getEvent()->attendee); $organizer = $existingEvent ? $existingEvent->resolveOrganizer() : $_iMIP->getEvent()->resolveOrganizer(); // internal organizer: // - event is up to date // - status change could also be done by calendar method // - normal notifications if ($organizer->account_id) { if (!$existingEvent) { // organizer has an account but no event exists, it seems that event was created from a non-caldav client // do not send notifications in this case + create event in context of organizer return; // not clear how to create in the organizers context... $sendNotifications = Calendar_Controller_Event::getInstance()->sendNotifications(FALSE); $existingEvent = Calendar_Controller_MSEventFacade::getInstance()->create($_iMIP->getEvent()); Calendar_Controller_Event::getInstance()->sendNotifications($sendNotifications); } if ($_status && $_status != $ownAttender->status) { $ownAttender->status = $_status; Calendar_Controller_Event::getInstance()->attenderStatusUpdate($existingEvent, $ownAttender, $ownAttender->status_authkey); } } else { if ($ownAttender && $_status) { $ownAttender->status = $_status; } if (!$existingEvent) { $event = $_iMIP->getEvent(); if (!$event->container_id) { $event->container_id = Tinebase_Core::getPreference('Calendar')->{Calendar_Preference::DEFAULTCALENDAR}; } $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->create($event); } else { $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->update($existingEvent); } // - send reply to organizer } }
/** * send notification to a single attender * * @param Calendar_Model_Attender $_attender * @param Calendar_Model_Event $_event * @param Tinebase_Model_FullAccount $_updater * @param Sting $_action * @param String $_notificationLevel * @param array $_updates * @param array $attachs * @return void */ public function sendNotificationToAttender($_attender, $_event, $_updater, $_action, $_notificationLevel, $_updates = NULL, $attachs = FALSE) { try { // find organizer account if ($_event->organizer && $_event->resolveOrganizer()->account_id) { $organizer = Tinebase_User::getInstance()->getFullUserById($_event->resolveOrganizer()->account_id); } else { // use creator as organizer $organizer = Tinebase_User::getInstance()->getFullUserById($_event->created_by); } // get prefered language, timezone and notification level $prefUser = $_attender->getUserAccountId(); $locale = Tinebase_Translation::getLocale(Tinebase_Core::getPreference()->getValueForUser(Tinebase_Preference::LOCALE, $prefUser ? $prefUser : $organizer->getId())); $timezone = Tinebase_Core::getPreference()->getValueForUser(Tinebase_Preference::TIMEZONE, $prefUser ? $prefUser : $organizer->getId()); $translate = Tinebase_Translation::getTranslation('Calendar', $locale); // check if user wants this notification $sendLevel = $prefUser ? Tinebase_Core::getPreference('Calendar')->getValueForUser(Calendar_Preference::NOTIFICATION_LEVEL, $prefUser) : 100; $sendOnOwnActions = $prefUser ? Tinebase_Core::getPreference('Calendar')->getValueForUser(Calendar_Preference::SEND_NOTIFICATION_OF_OWN_ACTIONS, $prefUser) : 0; // NOTE: organizer gets mails unless she set notificationlevel to NONE if ($prefUser == $_updater->getId() && !$sendOnOwnActions || $sendLevel < $_notificationLevel && ($prefUser != $organizer->getId() || $sendLevel == self::NOTIFICATION_LEVEL_NONE)) { return; } // get date strings $startDateString = Tinebase_Translation::dateToStringInTzAndLocaleFormat($_event->dtstart, $timezone, $locale); $endDateString = Tinebase_Translation::dateToStringInTzAndLocaleFormat($_event->dtend, $timezone, $locale); switch ($_action) { case 'alarm': $messageSubject = sprintf($translate->_('Alarm for event "%1$s" at %2$s'), $_event->summary, $startDateString); break; case 'created': $messageSubject = sprintf($translate->_('Event invitation "%1$s" at %2$s'), $_event->summary, $startDateString); $method = Calendar_Model_iMIP::METHOD_REQUEST; break; case 'deleted': $messageSubject = sprintf($translate->_('Event "%1$s" at %2$s has been canceled'), $_event->summary, $startDateString); $method = Calendar_Model_iMIP::METHOD_CANCEL; break; case 'changed': switch ($_notificationLevel) { case self::NOTIFICATION_LEVEL_EVENT_RESCHEDULE: $messageSubject = sprintf($translate->_('Event "%1$s" at %2$s has been rescheduled'), $_event->summary, $startDateString); $method = Calendar_Model_iMIP::METHOD_REQUEST; break; case self::NOTIFICATION_LEVEL_EVENT_UPDATE: $messageSubject = sprintf($translate->_('Event "%1$s" at %2$s has been updated'), $_event->summary, $startDateString); $method = Calendar_Model_iMIP::METHOD_REQUEST; break; case self::NOTIFICATION_LEVEL_ATTENDEE_STATUS_UPDATE: if (!empty($_updates['attendee']) && !empty($_updates['attendee']['toUpdate']) && count($_updates['attendee']['toUpdate']) == 1) { // single attendee status update $attender = $_updates['attendee']['toUpdate'][0]; switch ($attender->status) { case Calendar_Model_Attender::STATUS_ACCEPTED: $messageSubject = sprintf($translate->_('%1$s accepted event "%2$s" at %3$s'), $attender->getName(), $_event->summary, $startDateString); break; case Calendar_Model_Attender::STATUS_DECLINED: $messageSubject = sprintf($translate->_('%1$s declined event "%2$s" at %3$s'), $attender->getName(), $_event->summary, $startDateString); break; case Calendar_Model_Attender::STATUS_TENTATIVE: $messageSubject = sprintf($translate->_('Tentative response from %1$s for event "%2$s" at %3$s'), $attender->getName(), $_event->summary, $startDateString); break; case Calendar_Model_Attender::STATUS_NEEDSACTION: $messageSubject = sprintf($translate->_('No response from %1$s for event "%2$s" at %3$s'), $attender->getName(), $_event->summary, $startDateString); break; } } else { $messageSubject = sprintf($translate->_('Attendee changes for event "%1$s" at %2$s'), $_event->summary, $startDateString); } // we don't send iMIP parts to organizers with an account cause event is already up to date if ($_event->organizer && !$_event->resolveOrganizer()->account_id) { $method = Calendar_Model_iMIP::METHOD_REPLY; } break; } break; default: if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " unknown action '{$_action}'"); } break; } $view = new Zend_View(); $view->setScriptPath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'views'); $view->translate = $translate; $view->timezone = $timezone; $view->event = $_event; $view->updater = $_updater; $view->updates = $_updates; $messageBody = $view->render('eventNotification.php'); if (isset($method) && version_compare(PHP_VERSION, '5.3.0', '>=')) { $converter = Calendar_Convert_Event_VCalendar_Factory::factory(Calendar_Convert_Event_VCalendar_Factory::CLIENT_GENERIC); $converter->setMethod($method); $vcalendar = $converter->fromTine20Model($_event); // in Tine 2.0 non organizers might be given the grant to update events // @see rfc6047 section 2.2.1 & rfc5545 section 3.2.18 if ($method != Calendar_Model_iMIP::METHOD_REPLY && $_event->organizer !== $_updater->contact_id) { foreach ($vcalendar->children() as $component) { if ($component->name == 'VEVENT') { if (isset($component->{'ORGANIZER'})) { $component->{'ORGANIZER'}->add(new Sabre_VObject_Parameter('SEND-BY', 'mailto:' . $_updater->accountEmailAddress)); } } } } /* not yet supported // in Tine 2.0 status updater might not be updater if ($method == Calendar_Model_iMIP::METHOD_REPLY) { } */ $calendarPart = new Zend_Mime_Part($vcalendar->serialize()); $calendarPart->charset = 'UTF-8'; $calendarPart->type = 'text/calendar; method=' . $method; $calendarPart->encoding = Zend_Mime::ENCODING_QUOTEDPRINTABLE; $attachment = new Zend_Mime_Part($vcalendar->serialize()); $attachment->type = 'application/ics'; $attachment->encoding = Zend_Mime::ENCODING_QUOTEDPRINTABLE; $attachment->disposition = Zend_Mime::DISPOSITION_ATTACHMENT; $attachment->filename = 'event.ics'; $attachments = array($attachment); if ($attachs) { foreach ($attachs as $file) { $stream = fopen($file['tempFile']['path'], 'r'); $part = new Zend_Mime_Part($stream); $part->type = $file['tempFile']['type']; $part->encoding = Zend_Mime::ENCODING_BASE64; $part->disposition = Zend_Mime::DISPOSITION_ATTACHMENT; $part->filename = $file['tempFile']['name']; $attachments[] = $part; } } } else { $calendarPart = null; $attachments = null; } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " receiver: '{$_attender->getEmail()}'"); } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " subject: '{$messageSubject}'"); } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " body: {$messageBody}"); } // NOTE: this is a contact as we only support users and groupmembers $contact = $_attender->getResolvedUser(); $sender = $_action == 'alarm' ? $organizer : $_updater; Tinebase_Notification::getInstance()->send($sender, array($contact), $messageSubject, $messageBody, $calendarPart, $attachments); } catch (Exception $e) { Tinebase_Core::getLogger()->WARN(__METHOD__ . '::' . __LINE__ . " could not send notification :" . $e); return; } }
/** * process request * * @param Calendar_Model_iMIP $_iMIP * @param string $_status */ protected function _processRequest($_iMIP, $_status) { $existingEvent = $this->getExistingEvent($_iMIP); $ownAttender = Calendar_Model_Attender::getOwnAttender($existingEvent ? $existingEvent->attendee : $_iMIP->getEvent()->attendee); $organizer = $existingEvent ? $existingEvent->resolveOrganizer() : $_iMIP->getEvent()->resolveOrganizer(); // internal organizer: // - event is up to date // - status change could also be done by calendar method // - normal notifications if ($organizer->account_id) { if (!$existingEvent) { // organizer has an account but no event exists, it seems that event was created from a non-caldav client // do not send notifications in this case + create event in context of organizer if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Organizer has an account but no event exists!'); } return; // not clear how to create in the organizers context... $sendNotifications = Calendar_Controller_Event::getInstance()->sendNotifications(FALSE); $existingEvent = Calendar_Controller_MSEventFacade::getInstance()->create($_iMIP->getEvent()); Calendar_Controller_Event::getInstance()->sendNotifications($sendNotifications); } if ($_status && $_status != $ownAttender->status) { $ownAttender->status = $_status; Calendar_Controller_Event::getInstance()->attenderStatusUpdate($existingEvent, $ownAttender, $ownAttender->status_authkey); } } else { $sendNotifications = Calendar_Controller_Event::getInstance()->sendNotifications(false); $event = $_iMIP->getEvent(); if (!$existingEvent) { if (!$event->container_id) { $event->container_id = Tinebase_Core::getPreference('Calendar')->{Calendar_Preference::DEFAULTCALENDAR}; } $event = $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->create($event); } else { if ($event->external_seq > $existingEvent->external_seq && !$_status) { // no buttons pressed (just reading/updating) // updates event with .ics $event->id = $existingEvent->id; $event = $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->update($event); } else { $event = $_iMIP->event = Calendar_Controller_MSEventFacade::getInstance()->update($existingEvent); } } Calendar_Controller_Event::getInstance()->sendNotifications($sendNotifications); $ownAttender = Calendar_Model_Attender::getOwnAttender($event->attendee); // NOTE: we do the status update in a separate call to trigger the right notifications if ($ownAttender && $_status) { $ownAttender->status = $_status; $a = Calendar_Controller_Event::getInstance()->attenderStatusUpdate($event, $ownAttender, $ownAttender->status_authkey); } } }
/** * send notification to a single attender * * @param Calendar_Model_Attender $_attender * @param Calendar_Model_Event $_event * @param Tinebase_Model_FullAccount $_updater * @param string $_action * @param string $_notificationLevel * @param array $_updates * @return void * * TODO this needs major refactoring */ public function sendNotificationToAttender(Calendar_Model_Attender $_attender, $_event, $_updater, $_action, $_notificationLevel, $_updates = NULL) { try { $organizer = $_event->resolveOrganizer(); $organizerAccountId = $organizer->account_id; $attendee = $_attender->getResolvedUser(); if ($attendee instanceof Addressbook_Model_List) { // don't send notification to lists as we already resolved the list members for individual mails if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " Skip notification for list " . $attendee->name); } return; } $attendeeAccountId = $_attender->getUserAccountId(); $prefUserId = $attendeeAccountId ? $attendeeAccountId : ($organizerAccountId ? $organizerAccountId : $_event->created_by); try { $prefUser = Tinebase_User::getInstance()->getFullUserById($prefUserId); } catch (Exception $e) { $prefUser = Tinebase_Core::getUser(); $prefUserId = $prefUser->getId(); } // get prefered language, timezone and notification level $locale = Tinebase_Translation::getLocale(Tinebase_Core::getPreference()->getValueForUser(Tinebase_Preference::LOCALE, $prefUserId)); $timezone = Tinebase_Core::getPreference()->getValueForUser(Tinebase_Preference::TIMEZONE, $prefUserId); $translate = Tinebase_Translation::getTranslation('Calendar', $locale); $sendLevel = Tinebase_Core::getPreference('Calendar')->getValueForUser(Calendar_Preference::NOTIFICATION_LEVEL, $prefUserId); $sendOnOwnActions = Tinebase_Core::getPreference('Calendar')->getValueForUser(Calendar_Preference::SEND_NOTIFICATION_OF_OWN_ACTIONS, $prefUserId); $sendAlarms = Tinebase_Core::getPreference('Calendar')->getValueForUser(Calendar_Preference::SEND_ALARM_NOTIFICATIONS, $prefUserId); // external (non account) notification if (!$attendeeAccountId) { // external organizer needs status updates $sendLevel = is_object($organizer) && $_attender->getEmail() == $organizer->getPreferedEmailAddress() ? 40 : 30; $sendOnOwnActions = false; $sendAlarms = false; } $recipients = array($attendee); $this->_handleResourceEditors($_attender, $_notificationLevel, $recipients, $_action, $sendLevel, $_updates); // check if user wants this notification NOTE: organizer gets mails unless she set notificationlevel to NONE // NOTE prefUser is organizer for external notifications if ($attendeeAccountId == $_updater->getId() && !$sendOnOwnActions || $sendLevel < $_notificationLevel && (is_object($organizer) && method_exists($attendee, 'getPreferedEmailAddress') && $attendee->getPreferedEmailAddress() != $organizer->getPreferedEmailAddress() || is_object($organizer) && !method_exists($attendee, 'getPreferedEmailAddress') && $attendee->email != $organizer->getPreferedEmailAddress() || $sendLevel == self::NOTIFICATION_LEVEL_NONE)) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " Preferred notification level not reached -> skipping notification for {$_attender->getEmail()}"); } return; } if ($_action == 'alarm' && !$sendAlarms) { if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " User does not want alarm mails -> skipping notification for {$_attender->getEmail()}"); } return; } $method = NULL; // NOTE $method gets set in _getSubject as referenced param $messageSubject = $this->_getSubject($_event, $_notificationLevel, $_action, $_updates, $timezone, $locale, $translate, $method, $_attender); // we don't send iMIP parts to external attendee if config is active if (Calendar_Config::getInstance()->get(Calendar_Config::DISABLE_EXTERNAL_IMIP) && !$attendeeAccountId) { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " External iMIP is disabled."); } $method = NULL; } $view = new Zend_View(); $view->setScriptPath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'views'); $view->translate = $translate; $view->timezone = $timezone; $view->event = $_event; $view->updater = $_updater; $view->updates = $_updates; $messageBody = $view->render('eventNotification.php'); $calendarPart = null; $attachments = $this->_getAttachments($method, $_event, $_action, $_updater, $calendarPart); if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " receiver: '{$_attender->getEmail()}'"); } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " subject: '{$messageSubject}'"); } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " body: {$messageBody}"); } $sender = $_action == 'alarm' ? $prefUser : $_updater; Tinebase_Notification::getInstance()->send($sender, $recipients, $messageSubject, $messageBody, $calendarPart, $attachments); } catch (Exception $e) { Tinebase_Exception::log($e); return; } }
/** * converts Tinebase_Record_RecordSet to external format * * @param Tinebase_Record_RecordSet $_records * @param Tinebase_Model_Filter_FilterGroup $_filter * @param Tinebase_Model_Pagination $_pagination * * @return mixed */ public function fromTine20RecordSet(Tinebase_Record_RecordSet $_records = NULL, $_filter = NULL, $_pagination = NULL) { if (count($_records) == 0) { return array(); } Tinebase_Notes::getInstance()->getMultipleNotesOfRecords($_records); Tinebase_Tags::getInstance()->getMultipleTagsOfRecords($_records); if (Tinebase_Core::isFilesystemAvailable()) { Tinebase_FileSystem_RecordAttachments::getInstance()->getMultipleAttachmentsOfRecords($_records); } Calendar_Model_Attender::resolveAttendee($_records->attendee, TRUE, $_records); Calendar_Convert_Event_Json::resolveRrule($_records); Calendar_Controller_Event::getInstance()->getAlarms($_records); Calendar_Convert_Event_Json::resolveGrantsOfExternalOrganizers($_records); Calendar_Model_Rrule::mergeAndRemoveNonMatchingRecurrences($_records, $_filter); $_records->sortByPagination($_pagination); Tinebase_Frontend_Json_Abstract::resolveContainersAndTags($_records, array('container_id')); $_records->setTimezone(Tinebase_Core::getUserTimezone()); $_records->convertDates = true; $eventsData = $_records->toArray(); foreach ($eventsData as $idx => $eventData) { if (!(isset($eventData[Tinebase_Model_Grants::GRANT_READ]) || array_key_exists(Tinebase_Model_Grants::GRANT_READ, $eventData)) || !$eventData[Tinebase_Model_Grants::GRANT_READ]) { $eventsData[$idx] = array_intersect_key($eventsData[$idx], array_flip(array('id', 'dtstart', 'dtend', 'transp', 'is_all_day_event'))); } } return $eventsData; }
/** * returns multiple records prepared for json transport * * @param Tinebase_Record_RecordSet $_records Tinebase_Record_Abstract * @param Tinebase_Model_Filter_FilterGroup $_filter * @param Tinebase_Model_Pagination $_pagination needed for sorting * @return array data * * @todo perhaps we need to resolveContainerTagsUsers() before mergeAndRemoveNonMatchingRecurrences(), but i'm not sure about that * @todo use Calendar_Convert_Event_Json */ protected function _multipleRecordsToJson(Tinebase_Record_RecordSet $_records, $_filter = NULL, $_pagination = NULL) { if ($_records->getRecordClassName() == 'Calendar_Model_Event') { if (is_null($_filter)) { throw new Tinebase_Exception_InvalidArgument('Required argument $_filter is missing'); } Tinebase_Notes::getInstance()->getMultipleNotesOfRecords($_records); Calendar_Model_Attender::resolveAttendee($_records->attendee, TRUE, $_records); Calendar_Convert_Event_Json::resolveOrganizer($_records); Calendar_Convert_Event_Json::resolveRrule($_records); Calendar_Controller_Event::getInstance()->getAlarms($_records); Calendar_Model_Rrule::mergeAndRemoveNonMatchingRecurrences($_records, $_filter); $_records->sortByPagination($_pagination); $eventsData = parent::_multipleRecordsToJson($_records); foreach ($eventsData as $eventData) { if (!array_key_exists(Tinebase_Model_Grants::GRANT_READ, $eventData) || !$eventData[Tinebase_Model_Grants::GRANT_READ]) { $eventData['notes'] = array(); $eventData['tags'] = array(); } } return $eventsData; } return parent::_multipleRecordsToJson($_records); }
/** * converts an iTIP event to a tine20 event * * @param Calendar_Model_Event $_event * @param Calendar_Model_Event $_currentEvent (not iTIP!) */ protected function _fromiTIP($_event, $_currentEvent) { if (!$_event->rrule) { $_event->exdate = NULL; } if ($_event->exdate instanceof Tinebase_Record_RecordSet) { try { $currExdates = $this->_eventController->getRecurExceptions($_event, TRUE); $this->getAlarms($currExdates); $currClientExdates = $this->_eventController->getRecurExceptions($_event, TRUE, $this->getEventFilter()); $this->getAlarms($currClientExdates); } catch (Tinebase_Exception_NotFound $e) { $currExdates = NULL; $currClientExdates = NULL; } foreach ($_event->exdate as $idx => $exdate) { try { $this->_prepareException($_event, $exdate); } catch (Exception $e) { } $currExdate = $currExdates instanceof Tinebase_Record_RecordSet ? $currExdates->filter('recurid', $exdate->recurid)->getFirstRecord() : NULL; if ($exdate->is_deleted) { // reset implicit filter fallouts and mark as don't touch (seq = -1) $currClientExdate = $currClientExdates instanceof Tinebase_Record_RecordSet ? $currClientExdates->filter('recurid', $exdate->recurid)->getFirstRecord() : NULL; if ($currClientExdate && $currClientExdate->is_deleted) { $_event->exdate[$idx] = $currExdate; $currExdate->seq = -1; continue; } } $this->_fromiTIP($exdate, $currExdate ? $currExdate : clone $_currentEvent); } } // assert organizer $_event->organizer = $_event->organizer ?: ($_currentEvent->organizer ?: $this->_calendarUser->user_id); $this->_addAttendeeWithoutEmail($_event, $_currentEvent); $CUAttendee = Calendar_Model_Attender::getAttendee($_event->attendee, $this->_calendarUser); $currentCUAttendee = Calendar_Model_Attender::getAttendee($_currentEvent->attendee, $this->_calendarUser); $isOrganizer = $_event->isOrganizer($this->_calendarUser); // remove perspective if ($CUAttendee && !$isOrganizer) { $CUAttendee->transp = $_event->transp; $_event->transp = $_currentEvent->transp ? $_currentEvent->transp : $_event->transp; } // apply changes to original alarms $_currentEvent->alarms = $_currentEvent->alarms instanceof Tinebase_Record_RecordSet ? $_currentEvent->alarms : new Tinebase_Record_RecordSet('Tinebase_Model_Alarm'); $_event->alarms = $_event->alarms instanceof Tinebase_Record_RecordSet ? $_event->alarms : new Tinebase_Record_RecordSet('Tinebase_Model_Alarm'); foreach ($_currentEvent->alarms as $currentAlarm) { if (Calendar_Model_Attender::isAlarmForAttendee($this->_calendarUser, $currentAlarm)) { $alarmUpdate = Calendar_Controller_Alarm::getMatchingAlarm($_event->alarms, $currentAlarm); if ($alarmUpdate) { // we could map the alarm => save ack & snooze options if ($dtAck = Calendar_Controller_Alarm::getAcknowledgeTime($alarmUpdate)) { Calendar_Controller_Alarm::setAcknowledgeTime($currentAlarm, $dtAck, $this->getCalendarUser()->user_id); } if ($dtSnooze = Calendar_Controller_Alarm::getSnoozeTime($alarmUpdate)) { Calendar_Controller_Alarm::setSnoozeTime($currentAlarm, $dtSnooze, $this->getCalendarUser()->user_id); } $_event->alarms->removeRecord($alarmUpdate); } else { // alarm is to be skiped/deleted if (!$currentAlarm->getOption('attendee')) { Calendar_Controller_Alarm::skipAlarm($currentAlarm, $this->_calendarUser); } else { $_currentEvent->alarms->removeRecord($currentAlarm); } } } } if (!$isOrganizer) { $_event->alarms->setOption('attendee', Calendar_Controller_Alarm::attendeeToOption($this->_calendarUser)); } $_event->alarms->merge($_currentEvent->alarms); // assert organizer for personal calendars to be calendar owner if ($this->_currentEventFacadeContainer && $this->_currentEventFacadeContainer->getId() == $_event->container_id && $this->_currentEventFacadeContainer->type == Tinebase_Model_Container::TYPE_PERSONAL && !$_event->hasExternalOrganizer()) { $_event->organizer = $this->_calendarUser->user_id; } // in MS world only cal_user can do status updates if ($CUAttendee) { $CUAttendee->status_authkey = $currentCUAttendee ? $currentCUAttendee->status_authkey : NULL; } }
/** * testInvitationExternalReply */ public function testInvitationExternalReply() { $email = $email = $this->_getEmailAddress(); $ics = file_get_contents(dirname(__FILE__) . '/files/invitation_reply_external_accepted.ics'); $ics = preg_replace('/unittest@tine20\\.org/', $email, $ics); $iMIP = new Calendar_Model_iMIP(array('id' => Tinebase_Record_Abstract::generateUID(), 'ics' => $ics, 'method' => 'REPLY', 'originator' => '*****@*****.**')); $this->assertEquals(1, $iMIP->getEvent()->seq); $this->assertTrue(!empty($iMIP->getEvent()->last_modified_time)); // force creation of external attendee $externalAttendee = new Calendar_Model_Attender(array('user_type' => Calendar_Model_Attender::USERTYPE_USER, 'user_id' => $iMIP->getEvent()->attendee->getFirstRecord()->user_id, 'status' => Calendar_Model_Attender::STATUS_NEEDSACTION)); // create matching event $event = new Calendar_Model_Event(array('summary' => 'TEST7', 'dtstart' => '2011-11-30 14:00:00', 'dtend' => '2011-11-30 15:00:00', 'description' => 'Early to bed and early to rise, makes a men healthy, wealthy and wise ...', 'attendee' => $this->_getAttendee(), 'organizer' => Tinebase_Core::getUser()->contact_id, 'uid' => 'a8d10369e051094ae9322bd65e8afecac010bfc8')); $event->attendee->addRecord($externalAttendee); $event = Calendar_Controller_Event::getInstance()->create($event); $this->_eventIdsToDelete[] = $event->getId(); // TEST NORMAL REPLY try { $this->_iMIPFrontend->autoProcess($iMIP); } catch (Exception $e) { $this->fail('TEST NORMAL REPLY autoProcess throws Exception: ' . $e); } unset($iMIP->existing_event); $updatedEvent = Calendar_Controller_Event::getInstance()->get($event->getId()); $updatedExternalAttendee = Calendar_Model_Attender::getAttendee($updatedEvent->attendee, $externalAttendee); $this->assertEquals(3, count($updatedEvent->attendee)); $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $updatedExternalAttendee->status, 'status not updated'); // TEST ACCEPTABLE NON RECENT REPLY $updatedExternalAttendee->status = Calendar_Model_Attender::STATUS_NEEDSACTION; Calendar_Controller_Event::getInstance()->attenderStatusUpdate($updatedEvent, $updatedExternalAttendee, $updatedExternalAttendee->status_authkey); try { $iMIP->preconditionsChecked = false; $this->_iMIPFrontend->autoProcess($iMIP); } catch (Exception $e) { $this->fail('TEST ACCEPTABLE NON RECENT REPLY autoProcess throws Exception: ' . $e); } unset($iMIP->existing_event); $updatedEvent = Calendar_Controller_Event::getInstance()->get($event->getId()); $updatedExternalAttendee = Calendar_Model_Attender::getAttendee($updatedEvent->attendee, $externalAttendee); $this->assertEquals(3, count($updatedEvent->attendee)); $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $updatedExternalAttendee->status, 'status not updated'); // check if attendee are resolved $existingEvent = $this->_iMIPFrontend->getExistingEvent($iMIP); $this->assertTrue($iMIP->existing_event->attendee instanceof Tinebase_Record_RecordSet); $this->assertEquals(3, count($iMIP->existing_event->attendee)); // TEST NON ACCEPTABLE NON RECENT REPLY $iMIP->preconditionsChecked = false; try { $this->_iMIPFrontend->autoProcess($iMIP); $this->fail('autoProcess should throw Calendar_Exception_iMIP'); } catch (Calendar_Exception_iMIP $cei) { $this->assertContains('iMIP preconditions failed: RECENT', $cei->getMessage()); } }
/** * returns array with the filter settings of this filter * * @param bool $_valueToJson resolve value for json api? * @return array */ public function toArray($_valueToJson = false) { $result = parent::toArray($_valueToJson); if ($_valueToJson) { Calendar_Model_Attender::resolveAttendee($this->_value); } $result['value'] = $this->_operator == 'equals' ? $this->_value[0]->toArray($_valueToJson) : $this->_value->toArray($_valueToJson); return $result; }
public function testAlarmSkipDeclined() { $event = $this->_getEvent(); $event->attendee = $this->_getPersonaAttendee('sclever, pwulf'); $event->organizer = $this->_getPersonasContacts('sclever')->getId(); $event->dtstart = Tinebase_DateTime::now()->addMinute(25); $event->dtend = clone $event->dtstart; $event->dtend->addMinute(30); $event->alarms = new Tinebase_Record_RecordSet('Tinebase_Model_Alarm', array(new Tinebase_Model_Alarm(array('minutes_before' => 30), TRUE))); $persistentEvent = $this->_eventController->create($event); $sclever = Calendar_Model_Attender::getAttendee($persistentEvent->attendee, $event->attendee[0]); $sclever->status = Calendar_Model_Attender::STATUS_DECLINED; $this->_eventController->attenderStatusUpdate($persistentEvent, $sclever, $sclever->status_authkey); self::flushMailer(); Tinebase_Alarm::getInstance()->sendPendingAlarms("Tinebase_Event_Async_Minutely"); $this->_assertMail('pwulf', 'Alarm'); $this->assertEquals(1, count(self::getMessages())); }
public function testMeetingResponseWithNewInstanceId() { $syncrotonFolder = $this->testCreateFolder(); list($serverId, $event) = $this->testCreateEntry($syncrotonFolder); $controller = Syncroton_Data_Factory::factory($this->_class, $this->_getDevice(Syncroton_Model_Device::TYPE_IPHONE), new Tinebase_DateTime(null, null, 'de_DE')); $XMLMeetingResponse = $this->_testXMLMeetingResponse; $thisYear = Tinebase_DateTime::now()->format('Y'); $XMLMeetingResponse = str_replace('2012', $thisYear, $XMLMeetingResponse); $XMLMeetingResponse = str_replace('<CollectionId>17</CollectionId>', '<CollectionId>' . $syncrotonFolder->serverId . '</CollectionId>', $XMLMeetingResponse); $XMLMeetingResponse = str_replace('<RequestId>f0c79775b6b44be446f91187e24566aa1c5d06ab</RequestId>', '<RequestId>' . $serverId . '</RequestId>', $XMLMeetingResponse); $XMLMeetingResponse = str_replace('<InstanceId>20121125T130000Z</InstanceId>', '<InstanceId>20121126T130000Z</InstanceId>', $XMLMeetingResponse); $xml = new SimpleXMLElement($XMLMeetingResponse); $meetingResponse = new Syncroton_Model_MeetingResponse($xml->Request[0]); $eventId = $controller->setAttendeeStatus($meetingResponse); $event = Calendar_Controller_MSEventFacade::getInstance()->get($serverId); $event->exdate->sort('dtstart', 'DESC'); $instance = $event->exdate->filter('is_deleted', 0)->getFirstRecord(); $ownAttendee = Calendar_Model_Attender::getOwnAttender($instance->attendee); $this->assertEquals(Calendar_Model_Attender::STATUS_TENTATIVE, $ownAttendee->status); }
/** * test implicit recur (exception) series creation for attendee status only */ public function testAttendeeSetStatusRecurExceptionAllFollowing() { $from = new Tinebase_DateTime('2012-02-01 00:00:00'); $until = new Tinebase_DateTime('2012-02-29 23:59:59'); $event = new Calendar_Model_Event(array('summary' => 'Some Daily Event', 'dtstart' => '2012-02-03 09:00:00', 'dtend' => '2012-02-03 10:00:00', 'rrule' => 'FREQ=DAILY;INTERVAL=1', 'container_id' => $this->_testCalendar->getId(), 'attendee' => $this->_getAttendee())); $persistentEvent = $this->_controller->create($event); $exceptions = new Tinebase_Record_RecordSet('Calendar_Model_Event'); $recurSet = Calendar_Model_Rrule::computeRecurrenceSet($persistentEvent, $exceptions, $from, $until); // accept for sclever thisandfuture $start = $recurSet[10]; $sclever = Calendar_Model_Attender::getAttendee($start->attendee, $event->attendee[1]); $sclever->status = Calendar_Model_Attender::STATUS_ACCEPTED; $this->_controller->attenderStatusCreateRecurException($start, $sclever, $sclever->status_authkey, TRUE); $events = $this->_controller->search(new Calendar_Model_EventFilter(array(array('field' => 'container_id', 'operator' => 'equals', 'value' => $this->_testCalendar->getId()))))->sort('dtstart', 'ASC'); // assert two baseEvents $this->assertTrue($events[0]->rrule_until instanceof Tinebase_DateTime, 'rrule_until of first baseEvent is not set'); $this->assertTrue($events[0]->rrule_until < new Tinebase_DateTime('2012-02-14 09:00:00'), 'rrule_until of first baseEvent is not adopted properly'); $this->assertEquals(Calendar_Model_Attender::STATUS_NEEDSACTION, Calendar_Model_Attender::getAttendee($events[0]->attendee, $event->attendee[1])->status, 'first baseEvent status must not be touched'); $this->assertEquals($events[1]->dtstart, new Tinebase_DateTime('2012-02-14 09:00:00'), 'start of second baseEvent is wrong'); $this->assertTrue(empty($events[1]->recurid), 'second baseEvent is not a baseEvent'); $this->assertEquals($events[1]->rrule, $event->rrule, 'rrule of second baseEvent must be set'); $this->assertFalse($events[1]->rrule_until instanceof Tinebase_DateTime, 'rrule_until of second baseEvent must not be set'); $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, Calendar_Model_Attender::getAttendee($events[1]->attendee, $event->attendee[1])->status, 'second baseEvent status is not touched'); }