Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php
index 888f52c572..4f92fa5b0c 100644
--- a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php
@@ -1,621 +1,578 @@
<?php
final class PhabricatorCalendarEventEditController
extends PhabricatorCalendarController {
private $id;
public function isCreate() {
return !$this->id;
}
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');
$title = pht('Create Event');
$header_icon = 'fa-plus-square';
$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();
}
$end_value = AphrontFormDateControlValue::newFromEpoch(
$viewer,
$event->getViewerDateTo());
$start_value = AphrontFormDateControlValue::newFromEpoch(
$viewer,
$event->getViewerDateFrom());
$recurrence_end_date_value = id(clone $end_value)
->setOptional(true);
$submit_label = pht('Update');
$title = pht('Edit Event: %s', $event->getName());
$header_icon = 'fa-pencil';
$subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID(
$event->getPHID());
- $invitees = array();
- foreach ($event->getInvitees() as $invitee) {
- if ($invitee->isUninvited()) {
- continue;
- } else {
- $invitees[] = $invitee->getInviteePHID();
- }
- }
-
+ $invitees = $event->getInviteePHIDsForEdit();
$cancel_uri = $event->getURI();
}
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->isParentEvent();
$frequency = idx($event->getRecurrenceFrequency(), 'rule');
$icon = $event->getIcon();
$edit_policy = $event->getEditPolicy();
$view_policy = $event->getViewPolicy();
$space = $event->getSpacePHID();
if ($request->isFormPost()) {
$is_all_day = $request->getStr('isAllDay');
if ($is_all_day) {
// TODO: This is a very gross temporary hack to get this working
// reasonably: if this is an all day event, force the viewer's
// timezone to UTC so the date controls get interpreted as UTC.
$viewer->overrideTimezoneIdentifier('UTC');
}
$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');
$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->getEpoch());
}
}
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->getEpoch());
$xactions[] = id(new PhabricatorCalendarEventTransaction())
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_END_DATE)
->setNewValue($end_value->getEpoch());
}
$xactions[] = id(new PhabricatorCalendarEventTransaction())
->setTransactionType(
PhabricatorTransactions::TYPE_SUBSCRIBERS)
->setNewValue(array('=' => array_fuse($subscribers)));
$xactions[] = id(new PhabricatorCalendarEventTransaction())
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_INVITE)
- ->setNewValue($new_invitees);
+ ->setNewValue($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('Tags'))
->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());
-
$icon = id(new PHUIFormIconSetControl())
->setLabel(pht('Icon'))
->setName('icon')
->setIconSet(new PhabricatorCalendarIconSet())
->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($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(pht('Event'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setValidationException($validation_exception)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
if (!$this->isCreate()) {
$crumbs->addTextCrumb('E'.$event->getId(), '/E'.$event->getId());
$crumb_title = pht('Edit Event');
} else {
$crumb_title = pht('Create Event');
}
$crumbs->addTextCrumb($crumb_title);
$crumbs->setBorder(true);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon($header_icon);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($form_box);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
- public function getNewInviteeList(array $phids, $event) {
- $invitees = $event->getInvitees();
- $invitees = mpull($invitees, null, 'getInviteePHID');
- $invited_status = PhabricatorCalendarEventInvitee::STATUS_INVITED;
- $uninvited_status = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
- $phids = array_fuse($phids);
-
- $new = array();
- foreach ($phids as $phid) {
- $old_status = $event->getUserInviteStatus($phid);
- if ($old_status != $uninvited_status) {
- continue;
- }
- $new[$phid] = $invited_status;
- }
-
- foreach ($invitees as $invitee) {
- $deleted_invitee = !idx($phids, $invitee->getInviteePHID());
- if ($deleted_invitee) {
- $new[$invitee->getInviteePHID()] = $uninvited_status;
- }
- }
-
- return $new;
- }
-
private function getDefaultTimeValues($viewer) {
$start = new DateTime('@'.time());
$start->setTimeZone($viewer->getTimeZone());
$start->setTime($start->format('H'), 0, 0);
$start->modify('+1 hour');
$end = id(clone $start)->modify('+1 hour');
$start_value = AphrontFormDateControlValue::newFromEpoch(
$viewer,
$start->format('U'));
$end_value = AphrontFormDateControlValue::newFromEpoch(
$viewer,
$end->format('U'));
return array($start_value, $end_value);
}
}
diff --git a/src/applications/calendar/editor/PhabricatorCalendarEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarEditEngine.php
index 4aaab4634d..ee36c7e82e 100644
--- a/src/applications/calendar/editor/PhabricatorCalendarEditEngine.php
+++ b/src/applications/calendar/editor/PhabricatorCalendarEditEngine.php
@@ -1,107 +1,126 @@
<?php
final class PhabricatorCalendarEditEngine
extends PhabricatorEditEngine {
const ENGINECONST = 'calendar.event';
public function getEngineName() {
return pht('Calendar Events');
}
public function getSummaryHeader() {
return pht('Configure Calendar Event Forms');
}
public function getSummaryText() {
return pht('Configure how users create and edit events.');
}
public function getEngineApplicationClass() {
return 'PhabricatorCalendarApplication';
}
protected function newEditableObject() {
return PhabricatorCalendarEvent::initializeNewCalendarEvent(
$this->getViewer(),
$mode = null);
}
protected function newObjectQuery() {
return new PhabricatorCalendarEventQuery();
}
protected function getObjectCreateTitleText($object) {
return pht('Create New Event');
}
protected function getObjectEditTitleText($object) {
return pht('Edit Event: %s', $object->getName());
}
protected function getObjectEditShortText($object) {
return $object->getMonogram();
}
protected function getObjectCreateShortText() {
return pht('Create Event');
}
protected function getObjectName() {
return pht('Event');
}
protected function getObjectViewURI($object) {
return $object->getURI();
}
protected function getEditorURI() {
return $this->getApplication()->getApplicationURI('event/editpro/');
}
protected function buildCustomEditFields($object) {
+ $viewer = $this->getViewer();
+
+ if ($this->getIsCreate()) {
+ $invitee_phids = array($viewer->getPHID());
+ } else {
+ $invitee_phids = $object->getInviteePHIDsForEdit();
+ }
+
$fields = array(
id(new PhabricatorTextEditField())
->setKey('name')
->setLabel(pht('Name'))
->setDescription(pht('Name of the event.'))
->setIsRequired(true)
->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_NAME)
->setConduitDescription(pht('Rename the event.'))
->setConduitTypeDescription(pht('New event name.'))
->setValue($object->getName()),
id(new PhabricatorRemarkupEditField())
->setKey('description')
->setLabel(pht('Description'))
->setDescription(pht('Description of the event.'))
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION)
->setConduitDescription(pht('Update the event description.'))
->setConduitTypeDescription(pht('New event description.'))
->setValue($object->getDescription()),
id(new PhabricatorBoolEditField())
->setKey('cancelled')
->setOptions(pht('Active'), pht('Cancelled'))
->setLabel(pht('Cancelled'))
->setDescription(pht('Cancel the event.'))
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_CANCEL)
->setIsConduitOnly(true)
->setConduitDescription(pht('Cancel or restore the event.'))
->setConduitTypeDescription(pht('True to cancel the event.'))
->setValue($object->getIsCancelled()),
+ id(new PhabricatorDatasourceEditField())
+ ->setKey('inviteePHIDs')
+ ->setAliases(array('invite', 'invitee', 'invitees', 'inviteePHID'))
+ ->setLabel(pht('Invitees'))
+ ->setDatasource(new PhabricatorMetaMTAMailableDatasource())
+ ->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_INVITE)
+ ->setDescription(pht('Users invited to the event.'))
+ ->setConduitDescription(pht('Change invited users.'))
+ ->setConduitTypeDescription(pht('New event invitees.'))
+ ->setValue($invitee_phids)
+ ->setCommentActionLabel(pht('Change Invitees')),
id(new PhabricatorIconSetEditField())
->setKey('icon')
->setLabel(pht('Icon'))
->setIconSet(new PhabricatorCalendarIconSet())
->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_ICON)
->setDescription(pht('Event icon.'))
->setConduitDescription(pht('Change the event icon.'))
->setConduitTypeDescription(pht('New event icon.'))
->setValue($object->getIcon()),
);
return $fields;
}
}
diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php
index ce886d2ed6..16e00a2873 100644
--- a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php
+++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php
@@ -1,501 +1,561 @@
<?php
final class PhabricatorCalendarEventEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorCalendarApplication';
}
public function getEditorObjectsDescription() {
return pht('Calendar');
}
protected function shouldApplyInitialEffects(
PhabricatorLiskDAO $object,
array $xactions) {
return true;
}
protected function applyInitialEffects(
PhabricatorLiskDAO $object,
array $xactions) {
$actor = $this->requireActor();
if ($object->getIsStub()) {
$this->materializeStub($object);
}
}
private function materializeStub(PhabricatorCalendarEvent $event) {
if (!$event->getIsStub()) {
throw new Exception(
pht('Can not materialize an event stub: this event is not a stub.'));
}
$actor = $this->getActor();
$event->copyFromParent($actor);
$event->setIsStub(0);
$invitees = $event->getParentEvent()->getInvitees();
$new_invitees = array();
foreach ($invitees as $invitee) {
$invitee = id(new PhabricatorCalendarEventInvitee())
->setEventPHID($event->getPHID())
->setInviteePHID($invitee->getInviteePHID())
->setInviterPHID($invitee->getInviterPHID())
->setStatus($invitee->getStatus())
->save();
$new_invitees[] = $invitee;
}
$event->save();
$event->attachInvitees($new_invitees);
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorCalendarEventTransaction::TYPE_NAME;
$types[] = PhabricatorCalendarEventTransaction::TYPE_START_DATE;
$types[] = PhabricatorCalendarEventTransaction::TYPE_END_DATE;
$types[] = PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION;
$types[] = PhabricatorCalendarEventTransaction::TYPE_CANCEL;
$types[] = PhabricatorCalendarEventTransaction::TYPE_INVITE;
$types[] = PhabricatorCalendarEventTransaction::TYPE_ALL_DAY;
$types[] = PhabricatorCalendarEventTransaction::TYPE_ICON;
$types[] = PhabricatorCalendarEventTransaction::TYPE_ACCEPT;
$types[] = PhabricatorCalendarEventTransaction::TYPE_DECLINE;
$types[] = PhabricatorCalendarEventTransaction::TYPE_RECURRING;
$types[] = PhabricatorCalendarEventTransaction::TYPE_FREQUENCY;
$types[] = PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE;
$types[] = PhabricatorTransactions::TYPE_COMMENT;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorCalendarEventTransaction::TYPE_RECURRING:
return $object->getIsRecurring();
case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY:
return $object->getRecurrenceFrequency();
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
return $object->getRecurrenceEndDate();
case PhabricatorCalendarEventTransaction::TYPE_NAME:
return $object->getName();
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
return $object->getDateFrom();
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
return $object->getDateTo();
case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
return $object->getDescription();
case PhabricatorCalendarEventTransaction::TYPE_CANCEL:
return $object->getIsCancelled();
case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY:
return (int)$object->getIsAllDay();
case PhabricatorCalendarEventTransaction::TYPE_ICON:
return $object->getIcon();
case PhabricatorCalendarEventTransaction::TYPE_ACCEPT:
case PhabricatorCalendarEventTransaction::TYPE_DECLINE:
$actor_phid = $this->getActingAsPHID();
return $object->getUserInviteStatus($actor_phid);
case PhabricatorCalendarEventTransaction::TYPE_INVITE:
- $map = $xaction->getNewValue();
- $phids = array_keys($map);
- $invitees = mpull($object->getInvitees(), null, 'getInviteePHID');
-
- $old = array();
- foreach ($phids as $phid) {
- $invitee = idx($invitees, $phid);
- if ($invitee) {
- $old[$phid] = $invitee->getStatus();
- } else {
- $old[$phid] = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
- }
- }
- return $old;
+ $invitees = $object->getInvitees();
+ return mpull($invitees, 'getStatus', 'getInviteePHID');
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorCalendarEventTransaction::TYPE_RECURRING:
case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY:
case PhabricatorCalendarEventTransaction::TYPE_NAME:
case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
case PhabricatorCalendarEventTransaction::TYPE_CANCEL:
- case PhabricatorCalendarEventTransaction::TYPE_INVITE:
case PhabricatorCalendarEventTransaction::TYPE_ICON:
return $xaction->getNewValue();
case PhabricatorCalendarEventTransaction::TYPE_ACCEPT:
return PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
case PhabricatorCalendarEventTransaction::TYPE_DECLINE:
return PhabricatorCalendarEventInvitee::STATUS_DECLINED;
case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY:
return (int)$xaction->getNewValue();
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
return $xaction->getNewValue();
+ case PhabricatorCalendarEventTransaction::TYPE_INVITE:
+ $status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED;
+ $status_uninvited = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
+ $status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
+
+ $invitees = $object->getInvitees();
+ foreach ($invitees as $key => $invitee) {
+ if ($invitee->getStatus() == $status_uninvited) {
+ unset($invitees[$key]);
+ }
+ }
+ $invitees = mpull($invitees, null, 'getInviteePHID');
+
+ $new = $xaction->getNewValue();
+ $new = array_fuse($new);
+
+ $all = array_keys($invitees + $new);
+ $map = array();
+ foreach ($all as $phid) {
+ $is_old = isset($invitees[$phid]);
+ $is_new = isset($new[$phid]);
+
+ if ($is_old && !$is_new) {
+ $map[$phid] = $status_uninvited;
+ } else if (!$is_old && $is_new) {
+ $map[$phid] = $status_invited;
+ }
+ }
+
+ // If we're creating this event and the actor is inviting themselves,
+ // mark them as attending.
+ if ($this->getIsNewObject()) {
+ $acting_phid = $this->getActingAsPHID();
+ if (isset($map[$acting_phid])) {
+ $map[$acting_phid] = $status_attending;
+ }
+ }
+
+ return $map;
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorCalendarEventTransaction::TYPE_RECURRING:
return $object->setIsRecurring($xaction->getNewValue());
case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY:
return $object->setRecurrenceFrequency($xaction->getNewValue());
case PhabricatorCalendarEventTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
$object->setDateFrom($xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
$object->setDateTo($xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
$object->setRecurrenceEndDate($xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
$object->setDescription($xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_CANCEL:
$object->setIsCancelled((int)$xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY:
$object->setIsAllDay((int)$xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_ICON:
$object->setIcon($xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_INVITE:
case PhabricatorCalendarEventTransaction::TYPE_ACCEPT:
case PhabricatorCalendarEventTransaction::TYPE_DECLINE:
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorCalendarEventTransaction::TYPE_RECURRING:
case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY:
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
case PhabricatorCalendarEventTransaction::TYPE_NAME:
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
case PhabricatorCalendarEventTransaction::TYPE_CANCEL:
case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY:
case PhabricatorCalendarEventTransaction::TYPE_ICON:
return;
case PhabricatorCalendarEventTransaction::TYPE_INVITE:
$map = $xaction->getNewValue();
$phids = array_keys($map);
$invitees = $object->getInvitees();
$invitees = mpull($invitees, null, 'getInviteePHID');
foreach ($phids as $phid) {
$invitee = idx($invitees, $phid);
if (!$invitee) {
$invitee = id(new PhabricatorCalendarEventInvitee())
->setEventPHID($object->getPHID())
->setInviteePHID($phid)
->setInviterPHID($this->getActingAsPHID());
$invitees[] = $invitee;
}
$invitee->setStatus($map[$phid])
->save();
}
$object->attachInvitees($invitees);
return;
case PhabricatorCalendarEventTransaction::TYPE_ACCEPT:
case PhabricatorCalendarEventTransaction::TYPE_DECLINE:
$acting_phid = $this->getActingAsPHID();
$invitees = $object->getInvitees();
$invitees = mpull($invitees, null, 'getInviteePHID');
$invitee = idx($invitees, $acting_phid);
if (!$invitee) {
$invitee = id(new PhabricatorCalendarEventInvitee())
->setEventPHID($object->getPHID())
->setInviteePHID($acting_phid)
->setInviterPHID($acting_phid);
$invitees[$acting_phid] = $invitee;
}
$invitee
->setStatus($xaction->getNewValue())
->save();
$object->attachInvitees($invitees);
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
protected function applyFinalEffects(
PhabricatorLiskDAO $object,
array $xactions) {
// Clear the availability caches for users whose availability is affected
// by this edit.
$invalidate_all = false;
$invalidate_phids = array();
foreach ($xactions as $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorCalendarEventTransaction::TYPE_ICON:
break;
case PhabricatorCalendarEventTransaction::TYPE_RECURRING:
case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY:
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
case PhabricatorCalendarEventTransaction::TYPE_CANCEL:
case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY:
// For these kinds of changes, we need to invalidate the availabilty
// caches for all attendees.
$invalidate_all = true;
break;
case PhabricatorCalendarEventTransaction::TYPE_ACCEPT:
case PhabricatorCalendarEventTransaction::TYPE_DECLINE:
$acting_phid = $this->getActingAsPHID();
$invalidate_phids[$acting_phid] = $acting_phid;
break;
case PhabricatorCalendarEventTransaction::TYPE_INVITE:
foreach ($xaction->getNewValue() as $phid => $ignored) {
$invalidate_phids[$phid] = $phid;
}
break;
}
}
$phids = mpull($object->getInvitees(), 'getInviteePHID');
$phids = array_fuse($phids);
if (!$invalidate_all) {
$phids = array_select_keys($phids, $invalidate_phids);
}
if ($phids) {
$object->applyViewerTimezone($this->getActor());
$user = new PhabricatorUser();
$conn_w = $user->establishConnection('w');
queryfx(
$conn_w,
'UPDATE %T SET availabilityCacheTTL = NULL
WHERE phid IN (%Ls) AND availabilityCacheTTL >= %d',
$user->getTableName(),
$phids,
$object->getDateFromForCache());
}
return $xactions;
}
protected function validateAllTransactions(
PhabricatorLiskDAO $object,
array $xactions) {
$start_date_xaction =
PhabricatorCalendarEventTransaction::TYPE_START_DATE;
$end_date_xaction =
PhabricatorCalendarEventTransaction::TYPE_END_DATE;
$is_recurrence_xaction =
PhabricatorCalendarEventTransaction::TYPE_RECURRING;
$recurrence_end_xaction =
PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE;
$start_date = $object->getDateFrom();
$end_date = $object->getDateTo();
$recurrence_end = $object->getRecurrenceEndDate();
$is_recurring = $object->getIsRecurring();
$errors = array();
foreach ($xactions as $xaction) {
if ($xaction->getTransactionType() == $start_date_xaction) {
$start_date = $xaction->getNewValue();
} else if ($xaction->getTransactionType() == $end_date_xaction) {
$end_date = $xaction->getNewValue();
} else if ($xaction->getTransactionType() == $recurrence_end_xaction) {
$recurrence_end = $xaction->getNewValue();
} else if ($xaction->getTransactionType() == $is_recurrence_xaction) {
$is_recurring = $xaction->getNewValue();
}
}
if ($start_date > $end_date) {
$type = PhabricatorCalendarEventTransaction::TYPE_END_DATE;
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht('End date must be after start date.'),
null);
}
if ($recurrence_end && !$is_recurring) {
$type =
PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE;
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht('Event must be recurring to have a recurrence end date.').
null);
}
return $errors;
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case PhabricatorCalendarEventTransaction::TYPE_NAME:
$missing = $this->validateIsEmptyTextField(
$object->getName(),
$xactions);
if ($missing) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('Event name is required.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
break;
+ case PhabricatorCalendarEventTransaction::TYPE_INVITE:
+ $old = $object->getInvitees();
+ $old = mpull($old, null, 'getInviteePHID');
+ foreach ($xactions as $xaction) {
+ $new = $xaction->getNewValue();
+ $new = array_fuse($new);
+ $add = array_diff_key($new, $old);
+ if (!$add) {
+ continue;
+ }
+
+ // In the UI, we only allow you to invite mailable objects, but there
+ // is no definitive marker for "invitable object" today. Just allow
+ // any valid object to be invited.
+ $objects = id(new PhabricatorObjectQuery())
+ ->setViewer($this->getActor())
+ ->withPHIDs($add)
+ ->execute();
+ $objects = mpull($objects, null, 'getPHID');
+ foreach ($add as $phid) {
+ if (isset($objects[$phid])) {
+ continue;
+ }
+
+ $errors[] = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Invalid'),
+ pht(
+ 'Invitee "%s" identifies an object that does not exist or '.
+ 'which you do not have permission to view.',
+ $phid));
+ }
+ }
+ break;
}
return $errors;
}
protected function shouldPublishFeedStory(
PhabricatorLiskDAO $object,
array $xactions) {
return true;
}
protected function supportsSearch() {
return true;
}
protected function shouldSendMail(
PhabricatorLiskDAO $object,
array $xactions) {
return true;
}
protected function getMailSubjectPrefix() {
return pht('[Calendar]');
}
protected function getMailTo(PhabricatorLiskDAO $object) {
$phids = array();
if ($object->getUserPHID()) {
$phids[] = $object->getUserPHID();
}
$phids[] = $this->getActingAsPHID();
$invitees = $object->getInvitees();
foreach ($invitees as $invitee) {
$status = $invitee->getStatus();
if ($status === PhabricatorCalendarEventInvitee::STATUS_ATTENDING
|| $status === PhabricatorCalendarEventInvitee::STATUS_INVITED) {
$phids[] = $invitee->getInviteePHID();
}
}
$phids = array_unique($phids);
return $phids;
}
public function getMailTagsMap() {
return array(
PhabricatorCalendarEventTransaction::MAILTAG_CONTENT =>
pht(
"An event's name, status, invite list, ".
"icon, and description changes."),
PhabricatorCalendarEventTransaction::MAILTAG_RESCHEDULE =>
pht(
"An event's start and end date ".
"and cancellation status changes."),
PhabricatorCalendarEventTransaction::MAILTAG_OTHER =>
pht('Other event activity not listed above occurs.'),
);
}
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
return id(new PhabricatorCalendarReplyHandler())
->setMailReceiver($object);
}
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
$id = $object->getID();
$name = $object->getName();
return id(new PhabricatorMetaMTAMail())
->setSubject("E{$id}: {$name}")
->addHeader('Thread-Topic', "E{$id}: ".$object->getName());
}
protected function buildMailBody(
PhabricatorLiskDAO $object,
array $xactions) {
$description = $object->getDescription();
$body = parent::buildMailBody($object, $xactions);
if (strlen($description)) {
$body->addRemarkupSection(
pht('EVENT DESCRIPTION'),
$description);
}
$body->addLinkSection(
pht('EVENT DETAIL'),
PhabricatorEnv::getProductionURI('/E'.$object->getID()));
return $body;
}
}
diff --git a/src/applications/calendar/storage/PhabricatorCalendarEvent.php b/src/applications/calendar/storage/PhabricatorCalendarEvent.php
index 991bfbf521..3fba3f2e66 100644
--- a/src/applications/calendar/storage/PhabricatorCalendarEvent.php
+++ b/src/applications/calendar/storage/PhabricatorCalendarEvent.php
@@ -1,610 +1,623 @@
<?php
final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
implements PhabricatorPolicyInterface,
PhabricatorProjectInterface,
PhabricatorMarkupInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorSubscribableInterface,
PhabricatorTokenReceiverInterface,
PhabricatorDestructibleInterface,
PhabricatorMentionableInterface,
PhabricatorFlaggableInterface,
PhabricatorSpacesInterface,
PhabricatorFulltextInterface {
protected $name;
protected $userPHID;
protected $dateFrom;
protected $dateTo;
protected $description;
protected $isCancelled;
protected $isAllDay;
protected $icon;
protected $mailKey;
protected $isStub;
protected $isRecurring = 0;
protected $recurrenceFrequency = array();
protected $recurrenceEndDate;
private $isGhostEvent = false;
protected $instanceOfEventPHID;
protected $sequenceIndex;
protected $viewPolicy;
protected $editPolicy;
protected $spacePHID;
const DEFAULT_ICON = 'fa-calendar';
private $parentEvent = self::ATTACHABLE;
private $invitees = self::ATTACHABLE;
private $viewerDateFrom;
private $viewerDateTo;
// Frequency Constants
const FREQUENCY_DAILY = 'daily';
const FREQUENCY_WEEKLY = 'weekly';
const FREQUENCY_MONTHLY = 'monthly';
const FREQUENCY_YEARLY = 'yearly';
public static function initializeNewCalendarEvent(
PhabricatorUser $actor,
$mode) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
->withClasses(array('PhabricatorCalendarApplication'))
->executeOne();
$view_policy = null;
$is_recurring = 0;
if ($mode == 'public') {
$view_policy = PhabricatorPolicies::getMostOpenPolicy();
}
if ($mode == 'recurring') {
$is_recurring = true;
}
return id(new PhabricatorCalendarEvent())
->setUserPHID($actor->getPHID())
->setIsCancelled(0)
->setIsAllDay(0)
->setIsStub(0)
->setIsRecurring($is_recurring)
->setIcon(self::DEFAULT_ICON)
->setViewPolicy($view_policy)
->setEditPolicy($actor->getPHID())
->setSpacePHID($actor->getDefaultSpacePHID())
->attachInvitees(array())
->applyViewerTimezone($actor);
}
private function newChild(PhabricatorUser $actor, $sequence) {
if (!$this->isParentEvent()) {
throw new Exception(
pht(
'Unable to generate a new child event for an event which is not '.
'a recurring parent event!'));
}
$child = id(new self())
->setIsCancelled(0)
->setIsStub(0)
->setInstanceOfEventPHID($this->getPHID())
->setSequenceIndex($sequence)
->setIsRecurring(true)
->setRecurrenceFrequency($this->getRecurrenceFrequency())
->attachParentEvent($this);
return $child->copyFromParent($actor);
}
protected function readField($field) {
static $inherit = array(
'userPHID' => true,
'isAllDay' => true,
'icon' => true,
'spacePHID' => true,
'viewPolicy' => true,
'editPolicy' => true,
'name' => true,
'description' => true,
);
// Read these fields from the parent event instead of this event. For
// example, we want any changes to the parent event's name to
if (isset($inherit[$field])) {
if ($this->getIsStub()) {
// TODO: This should be unconditional, but the execution order of
// CalendarEventQuery and applyViewerTimezone() are currently odd.
if ($this->parentEvent !== self::ATTACHABLE) {
return $this->getParentEvent()->readField($field);
}
}
}
return parent::readField($field);
}
public function copyFromParent(PhabricatorUser $actor) {
if (!$this->isChildEvent()) {
throw new Exception(
pht(
'Unable to copy from parent event: this is not a child event.'));
}
$parent = $this->getParentEvent();
$this
->setUserPHID($parent->getUserPHID())
->setIsAllDay($parent->getIsAllDay())
->setIcon($parent->getIcon())
->setSpacePHID($parent->getSpacePHID())
->setViewPolicy($parent->getViewPolicy())
->setEditPolicy($parent->getEditPolicy())
->setName($parent->getName())
->setDescription($parent->getDescription());
$frequency = $parent->getFrequencyUnit();
$modify_key = '+'.$this->getSequenceIndex().' '.$frequency;
$date = $parent->getDateFrom();
$date_time = PhabricatorTime::getDateTimeFromEpoch($date, $actor);
$date_time->modify($modify_key);
$date = $date_time->format('U');
$duration = $this->getDuration();
$this
->setDateFrom($date)
->setDateTo($date + $duration);
return $this;
}
public function newStub(PhabricatorUser $actor, $sequence) {
$stub = $this->newChild($actor, $sequence);
$stub->setIsStub(1);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$stub->save();
unset($unguarded);
$stub->applyViewerTimezone($actor);
return $stub;
}
public function newGhost(PhabricatorUser $actor, $sequence) {
$ghost = $this->newChild($actor, $sequence);
$ghost
->setIsGhostEvent(true)
->makeEphemeral();
$ghost->applyViewerTimezone($actor);
return $ghost;
}
public function getViewerDateFrom() {
if ($this->viewerDateFrom === null) {
throw new PhutilInvalidStateException('applyViewerTimezone');
}
return $this->viewerDateFrom;
}
public function getViewerDateTo() {
if ($this->viewerDateTo === null) {
throw new PhutilInvalidStateException('applyViewerTimezone');
}
return $this->viewerDateTo;
}
public function applyViewerTimezone(PhabricatorUser $viewer) {
if (!$this->getIsAllDay()) {
$this->viewerDateFrom = $this->getDateFrom();
$this->viewerDateTo = $this->getDateTo();
} else {
$zone = $viewer->getTimeZone();
$this->viewerDateFrom = $this->getDateEpochForTimeZone(
$this->getDateFrom(),
new DateTimeZone('UTC'),
'Y-m-d',
null,
$zone);
$this->viewerDateTo = $this->getDateEpochForTimeZone(
$this->getDateTo(),
new DateTimeZone('UTC'),
'Y-m-d 23:59:00',
null,
$zone);
}
return $this;
}
public function getDuration() {
return $this->getDateTo() - $this->getDateFrom();
}
private function getDateEpochForTimeZone(
$epoch,
$src_zone,
$format,
$adjust,
$dst_zone) {
$src = new DateTime('@'.$epoch);
$src->setTimeZone($src_zone);
if (strlen($adjust)) {
$adjust = ' '.$adjust;
}
$dst = new DateTime($src->format($format).$adjust, $dst_zone);
return $dst->format('U');
}
public function save() {
if (!$this->mailKey) {
$this->mailKey = Filesystem::readRandomCharacters(20);
}
return parent::save();
}
/**
* Get the event start epoch for evaluating invitee availability.
*
* When assessing availability, we pretend events start earlier than they
* really do. This allows us to mark users away for the entire duration of a
* series of back-to-back meetings, even if they don't strictly overlap.
*
* @return int Event start date for availability caches.
*/
public function getDateFromForCache() {
return ($this->getViewerDateFrom() - phutil_units('15 minutes in seconds'));
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'text',
'dateFrom' => 'epoch',
'dateTo' => 'epoch',
'description' => 'text',
'isCancelled' => 'bool',
'isAllDay' => 'bool',
'icon' => 'text32',
'mailKey' => 'bytes20',
'isRecurring' => 'bool',
'recurrenceEndDate' => 'epoch?',
'instanceOfEventPHID' => 'phid?',
'sequenceIndex' => 'uint32?',
'isStub' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
'userPHID_dateFrom' => array(
'columns' => array('userPHID', 'dateTo'),
),
'key_instance' => array(
'columns' => array('instanceOfEventPHID', 'sequenceIndex'),
'unique' => true,
),
),
self::CONFIG_SERIALIZATION => array(
'recurrenceFrequency' => self::SERIALIZATION_JSON,
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorCalendarEventPHIDType::TYPECONST);
}
public function getMonogram() {
return 'E'.$this->getID();
}
public function getInvitees() {
return $this->assertAttached($this->invitees);
}
public function attachInvitees(array $invitees) {
$this->invitees = $invitees;
return $this;
}
+ public function getInviteePHIDsForEdit() {
+ $invitees = array();
+
+ foreach ($this->getInvitees() as $invitee) {
+ if ($invitee->isUninvited()) {
+ continue;
+ }
+ $invitees[] = $invitee->getInviteePHID();
+ }
+
+ return $invitees;
+ }
+
public function getUserInviteStatus($phid) {
$invitees = $this->getInvitees();
$invitees = mpull($invitees, null, 'getInviteePHID');
$invited = idx($invitees, $phid);
if (!$invited) {
return PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
}
$invited = $invited->getStatus();
return $invited;
}
public function getIsUserAttending($phid) {
$attending_status = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
$old_status = $this->getUserInviteStatus($phid);
$is_attending = ($old_status == $attending_status);
return $is_attending;
}
public function getIsUserInvited($phid) {
$uninvited_status = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
$declined_status = PhabricatorCalendarEventInvitee::STATUS_DECLINED;
$status = $this->getUserInviteStatus($phid);
if ($status == $uninvited_status || $status == $declined_status) {
return false;
}
return true;
}
public function getIsGhostEvent() {
return $this->isGhostEvent;
}
public function setIsGhostEvent($is_ghost_event) {
$this->isGhostEvent = $is_ghost_event;
return $this;
}
public function getFrequencyUnit() {
$frequency = idx($this->recurrenceFrequency, 'rule');
switch ($frequency) {
case 'daily':
return 'day';
case 'weekly':
return 'week';
case 'monthly':
return 'month';
case 'yearly':
return 'year';
default:
return 'day';
}
}
public function getURI() {
if ($this->getIsGhostEvent()) {
$base = $this->getParentEvent()->getURI();
$sequence = $this->getSequenceIndex();
return "{$base}/{$sequence}/";
}
return '/'.$this->getMonogram();
}
public function getParentEvent() {
return $this->assertAttached($this->parentEvent);
}
public function attachParentEvent($event) {
$this->parentEvent = $event;
return $this;
}
public function isParentEvent() {
return ($this->isRecurring && !$this->instanceOfEventPHID);
}
public function isChildEvent() {
return ($this->instanceOfEventPHID !== null);
}
public function isCancelledEvent() {
if ($this->getIsCancelled()) {
return true;
}
if ($this->isChildEvent()) {
if ($this->getParentEvent()->getIsCancelled()) {
return true;
}
}
return false;
}
public function getDisplayDuration() {
$seconds = $this->getDuration();
$minutes = round($seconds / 60, 1);
$hours = round($minutes / 60, 3);
$days = round($hours / 24, 2);
$duration = '';
if ($days >= 1) {
return pht(
'%s day(s)',
round($days, 1));
} else if ($hours >= 1) {
return pht(
'%s hour(s)',
round($hours, 1));
} else if ($minutes >= 1) {
return pht(
'%s minute(s)',
round($minutes, 0));
}
}
/* -( Markup Interface )--------------------------------------------------- */
/**
* @task markup
*/
public function getMarkupFieldKey($field) {
$hash = PhabricatorHash::digest($this->getMarkupText($field));
$id = $this->getID();
return "calendar:T{$id}:{$field}:{$hash}";
}
/**
* @task markup
*/
public function getMarkupText($field) {
return $this->getDescription();
}
/**
* @task markup
*/
public function newMarkupEngine($field) {
return PhabricatorMarkupEngine::newCalendarMarkupEngine();
}
/**
* @task markup
*/
public function didMarkupText(
$field,
$output,
PhutilMarkupEngine $engine) {
return $output;
}
/**
* @task markup
*/
public function shouldUseMarkupCache($field) {
return (bool)$this->getID();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
// The owner of a task can always view and edit it.
$user_phid = $this->getUserPHID();
if ($user_phid) {
$viewer_phid = $viewer->getPHID();
if ($viewer_phid == $user_phid) {
return true;
}
}
if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
$status = $this->getUserInviteStatus($viewer->getPHID());
if ($status == PhabricatorCalendarEventInvitee::STATUS_INVITED ||
$status == PhabricatorCalendarEventInvitee::STATUS_ATTENDING ||
$status == PhabricatorCalendarEventInvitee::STATUS_DECLINED) {
return true;
}
}
return false;
}
public function describeAutomaticCapability($capability) {
return pht('The owner of an event can always view and edit it,
and invitees can always view it, except if the event is an
instance of a recurring event.');
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhabricatorCalendarEventEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhabricatorCalendarEventTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
/* -( PhabricatorSubscribableInterface )----------------------------------- */
public function isAutomaticallySubscribed($phid) {
return ($phid == $this->getUserPHID());
}
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
public function getUsersToNotifyOfTokenGiven() {
return array($this->getUserPHID());
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$this->delete();
$this->saveTransaction();
}
/* -( PhabricatorSpacesInterface )----------------------------------------- */
public function getSpacePHID() {
return $this->spacePHID;
}
/* -( PhabricatorFulltextInterface )--------------------------------------- */
public function newFulltextEngine() {
return new PhabricatorCalendarEventFulltextEngine();
}
}
diff --git a/src/applications/calendar/storage/PhabricatorCalendarEventTransaction.php b/src/applications/calendar/storage/PhabricatorCalendarEventTransaction.php
index 584c41e569..0040ff8817 100644
--- a/src/applications/calendar/storage/PhabricatorCalendarEventTransaction.php
+++ b/src/applications/calendar/storage/PhabricatorCalendarEventTransaction.php
@@ -1,578 +1,585 @@
<?php
final class PhabricatorCalendarEventTransaction
extends PhabricatorApplicationTransaction {
const TYPE_NAME = 'calendar.name';
const TYPE_START_DATE = 'calendar.startdate';
const TYPE_END_DATE = 'calendar.enddate';
const TYPE_DESCRIPTION = 'calendar.description';
const TYPE_CANCEL = 'calendar.cancel';
const TYPE_ALL_DAY = 'calendar.allday';
const TYPE_ICON = 'calendar.icon';
const TYPE_INVITE = 'calendar.invite';
const TYPE_ACCEPT = 'calendar.accept';
const TYPE_DECLINE = 'calendar.decline';
const TYPE_RECURRING = 'calendar.recurring';
const TYPE_FREQUENCY = 'calendar.frequency';
const TYPE_RECURRENCE_END_DATE = 'calendar.recurrenceenddate';
-
const MAILTAG_RESCHEDULE = 'calendar-reschedule';
const MAILTAG_CONTENT = 'calendar-content';
const MAILTAG_OTHER = 'calendar-other';
public function getApplicationName() {
return 'calendar';
}
public function getApplicationTransactionType() {
return PhabricatorCalendarEventPHIDType::TYPECONST;
}
public function getApplicationTransactionCommentObject() {
return new PhabricatorCalendarEventTransactionComment();
}
public function getRequiredHandlePHIDs() {
$phids = parent::getRequiredHandlePHIDs();
switch ($this->getTransactionType()) {
case self::TYPE_NAME:
case self::TYPE_START_DATE:
case self::TYPE_END_DATE:
case self::TYPE_DESCRIPTION:
case self::TYPE_CANCEL:
case self::TYPE_ALL_DAY:
case self::TYPE_RECURRING:
case self::TYPE_FREQUENCY:
case self::TYPE_RECURRENCE_END_DATE:
$phids[] = $this->getObjectPHID();
break;
case self::TYPE_INVITE:
$new = $this->getNewValue();
foreach ($new as $phid => $status) {
$phids[] = $phid;
}
break;
}
return $phids;
}
public function shouldHide() {
$old = $this->getOldValue();
switch ($this->getTransactionType()) {
case self::TYPE_START_DATE:
case self::TYPE_END_DATE:
case self::TYPE_DESCRIPTION:
case self::TYPE_CANCEL:
case self::TYPE_ALL_DAY:
case self::TYPE_INVITE:
case self::TYPE_RECURRING:
case self::TYPE_FREQUENCY:
case self::TYPE_RECURRENCE_END_DATE:
return ($old === null);
}
return parent::shouldHide();
}
public function getIcon() {
switch ($this->getTransactionType()) {
case self::TYPE_ICON:
return $this->getNewValue();
case self::TYPE_NAME:
case self::TYPE_START_DATE:
case self::TYPE_END_DATE:
case self::TYPE_DESCRIPTION:
case self::TYPE_ALL_DAY:
case self::TYPE_CANCEL:
case self::TYPE_RECURRING:
case self::TYPE_FREQUENCY:
case self::TYPE_RECURRENCE_END_DATE:
return 'fa-pencil';
break;
case self::TYPE_INVITE:
return 'fa-user-plus';
break;
}
return parent::getIcon();
}
public function getTitle() {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
switch ($type) {
case self::TYPE_NAME:
if ($old === null) {
return pht(
'%s created this event.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s changed the name of this event from %s to %s.',
$this->renderHandleLink($author_phid),
$old,
$new);
}
case self::TYPE_START_DATE:
if ($old) {
return pht(
'%s edited the start date of this event.',
$this->renderHandleLink($author_phid));
}
break;
case self::TYPE_END_DATE:
if ($old) {
return pht(
'%s edited the end date of this event.',
$this->renderHandleLink($author_phid));
}
break;
case self::TYPE_DESCRIPTION:
return pht(
"%s updated the event's description.",
$this->renderHandleLink($author_phid));
case self::TYPE_ALL_DAY:
if ($new) {
return pht(
'%s made this an all day event.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s converted this from an all day event.',
$this->renderHandleLink($author_phid));
}
case self::TYPE_ICON:
$set = new PhabricatorCalendarIconSet();
return pht(
'%s set this event\'s icon to %s.',
$this->renderHandleLink($author_phid),
$set->getIconLabel($new));
break;
case self::TYPE_CANCEL:
if ($new) {
return pht(
'%s cancelled this event.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s reinstated this event.',
$this->renderHandleLink($author_phid));
}
case self::TYPE_ACCEPT:
return pht(
'%s is attending this event.',
$this->renderHandleLink($author_phid));
case self::TYPE_DECLINE:
return pht(
'%s declined this event.',
$this->renderHandleLink($author_phid));
case self::TYPE_INVITE:
$text = null;
+ // Fill in any new invitees as "uninvited" in the old data, to make
+ // some of the rendering logic a little easier.
+ $status_uninvited = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
+ $old = $old + array_fill_keys(array_keys($new), $status_uninvited);
+
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 this event.',
$this->renderHandleLink($author_phid));
break;
case PhabricatorCalendarEventInvitee::STATUS_ATTENDING:
$text = pht(
'%s is attending this event.',
$this->renderHandleLink($author_phid));
break;
case PhabricatorCalendarEventInvitee::STATUS_DECLINED:
case PhabricatorCalendarEventInvitee::STATUS_UNINVITED:
$text = pht(
'%s has declined this event.',
$this->renderHandleLink($author_phid));
break;
default:
$text = pht(
'%s has changed their status for this event.',
$this->renderHandleLink($author_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.',
$this->renderHandleLink($author_phid),
$added_text);
} 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.',
$this->renderHandleLink($author_phid),
$added_text,
$uninvited_text);
} else if ($count_added == 0 && $count_uninvited > 0) {
$uninvited_text = $this->renderHandleList($uninvited);
$text = pht('%s uninvited %s.',
$this->renderHandleLink($author_phid),
$uninvited_text);
} else {
$text = pht('%s updated the invitee list.',
$this->renderHandleLink($author_phid));
}
}
return $text;
case self::TYPE_RECURRING:
$text = pht('%s made this event recurring.',
$this->renderHandleLink($author_phid));
return $text;
case self::TYPE_FREQUENCY:
$text = '';
switch ($new['rule']) {
case PhabricatorCalendarEvent::FREQUENCY_DAILY:
$text = pht('%s set this event to repeat daily.',
$this->renderHandleLink($author_phid));
break;
case PhabricatorCalendarEvent::FREQUENCY_WEEKLY:
$text = pht('%s set this event to repeat weekly.',
$this->renderHandleLink($author_phid));
break;
case PhabricatorCalendarEvent::FREQUENCY_MONTHLY:
$text = pht('%s set this event to repeat monthly.',
$this->renderHandleLink($author_phid));
break;
case PhabricatorCalendarEvent::FREQUENCY_YEARLY:
$text = pht('%s set this event to repeat yearly.',
$this->renderHandleLink($author_phid));
break;
}
return $text;
case self::TYPE_RECURRENCE_END_DATE:
$text = pht('%s has changed the recurrence end date of this event.',
$this->renderHandleLink($author_phid));
return $text;
}
return parent::getTitle();
}
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:
$set = new PhabricatorCalendarIconSet();
return pht(
'%s set the icon for %s to %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid),
$set->getIconLabel($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_ACCEPT:
return pht(
'%s is attending %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
case self::TYPE_DECLINE:
return pht(
'%s declined %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
case self::TYPE_INVITE:
$text = null;
+ $status_uninvited = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
+ $old = $old + array_fill_keys(array_keys($new), $status_uninvited);
+
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;
}
return parent::getTitleForFeed();
}
public function getColor() {
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_NAME:
case self::TYPE_START_DATE:
case self::TYPE_END_DATE:
case self::TYPE_DESCRIPTION:
case self::TYPE_CANCEL:
case self::TYPE_INVITE:
return PhabricatorTransactions::COLOR_GREEN;
}
return parent::getColor();
}
public function hasChangeDetails() {
switch ($this->getTransactionType()) {
case self::TYPE_DESCRIPTION:
return ($this->getOldValue() !== null);
}
return parent::hasChangeDetails();
}
public function renderChangeDetails(PhabricatorUser $viewer) {
switch ($this->getTransactionType()) {
case self::TYPE_DESCRIPTION:
$old = $this->getOldValue();
$new = $this->getNewValue();
return $this->renderTextCorpusChangeDetails(
$viewer,
$old,
$new);
}
return parent::renderChangeDetails($viewer);
}
public function getMailTags() {
$tags = array();
switch ($this->getTransactionType()) {
case self::TYPE_NAME:
case self::TYPE_DESCRIPTION:
case self::TYPE_INVITE:
case self::TYPE_ICON:
$tags[] = self::MAILTAG_CONTENT;
break;
case self::TYPE_START_DATE:
case self::TYPE_END_DATE:
case self::TYPE_CANCEL:
$tags[] = self::MAILTAG_RESCHEDULE;
break;
}
return $tags;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jul 2, 9:17 AM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
164820
Default Alt Text
(83 KB)

Event Timeline