public function handleRequest(AphrontRequest $request)
 {
     $viewer = $request->getUser();
     $id = $request->getURIData('id');
     if ($id) {
         $event = id(new PhabricatorCalendarEventQuery())->setViewer($viewer)->withIDs(array($id))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->executeOne();
         if (!$event) {
             return new Aphront404Response();
         }
         $cancel_uri = $this->getApplicationURI('/E' . $event->getID());
         $event_icon = $event->getIcon();
     } else {
         $cancel_uri = '/calendar/';
         $event_icon = $request->getStr('value');
     }
     require_celerity_resource('calendar-icon-css');
     Javelin::initBehavior('phabricator-tooltips');
     $calendar_icons = PhabricatorCalendarIcon::getIconMap();
     if ($request->isFormPost()) {
         $v_icon = $request->getStr('icon');
         return id(new AphrontAjaxResponse())->setContent(array('value' => $v_icon, 'display' => PhabricatorCalendarIcon::renderIconForChooser($v_icon)));
     }
     $ii = 0;
     $buttons = array();
     foreach ($calendar_icons as $icon => $label) {
         $view = id(new PHUIIconView())->setIconFont($icon);
         $aural = javelin_tag('span', array('aural' => true), pht('Choose "%s" Icon', $label));
         if ($icon == $event_icon) {
             $class_extra = ' selected';
         } else {
             $class_extra = null;
         }
         $buttons[] = javelin_tag('button', array('class' => 'icon-button' . $class_extra, 'name' => 'icon', 'value' => $icon, 'type' => 'submit', 'sigil' => 'has-tooltip', 'meta' => array('tip' => $label)), array($aural, $view));
         if (++$ii % 4 == 0) {
             $buttons[] = phutil_tag('br');
         }
     }
     $buttons = phutil_tag('div', array('class' => 'icon-grid'), $buttons);
     return $this->newDialog()->setTitle(pht('Choose Calendar Event Icon'))->appendChild($buttons)->addCancelButton($cancel_uri);
 }
 private function buildPropertyView(PhabricatorCalendarEvent $event)
 {
     $viewer = $this->getRequest()->getUser();
     $properties = id(new PHUIPropertyListView())->setUser($viewer)->setObject($event);
     if ($event->getIsAllDay()) {
         $date_start = phabricator_date($event->getDateFrom(), $viewer);
         $date_end = phabricator_date($event->getDateTo(), $viewer);
         if ($date_start == $date_end) {
             $properties->addProperty(pht('Time'), phabricator_date($event->getDateFrom(), $viewer));
         } else {
             $properties->addProperty(pht('Starts'), phabricator_date($event->getDateFrom(), $viewer));
             $properties->addProperty(pht('Ends'), phabricator_date($event->getDateTo(), $viewer));
         }
     } else {
         $properties->addProperty(pht('Starts'), phabricator_datetime($event->getDateFrom(), $viewer));
         $properties->addProperty(pht('Ends'), phabricator_datetime($event->getDateTo(), $viewer));
     }
     if ($event->getIsRecurring()) {
         $properties->addProperty(pht('Recurs'), ucwords(idx($event->getRecurrenceFrequency(), 'rule')));
         if ($event->getRecurrenceEndDate()) {
             $properties->addProperty(pht('Recurrence Ends'), phabricator_datetime($event->getRecurrenceEndDate(), $viewer));
         }
         if ($event->getInstanceOfEventPHID()) {
             $properties->addProperty(pht('Recurrence of Event'), pht('%s of %s', $event->getSequenceIndex(), $viewer->renderHandle($event->getInstanceOfEventPHID())->render()));
         }
     }
     $properties->addProperty(pht('Host'), $viewer->renderHandle($event->getUserPHID()));
     $invitees = $event->getInvitees();
     foreach ($invitees as $key => $invitee) {
         if ($invitee->isUninvited()) {
             unset($invitees[$key]);
         }
     }
     if ($invitees) {
         $invitee_list = new PHUIStatusListView();
         $icon_invited = PHUIStatusItemView::ICON_OPEN;
         $icon_attending = PHUIStatusItemView::ICON_ACCEPT;
         $icon_declined = PHUIStatusItemView::ICON_REJECT;
         $status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED;
         $status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
         $status_declined = PhabricatorCalendarEventInvitee::STATUS_DECLINED;
         $icon_map = array($status_invited => $icon_invited, $status_attending => $icon_attending, $status_declined => $icon_declined);
         $icon_color_map = array($status_invited => null, $status_attending => 'green', $status_declined => 'red');
         foreach ($invitees as $invitee) {
             $item = new PHUIStatusItemView();
             $invitee_phid = $invitee->getInviteePHID();
             $status = $invitee->getStatus();
             $target = $viewer->renderHandle($invitee_phid);
             $icon = $icon_map[$status];
             $icon_color = $icon_color_map[$status];
             $item->setIcon($icon, $icon_color)->setTarget($target);
             $invitee_list->addItem($item);
         }
     } else {
         $invitee_list = phutil_tag('em', array(), pht('None'));
     }
     $properties->addProperty(pht('Invitees'), $invitee_list);
     $properties->invokeWillRenderEvent();
     $icon_display = PhabricatorCalendarIcon::renderIconForChooser($event->getIcon());
     $properties->addProperty(pht('Icon'), $icon_display);
     if (strlen($event->getDescription())) {
         $description = PhabricatorMarkupEngine::renderOneObject(id(new PhabricatorMarkupOneOff())->setContent($event->getDescription()), 'default', $viewer);
         $properties->addSectionHeader(pht('Description'), PHUIPropertyListView::ICON_SUMMARY);
         $properties->addTextContent($description);
     }
     return $properties;
 }
 public function handleRequest(AphrontRequest $request)
 {
     $viewer = $request->getViewer();
     $user_phid = $viewer->getPHID();
     $this->id = $request->getURIData('id');
     $error_name = true;
     $error_recurrence_end_date = null;
     $error_start_date = true;
     $error_end_date = true;
     $validation_exception = null;
     $is_recurring_id = celerity_generate_unique_node_id();
     $recurrence_end_date_id = celerity_generate_unique_node_id();
     $frequency_id = celerity_generate_unique_node_id();
     $all_day_id = celerity_generate_unique_node_id();
     $start_date_id = celerity_generate_unique_node_id();
     $end_date_id = celerity_generate_unique_node_id();
     $next_workflow = $request->getStr('next');
     $uri_query = $request->getStr('query');
     if ($this->isCreate()) {
         $mode = $request->getStr('mode');
         $event = PhabricatorCalendarEvent::initializeNewCalendarEvent($viewer, $mode);
         $create_start_year = $request->getInt('year');
         $create_start_month = $request->getInt('month');
         $create_start_day = $request->getInt('day');
         $create_start_time = $request->getStr('time');
         if ($create_start_year) {
             $start = AphrontFormDateControlValue::newFromParts($viewer, $create_start_year, $create_start_month, $create_start_day, $create_start_time);
             if (!$start->isValid()) {
                 return new Aphront400Response();
             }
             $start_value = AphrontFormDateControlValue::newFromEpoch($viewer, $start->getEpoch());
             $end = clone $start_value->getDateTime();
             $end->modify('+1 hour');
             $end_value = AphrontFormDateControlValue::newFromEpoch($viewer, $end->format('U'));
         } else {
             list($start_value, $end_value) = $this->getDefaultTimeValues($viewer);
         }
         $recurrence_end_date_value = clone $end_value;
         $recurrence_end_date_value->setOptional(true);
         $submit_label = pht('Create');
         $page_title = pht('Create Event');
         $redirect = 'created';
         $subscribers = array();
         $invitees = array($user_phid);
         $cancel_uri = $this->getApplicationURI();
     } else {
         $event = id(new PhabricatorCalendarEventQuery())->setViewer($viewer)->withIDs(array($this->id))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->executeOne();
         if (!$event) {
             return new Aphront404Response();
         }
         if ($request->getURIData('sequence')) {
             $index = $request->getURIData('sequence');
             $result = $this->getEventAtIndexForGhostPHID($viewer, $event->getPHID(), $index);
             if ($result) {
                 return id(new AphrontRedirectResponse())->setURI('/calendar/event/edit/' . $result->getID() . '/');
             }
             $event = $this->createEventFromGhost($viewer, $event, $index);
             return id(new AphrontRedirectResponse())->setURI('/calendar/event/edit/' . $event->getID() . '/');
         }
         $end_value = AphrontFormDateControlValue::newFromEpoch($viewer, $event->getDateTo());
         $start_value = AphrontFormDateControlValue::newFromEpoch($viewer, $event->getDateFrom());
         $recurrence_end_date_value = id(clone $end_value)->setOptional(true);
         $submit_label = pht('Update');
         $page_title = pht('Update Event');
         $subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID($event->getPHID());
         $invitees = array();
         foreach ($event->getInvitees() as $invitee) {
             if ($invitee->isUninvited()) {
                 continue;
             } else {
                 $invitees[] = $invitee->getInviteePHID();
             }
         }
         $cancel_uri = '/' . $event->getMonogram();
     }
     if ($this->isCreate()) {
         $projects = array();
     } else {
         $projects = PhabricatorEdgeQuery::loadDestinationPHIDs($event->getPHID(), PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
         $projects = array_reverse($projects);
     }
     $name = $event->getName();
     $description = $event->getDescription();
     $is_all_day = $event->getIsAllDay();
     $is_recurring = $event->getIsRecurring();
     $is_parent = $event->getIsRecurrenceParent();
     $frequency = idx($event->getRecurrenceFrequency(), 'rule');
     $icon = $event->getIcon();
     $edit_policy = $event->getEditPolicy();
     $view_policy = $event->getViewPolicy();
     $space = $event->getSpacePHID();
     if ($request->isFormPost()) {
         $xactions = array();
         $name = $request->getStr('name');
         $start_value = AphrontFormDateControlValue::newFromRequest($request, 'start');
         $end_value = AphrontFormDateControlValue::newFromRequest($request, 'end');
         $recurrence_end_date_value = AphrontFormDateControlValue::newFromRequest($request, 'recurrenceEndDate');
         $recurrence_end_date_value->setOptional(true);
         $projects = $request->getArr('projects');
         $description = $request->getStr('description');
         $subscribers = $request->getArr('subscribers');
         $edit_policy = $request->getStr('editPolicy');
         $view_policy = $request->getStr('viewPolicy');
         $space = $request->getStr('spacePHID');
         $is_recurring = $request->getStr('isRecurring') ? 1 : 0;
         $frequency = $request->getStr('frequency');
         $is_all_day = $request->getStr('isAllDay');
         $icon = $request->getStr('icon');
         $invitees = $request->getArr('invitees');
         $new_invitees = $this->getNewInviteeList($invitees, $event);
         $status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
         if ($this->isCreate()) {
             $status = idx($new_invitees, $viewer->getPHID());
             if ($status) {
                 $new_invitees[$viewer->getPHID()] = $status_attending;
             }
         }
         $xactions[] = id(new PhabricatorCalendarEventTransaction())->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_NAME)->setNewValue($name);
         if ($is_recurring && $this->isCreate()) {
             $xactions[] = id(new PhabricatorCalendarEventTransaction())->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_RECURRING)->setNewValue($is_recurring);
             $xactions[] = id(new PhabricatorCalendarEventTransaction())->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_FREQUENCY)->setNewValue(array('rule' => $frequency));
             if (!$recurrence_end_date_value->isDisabled()) {
                 $xactions[] = id(new PhabricatorCalendarEventTransaction())->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE)->setNewValue($recurrence_end_date_value);
             }
         }
         if ($is_recurring && $this->isCreate() || !$is_parent) {
             $xactions[] = id(new PhabricatorCalendarEventTransaction())->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_ALL_DAY)->setNewValue($is_all_day);
             $xactions[] = id(new PhabricatorCalendarEventTransaction())->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_ICON)->setNewValue($icon);
             $xactions[] = id(new PhabricatorCalendarEventTransaction())->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_START_DATE)->setNewValue($start_value);
             $xactions[] = id(new PhabricatorCalendarEventTransaction())->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_END_DATE)->setNewValue($end_value);
         }
         $xactions[] = id(new PhabricatorCalendarEventTransaction())->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)->setNewValue(array('=' => array_fuse($subscribers)));
         $xactions[] = id(new PhabricatorCalendarEventTransaction())->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_INVITE)->setNewValue($new_invitees);
         $xactions[] = id(new PhabricatorCalendarEventTransaction())->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION)->setNewValue($description);
         $xactions[] = id(new PhabricatorCalendarEventTransaction())->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)->setNewValue($request->getStr('viewPolicy'));
         $xactions[] = id(new PhabricatorCalendarEventTransaction())->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)->setNewValue($request->getStr('editPolicy'));
         $xactions[] = id(new PhabricatorCalendarEventTransaction())->setTransactionType(PhabricatorTransactions::TYPE_SPACE)->setNewValue($space);
         $editor = id(new PhabricatorCalendarEventEditor())->setActor($viewer)->setContentSourceFromRequest($request)->setContinueOnNoEffect(true);
         try {
             $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
             $xactions[] = id(new PhabricatorCalendarEventTransaction())->setTransactionType(PhabricatorTransactions::TYPE_EDGE)->setMetadataValue('edge:type', $proj_edge_type)->setNewValue(array('=' => array_fuse($projects)));
             $xactions = $editor->applyTransactions($event, $xactions);
             $response = id(new AphrontRedirectResponse());
             switch ($next_workflow) {
                 case 'day':
                     if (!$uri_query) {
                         $uri_query = 'month';
                     }
                     $year = $start_value->getDateTime()->format('Y');
                     $month = $start_value->getDateTime()->format('m');
                     $day = $start_value->getDateTime()->format('d');
                     $response->setURI('/calendar/query/' . $uri_query . '/' . $year . '/' . $month . '/' . $day . '/');
                     break;
                 default:
                     $response->setURI('/E' . $event->getID());
                     break;
             }
             return $response;
         } catch (PhabricatorApplicationTransactionValidationException $ex) {
             $validation_exception = $ex;
             $error_name = $ex->getShortMessage(PhabricatorCalendarEventTransaction::TYPE_NAME);
             $error_start_date = $ex->getShortMessage(PhabricatorCalendarEventTransaction::TYPE_START_DATE);
             $error_end_date = $ex->getShortMessage(PhabricatorCalendarEventTransaction::TYPE_END_DATE);
             $error_recurrence_end_date = $ex->getShortMessage(PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE);
         }
     }
     $is_recurring_checkbox = null;
     $recurrence_end_date_control = null;
     $recurrence_frequency_select = null;
     $all_day_checkbox = null;
     $start_control = null;
     $end_control = null;
     $recurring_date_edit_label = null;
     $current_policies = id(new PhabricatorPolicyQuery())->setViewer($viewer)->setObject($event)->execute();
     $name = id(new AphrontFormTextControl())->setLabel(pht('Name'))->setName('name')->setValue($name)->setError($error_name);
     if ($this->isCreate()) {
         Javelin::initBehavior('recurring-edit', array('isRecurring' => $is_recurring_id, 'frequency' => $frequency_id, 'recurrenceEndDate' => $recurrence_end_date_id));
         $is_recurring_checkbox = id(new AphrontFormCheckboxControl())->addCheckbox('isRecurring', 1, pht('Recurring Event'), $is_recurring, $is_recurring_id);
         $recurrence_end_date_control = id(new AphrontFormDateControl())->setUser($viewer)->setName('recurrenceEndDate')->setLabel(pht('Recurrence End Date'))->setError($error_recurrence_end_date)->setValue($recurrence_end_date_value)->setID($recurrence_end_date_id)->setIsTimeDisabled(true)->setIsDisabled($recurrence_end_date_value->isDisabled())->setAllowNull(true);
         $recurrence_frequency_select = id(new AphrontFormSelectControl())->setName('frequency')->setOptions(array(PhabricatorCalendarEvent::FREQUENCY_DAILY => pht('Daily'), PhabricatorCalendarEvent::FREQUENCY_WEEKLY => pht('Weekly'), PhabricatorCalendarEvent::FREQUENCY_MONTHLY => pht('Monthly'), PhabricatorCalendarEvent::FREQUENCY_YEARLY => pht('Yearly')))->setValue($frequency)->setLabel(pht('Recurring Event Frequency'))->setID($frequency_id)->setDisabled(!$is_recurring);
     }
     if ($this->isCreate() || !$is_parent && !$this->isCreate()) {
         Javelin::initBehavior('event-all-day', array('allDayID' => $all_day_id, 'startDateID' => $start_date_id, 'endDateID' => $end_date_id));
         $all_day_checkbox = id(new AphrontFormCheckboxControl())->addCheckbox('isAllDay', 1, pht('All Day Event'), $is_all_day, $all_day_id);
         $start_control = id(new AphrontFormDateControl())->setUser($viewer)->setName('start')->setLabel(pht('Start'))->setError($error_start_date)->setValue($start_value)->setID($start_date_id)->setIsTimeDisabled($is_all_day)->setEndDateID($end_date_id);
         $end_control = id(new AphrontFormDateControl())->setUser($viewer)->setName('end')->setLabel(pht('End'))->setError($error_end_date)->setValue($end_value)->setID($end_date_id)->setIsTimeDisabled($is_all_day);
     } else {
         if ($is_parent) {
             $recurring_date_edit_label = id(new AphrontFormStaticControl())->setUser($viewer)->setValue(pht('Date and time of recurring event cannot be edited.'));
             if (!$recurrence_end_date_value->isDisabled()) {
                 $disabled_recurrence_end_date_value = $recurrence_end_date_value->getValueAsFormat('M d, Y');
                 $recurrence_end_date_control = id(new AphrontFormStaticControl())->setUser($viewer)->setLabel(pht('Recurrence End Date'))->setValue($disabled_recurrence_end_date_value)->setDisabled(true);
             }
             $recurrence_frequency_select = id(new AphrontFormSelectControl())->setName('frequency')->setOptions(array('daily' => pht('Daily'), 'weekly' => pht('Weekly'), 'monthly' => pht('Monthly'), 'yearly' => pht('Yearly')))->setValue($frequency)->setLabel(pht('Recurring Event Frequency'))->setID($frequency_id)->setDisabled(true);
             $all_day_checkbox = id(new AphrontFormCheckboxControl())->addCheckbox('isAllDay', 1, pht('All Day Event'), $is_all_day, $all_day_id)->setDisabled(true);
             $start_disabled = $start_value->getValueAsFormat('M d, Y, g:i A');
             $end_disabled = $end_value->getValueAsFormat('M d, Y, g:i A');
             $start_control = id(new AphrontFormStaticControl())->setUser($viewer)->setLabel(pht('Start'))->setValue($start_disabled)->setDisabled(true);
             $end_control = id(new AphrontFormStaticControl())->setUser($viewer)->setLabel(pht('End'))->setValue($end_disabled);
         }
     }
     $projects = id(new AphrontFormTokenizerControl())->setLabel(pht('Projects'))->setName('projects')->setValue($projects)->setUser($viewer)->setDatasource(new PhabricatorProjectDatasource());
     $description = id(new PhabricatorRemarkupControl())->setLabel(pht('Description'))->setName('description')->setValue($description)->setUser($viewer);
     $view_policies = id(new AphrontFormPolicyControl())->setUser($viewer)->setValue($view_policy)->setCapability(PhabricatorPolicyCapability::CAN_VIEW)->setPolicyObject($event)->setPolicies($current_policies)->setSpacePHID($space)->setName('viewPolicy');
     $edit_policies = id(new AphrontFormPolicyControl())->setUser($viewer)->setValue($edit_policy)->setCapability(PhabricatorPolicyCapability::CAN_EDIT)->setPolicyObject($event)->setPolicies($current_policies)->setName('editPolicy');
     $subscribers = id(new AphrontFormTokenizerControl())->setLabel(pht('Subscribers'))->setName('subscribers')->setValue($subscribers)->setUser($viewer)->setDatasource(new PhabricatorMetaMTAMailableDatasource());
     $invitees = id(new AphrontFormTokenizerControl())->setLabel(pht('Invitees'))->setName('invitees')->setValue($invitees)->setUser($viewer)->setDatasource(new PhabricatorMetaMTAMailableDatasource());
     if ($this->isCreate()) {
         $icon_uri = $this->getApplicationURI('icon/');
     } else {
         $icon_uri = $this->getApplicationURI('icon/' . $event->getID() . '/');
     }
     $icon_display = PhabricatorCalendarIcon::renderIconForChooser($icon);
     $icon = id(new AphrontFormChooseButtonControl())->setLabel(pht('Icon'))->setName('icon')->setDisplayValue($icon_display)->setButtonText(pht('Choose Icon...'))->setChooseURI($icon_uri)->setValue($icon);
     $form = id(new AphrontFormView())->addHiddenInput('next', $next_workflow)->addHiddenInput('query', $uri_query)->setUser($viewer)->appendChild($name);
     if ($recurring_date_edit_label) {
         $form->appendControl($recurring_date_edit_label);
     }
     if ($is_recurring_checkbox) {
         $form->appendChild($is_recurring_checkbox);
     }
     if ($recurrence_end_date_control) {
         $form->appendChild($recurrence_end_date_control);
     }
     if ($recurrence_frequency_select) {
         $form->appendControl($recurrence_frequency_select);
     }
     $form->appendChild($all_day_checkbox)->appendChild($start_control)->appendChild($end_control)->appendControl($view_policies)->appendControl($edit_policies)->appendControl($subscribers)->appendControl($invitees)->appendChild($projects)->appendChild($description)->appendChild($icon);
     if ($request->isAjax()) {
         return $this->newDialog()->setTitle($page_title)->setWidth(AphrontDialogView::WIDTH_FULL)->appendForm($form)->addCancelButton($cancel_uri)->addSubmitButton($submit_label);
     }
     $submit = id(new AphrontFormSubmitControl())->addCancelButton($cancel_uri)->setValue($submit_label);
     $form->appendChild($submit);
     $form_box = id(new PHUIObjectBoxView())->setHeaderText($page_title)->setForm($form);
     $crumbs = $this->buildApplicationCrumbs();
     if (!$this->isCreate()) {
         $crumbs->addTextCrumb('E' . $event->getId(), '/E' . $event->getId());
     }
     $crumbs->addTextCrumb($page_title);
     $object_box = id(new PHUIObjectBoxView())->setHeaderText($page_title)->setValidationException($validation_exception)->appendChild($form);
     return $this->buildApplicationPage(array($crumbs, $object_box), array('title' => $page_title));
 }
 public function getTitleForFeed()
 {
     $author_phid = $this->getAuthorPHID();
     $object_phid = $this->getObjectPHID();
     $old = $this->getOldValue();
     $new = $this->getNewValue();
     $viewer = $this->getViewer();
     $type = $this->getTransactionType();
     switch ($type) {
         case self::TYPE_NAME:
             if ($old === null) {
                 return pht('%s created %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
             } else {
                 return pht('%s changed the name of %s from %s to %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $old, $new);
             }
         case self::TYPE_START_DATE:
             if ($old) {
                 $old = phabricator_datetime($old, $viewer);
                 $new = phabricator_datetime($new, $viewer);
                 return pht('%s changed the start date of %s from %s to %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $old, $new);
             }
             break;
         case self::TYPE_END_DATE:
             if ($old) {
                 $old = phabricator_datetime($old, $viewer);
                 $new = phabricator_datetime($new, $viewer);
                 return pht('%s edited the end date of %s from %s to %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $old, $new);
             }
             break;
         case self::TYPE_DESCRIPTION:
             return pht('%s updated the description of %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
         case self::TYPE_ALL_DAY:
             if ($new) {
                 return pht('%s made %s an all day event.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
             } else {
                 return pht('%s converted %s from an all day event.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
             }
         case self::TYPE_ICON:
             return pht('%s set the icon for %s to %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), PhabricatorCalendarIcon::getLabel($new));
         case self::TYPE_CANCEL:
             if ($new) {
                 return pht('%s cancelled %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
             } else {
                 return pht('%s reinstated %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
             }
         case self::TYPE_INVITE:
             $text = null;
             if (count($old) === 1 && count($new) === 1 && isset($old[$author_phid])) {
                 // user joined/declined/accepted event themself
                 $old_status = $old[$author_phid];
                 $new_status = $new[$author_phid];
                 if ($old_status !== $new_status) {
                     switch ($new_status) {
                         case PhabricatorCalendarEventInvitee::STATUS_INVITED:
                             $text = pht('%s has joined %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
                             break;
                         case PhabricatorCalendarEventInvitee::STATUS_ATTENDING:
                             $text = pht('%s is attending %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
                             break;
                         case PhabricatorCalendarEventInvitee::STATUS_DECLINED:
                         case PhabricatorCalendarEventInvitee::STATUS_UNINVITED:
                             $text = pht('%s has declined %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
                             break;
                         default:
                             $text = pht('%s has changed their status of %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
                             break;
                     }
                 }
             } else {
                 // user changed status for many users
                 $self_joined = null;
                 $self_declined = null;
                 $added = array();
                 $uninvited = array();
                 foreach ($new as $phid => $status) {
                     if ($status == PhabricatorCalendarEventInvitee::STATUS_INVITED || $status == PhabricatorCalendarEventInvitee::STATUS_ATTENDING) {
                         // added users
                         $added[] = $phid;
                     } else {
                         if ($status == PhabricatorCalendarEventInvitee::STATUS_DECLINED || $status == PhabricatorCalendarEventInvitee::STATUS_UNINVITED) {
                             $uninvited[] = $phid;
                         }
                     }
                 }
                 $count_added = count($added);
                 $count_uninvited = count($uninvited);
                 $added_text = null;
                 $uninvited_text = null;
                 if ($count_added > 0 && $count_uninvited == 0) {
                     $added_text = $this->renderHandleList($added);
                     $text = pht('%s invited %s to %s.', $this->renderHandleLink($author_phid), $added_text, $this->renderHandleLink($object_phid));
                 } else {
                     if ($count_added > 0 && $count_uninvited > 0) {
                         $added_text = $this->renderHandleList($added);
                         $uninvited_text = $this->renderHandleList($uninvited);
                         $text = pht('%s invited %s and uninvited %s to %s.', $this->renderHandleLink($author_phid), $added_text, $uninvited_text, $this->renderHandleLink($object_phid));
                     } else {
                         if ($count_added == 0 && $count_uninvited > 0) {
                             $uninvited_text = $this->renderHandleList($uninvited);
                             $text = pht('%s uninvited %s to %s.', $this->renderHandleLink($author_phid), $uninvited_text, $this->renderHandleLink($object_phid));
                         } else {
                             $text = pht('%s updated the invitee list of %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
                         }
                     }
                 }
             }
             return $text;
         case self::TYPE_RECURRING:
             $text = pht('%s made %s a recurring event.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
             return $text;
         case self::TYPE_FREQUENCY:
             $text = '';
             switch ($new['rule']) {
                 case PhabricatorCalendarEvent::FREQUENCY_DAILY:
                     $text = pht('%s set %s to repeat daily.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
                     break;
                 case PhabricatorCalendarEvent::FREQUENCY_WEEKLY:
                     $text = pht('%s set %s to repeat weekly.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
                     break;
                 case PhabricatorCalendarEvent::FREQUENCY_MONTHLY:
                     $text = pht('%s set %s to repeat monthly.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
                     break;
                 case PhabricatorCalendarEvent::FREQUENCY_YEARLY:
                     $text = pht('%s set %s to repeat yearly.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid));
                     break;
             }
             return $text;
         case self::TYPE_RECURRENCE_END_DATE:
             $text = pht('%s set the recurrence end date of %s to %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $new);
             return $text;
         case self::TYPE_INSTANCE_OF_EVENT:
         case self::TYPE_SEQUENCE_INDEX:
             return pht('Recurring event has been updated.');
     }
     return parent::getTitleForFeed();
 }