/** * 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; } }
/** * updates an attender * * @param Calendar_Model_Attender $attender * @param Calendar_Model_Attender $currentAttender * @param Calendar_Model_Event $event * @param bool $isRescheduled event got rescheduled reset all attendee status * @param Tinebase_Model_Container $calendar */ protected function _updateAttender($attender, $currentAttender, $event, $isRescheduled, $calendar = NULL) { $userAccountId = $currentAttender->getUserAccountId(); // update display calendar if attender has/is a useraccount if ($userAccountId) { if ($calendar->type == Tinebase_Model_Container::TYPE_PERSONAL && Tinebase_Container::getInstance()->hasGrant($userAccountId, $calendar, Tinebase_Model_Grants::GRANT_ADMIN)) { // if attender has admin grant to personal physical container, this phys. cal also gets displ. cal $attender->displaycontainer_id = $calendar->getId(); } else { if ($userAccountId == Tinebase_Core::getUser()->getId() && Tinebase_Container::getInstance()->hasGrant($userAccountId, $attender->displaycontainer_id, Tinebase_Model_Grants::GRANT_ADMIN)) { // allow user to set his own displ. cal $attender->displaycontainer_id = $attender->displaycontainer_id; } else { $attender->displaycontainer_id = $currentAttender->displaycontainer_id; } } } // reset status if user has no right and authkey is wrong if ($attender->displaycontainer_id) { if (!Tinebase_Core::getUser()->hasGrant($attender->displaycontainer_id, Tinebase_Model_Grants::GRANT_EDIT) && $attender->status_authkey != $currentAttender->status_authkey) { if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) { Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . ' Wrong authkey, resetting status (' . $attender->status . ' -> ' . $currentAttender->status . ')'); } $attender->status = $currentAttender->status; } } // reset all status but calUser on reschedule except resources (Resources might have a configured default value) if ($isRescheduled && !$attender->isSame($this->getCalendarUser())) { if ($attender->user_type === Calendar_Model_Attender::USERTYPE_RESOURCE) { //If resource has a default status reset to this $resource = Calendar_Controller_Resource::getInstance()->get($attender->user_id); $attender->status = isset($resource->status) ? $resource->status : Calendar_Model_Attender::STATUS_NEEDSACTION; } else { $attender->status = Calendar_Model_Attender::STATUS_NEEDSACTION; } $attender->transp = null; } // preserve old authkey $attender->status_authkey = $currentAttender->status_authkey; if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) { Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . " Updating attender: " . print_r($attender->toArray(), TRUE)); } Tinebase_Timemachine_ModificationLog::getInstance()->setRecordMetaData($attender, 'update', $currentAttender); Tinebase_Timemachine_ModificationLog::getInstance()->writeModLog($attender, $currentAttender, get_class($attender), $this->_getBackendType(), $attender->getId()); $this->_backend->updateAttendee($attender); if ($attender->displaycontainer_id !== $currentAttender->displaycontainer_id) { $this->_increaseDisplayContainerContentSequence($currentAttender, $event, Tinebase_Model_ContainerContent::ACTION_DELETE); $this->_increaseDisplayContainerContentSequence($attender, $event, Tinebase_Model_ContainerContent::ACTION_CREATE); } else { $this->_increaseDisplayContainerContentSequence($attender, $event); } }
/** * 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; } }
/** * updates an attender * * @param Calendar_Model_Attender $_attender * @param Calendar_Model_Attender $_currentAttender * @param Tinebase_Model_Container $_calendar * @param bool $_isRescheduled event got rescheduled reset all attendee status */ protected function _updateAttender($_attender, $_currentAttender, $_calendar, $_isRescheduled) { //echo "save: ". (int) $_isRescheduled . "\n"; $userAccountId = $_currentAttender->getUserAccountId(); // reset status if attender != currentuser and wrong authkey if ($_attender->user_type == Calendar_Model_Attender::USERTYPE_GROUP || $userAccountId != Tinebase_Core::getUser()->getId()) { if ($_isRescheduled) { $_attender->status = Calendar_Model_Attender::STATUS_NEEDSACTION; } else { if ($_attender->status_authkey != $_currentAttender->status_authkey) { Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ . " wrong authkey -> resetting status "); $_attender->status = $_currentAttender->status; } } } // reset alarm ack and snooze times if ($_isRescheduled) { $_attender->alarm_ack_time = null; $_attender->alarm_snooze_time = null; } // preserv old authkey $_attender->status_authkey = $_currentAttender->status_authkey; //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " updating attender: " . print_r($_attender->toArray(), TRUE)); // update display calendar if attender has/is a useraccount if ($userAccountId) { if ($_calendar->type == Tinebase_Model_Container::TYPE_PERSONAL && Tinebase_Container::getInstance()->hasGrant($userAccountId, $_calendar, Tinebase_Model_Grants::GRANT_ADMIN)) { // if attender has admin grant to personal physical container, this phys. cal also gets displ. cal $_attender->displaycontainer_id = $_calendar->getId(); } else { if ($userAccountId == Tinebase_Core::getUser()->getId() && Tinebase_Container::getInstance()->hasGrant($userAccountId, $_attender->displaycontainer_id, Tinebase_Model_Grants::GRANT_ADMIN)) { // allow user to set his own displ. cal $_attender->displaycontainer_id = $_attender->displaycontainer_id; } else { $_attender->displaycontainer_id = $_currentAttender->displaycontainer_id; } } } $this->_backend->updateAttendee($_attender); }