Page MenuHomePhorge

No OneTemporary

diff --git a/src/applications/calendar/application/PhabricatorCalendarApplication.php b/src/applications/calendar/application/PhabricatorCalendarApplication.php
index 38bd7f9d0a..20e89425df 100644
--- a/src/applications/calendar/application/PhabricatorCalendarApplication.php
+++ b/src/applications/calendar/application/PhabricatorCalendarApplication.php
@@ -1,97 +1,97 @@
<?php
final class PhabricatorCalendarApplication extends PhabricatorApplication {
public function getName() {
return pht('Calendar');
}
public function getShortDescription() {
return pht('Upcoming Events');
}
public function getFlavorText() {
return pht('Never miss an episode ever again.');
}
public function getBaseURI() {
return '/calendar/';
}
public function getFontIcon() {
return 'fa-calendar';
}
public function getTitleGlyph() {
// Unicode has a calendar character but it's in some distant code plane,
// use "keyboard" since it looks vaguely similar.
return "\xE2\x8C\xA8";
}
public function isPrototype() {
return true;
}
public function getRemarkupRules() {
return array(
new PhabricatorCalendarRemarkupRule(),
);
}
public function getRoutes() {
return array(
'/E(?P<id>[1-9]\d*)(?:/(?P<sequence>\d+))?'
=> 'PhabricatorCalendarEventViewController',
'/calendar/' => array(
'(?:query/(?P<queryKey>[^/]+)/(?:(?P<year>\d+)/'.
'(?P<month>\d+)/)?(?:(?P<day>\d+)/)?)?'
=> 'PhabricatorCalendarEventListController',
'icon/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventEditIconController',
'icon/'
=> 'PhabricatorCalendarEventEditIconController',
'event/' => array(
'create/'
=> 'PhabricatorCalendarEventEditController',
'edit/(?P<id>[1-9]\d*)/(?:(?P<sequence>\d+)/)?'
=> 'PhabricatorCalendarEventEditController',
'drag/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventDragController',
- 'cancel/(?P<id>[1-9]\d*)/'
+ 'cancel/(?P<id>[1-9]\d*)/(?:(?P<sequence>\d+)/)?'
=> 'PhabricatorCalendarEventCancelController',
'(?P<action>join|decline|accept)/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventJoinController',
'comment/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventCommentController',
),
),
);
}
public function getQuickCreateItems(PhabricatorUser $viewer) {
$items = array();
$item = id(new PHUIListItemView())
->setName(pht('Calendar Event'))
->setIcon('fa-calendar')
->setHref($this->getBaseURI().'event/create/');
$items[] = $item;
return $items;
}
public function getMailCommandObjects() {
return array(
'event' => array(
'name' => pht('Email Commands: Events'),
'header' => pht('Interacting with Calendar Events'),
'object' => new PhabricatorCalendarEvent(),
'summary' => pht(
'This page documents the commands you can use to interact with '.
'events in Calendar. These commands work when creating new tasks '.
'via email and when replying to existing tasks.'),
),
);
}
}
diff --git a/src/applications/calendar/controller/PhabricatorCalendarController.php b/src/applications/calendar/controller/PhabricatorCalendarController.php
index 2e088ecf49..a17a96784f 100644
--- a/src/applications/calendar/controller/PhabricatorCalendarController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarController.php
@@ -1,29 +1,72 @@
<?php
abstract class PhabricatorCalendarController extends PhabricatorController {
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$actions = id(new PhabricatorActionListView())
->setUser($this->getViewer())
->addAction(
id(new PhabricatorActionView())
->setName(pht('Create Private Event'))
->setHref('/calendar/event/create/?mode=private'))
->addAction(
id(new PhabricatorActionView())
->setName(pht('Create Public Event'))
->setHref('/calendar/event/create/?mode=public'));
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('Create Event'))
->setHref($this->getApplicationURI().'event/create/')
->setIcon('fa-plus-square')
->setDropdownMenu($actions));
return $crumbs;
}
+ protected function getEventAtIndexForGhostPHID($viewer, $phid, $index) {
+ $result = id(new PhabricatorCalendarEventQuery())
+ ->setViewer($viewer)
+ ->withInstanceSequencePairs(
+ array(
+ array(
+ $phid,
+ $index,
+ ),
+ ))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+
+ return $result;
+ }
+
+ protected function createEventFromGhost($viewer, $event, $index) {
+ $invitees = $event->getInvitees();
+
+ $new_ghost = $event->generateNthGhost($index, $viewer);
+ $new_ghost->attachParentEvent($event);
+
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+ $new_ghost
+ ->setID(null)
+ ->setPHID(null)
+ ->removeViewerTimezone($viewer)
+ ->save();
+ $ghost_invitees = array();
+ foreach ($invitees as $invitee) {
+ $ghost_invitee = clone $invitee;
+ $ghost_invitee
+ ->setID(null)
+ ->setEventPHID($new_ghost->getPHID())
+ ->save();
+ }
+ unset($unguarded);
+ return $new_ghost;
+ }
}
diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventCancelController.php b/src/applications/calendar/controller/PhabricatorCalendarEventCancelController.php
index a3909e7207..8e3cfdb8fa 100644
--- a/src/applications/calendar/controller/PhabricatorCalendarEventCancelController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarEventCancelController.php
@@ -1,75 +1,130 @@
<?php
final class PhabricatorCalendarEventCancelController
extends PhabricatorCalendarController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
+ $sequence = $request->getURIData('sequence');
- $status = id(new PhabricatorCalendarEventQuery())
+ $event = id(new PhabricatorCalendarEventQuery())
->setViewer($user)
->withIDs(array($this->id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
- if (!$status) {
+ if ($sequence) {
+ $parent_event = $event;
+ $event = $parent_event->generateNthGhost($sequence, $user);
+ $event->attachParentEvent($parent_event);
+ }
+
+ if (!$event) {
return new Aphront404Response();
}
- $cancel_uri = '/E'.$status->getID();
+ if (!$sequence) {
+ $cancel_uri = '/E'.$event->getID();
+ } else {
+ $cancel_uri = '/E'.$event->getID().'/'.$sequence;
+ }
+
+ $is_cancelled = $event->getIsCancelled();
+ $is_parent_cancelled = $event->getIsParentCancelled();
+ $is_recurring = $event->getIsRecurring();
+ $instance_of = $event->getInstanceOfEventPHID();
+
$validation_exception = null;
- $is_cancelled = $status->getIsCancelled();
if ($request->isFormPost()) {
+ if ($is_cancelled && $sequence) {
+ return id(new AphrontRedirectResponse())->setURI($cancel_uri);
+ } else if ($sequence) {
+ $event = $this->createEventFromGhost(
+ $user,
+ $event,
+ $sequence);
+ $event->applyViewerTimezone($user);
+ }
+
$xactions = array();
$xaction = id(new PhabricatorCalendarEventTransaction())
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_CANCEL)
->setNewValue(!$is_cancelled);
$editor = id(new PhabricatorCalendarEventEditor())
->setActor($user)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
try {
- $editor->applyTransactions($status, array($xaction));
+ $editor->applyTransactions($event, array($xaction));
return id(new AphrontRedirectResponse())->setURI($cancel_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
}
}
if ($is_cancelled) {
- $title = pht('Reinstate Event');
- $paragraph = pht('Reinstate this event?');
- $cancel = pht('Don\'t Reinstate Event');
- $submit = pht('Reinstate Event');
+ if ($sequence || $is_parent_cancelled) {
+ $title = pht('Cannot Reinstate Instance');
+ $paragraph = pht('Cannot reinstate an instance of a
+ cancelled recurring event.');
+ $cancel = pht('Cancel');
+ $submit = null;
+ } else if ($is_recurring && !$instance_of) {
+ $title = pht('Reinstate Recurrence');
+ $paragraph = pht('Reinstate the entire series
+ of recurring events?');
+ $cancel = pht('Don\'t Reinstate Recurrence');
+ $submit = pht('Reinstate Recurrence');
+ } else {
+ $title = pht('Reinstate Event');
+ $paragraph = pht('Reinstate this event?');
+ $cancel = pht('Don\'t Reinstate Event');
+ $submit = pht('Reinstate Event');
+ }
} else {
- $title = pht('Cancel Event');
- $paragraph = pht('You can always reinstate the event later.');
- $cancel = pht('Don\'t Cancel Event');
- $submit = pht('Cancel Event');
+ if ($sequence) {
+ $title = pht('Cancel Instance');
+ $paragraph = pht('Cancel just this instance
+ of a recurring event.');
+ $cancel = pht('Don\'t Cancel Instance');
+ $submit = pht('Cancel Instance');
+ } else if ($is_recurring && !$instance_of) {
+ $title = pht('Cancel Recurrence');
+ $paragraph = pht('Cancel the entire series
+ of recurring events?');
+ $cancel = pht('Don\'t Cancel Recurrence');
+ $submit = pht('Cancel Recurrence');
+ } else {
+ $title = pht('Cancel Event');
+ $paragraph = pht('You can always reinstate
+ the event later.');
+ $cancel = pht('Don\'t Cancel Event');
+ $submit = pht('Cancel Event');
+ }
}
return $this->newDialog()
->setTitle($title)
->setValidationException($validation_exception)
->appendParagraph($paragraph)
->addCancelButton($cancel_uri, $cancel)
->addSubmitButton($submit);
}
}
diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php
index 5929ca9428..bb5812a09a 100644
--- a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php
@@ -1,560 +1,537 @@
<?php
final class PhabricatorCalendarEventEditController
extends PhabricatorCalendarController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function isCreate() {
return !$this->id;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$user_phid = $viewer->getPHID();
$error_name = true;
$error_recurrence_end_date = true;
$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();
+ ->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 = id(new PhabricatorCalendarEventQuery())
- ->setViewer($viewer)
- ->withInstanceSequencePairs(
- array(
- array(
- $event->getPHID(),
- $index,
- ),
- ))
- ->requireCapabilities(
- array(
- PhabricatorPolicyCapability::CAN_VIEW,
- PhabricatorPolicyCapability::CAN_EDIT,
- ))
- ->executeOne();
+ $result = $this->getEventAtIndexForGhostPHID(
+ $viewer,
+ $event->getPHID(),
+ $index);
if ($result) {
return id(new AphrontRedirectResponse())
->setURI('/calendar/event/edit/'.$result->getID().'/');
}
- $invitees = $event->getInvitees();
-
- $new_ghost = $event->generateNthGhost($index, $viewer);
- $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
- $new_ghost
- ->setID(null)
- ->setPHID(null)
- ->removeViewerTimezone($viewer)
- ->save();
- $ghost_invitees = array();
- foreach ($invitees as $invitee) {
- $ghost_invitee = clone $invitee;
- $ghost_invitee
- ->setID(null)
- ->setEventPHID($new_ghost->getPHID())
- ->save();
- }
- unset($unguarded);
+ $event = $this->createEventFromGhost(
+ $viewer,
+ $event,
+ $index);
+
return id(new AphrontRedirectResponse())
- ->setURI('/calendar/event/edit/'.$new_ghost->getID().'/');
+ ->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();
}
$name = $event->getName();
$description = $event->getDescription();
$is_all_day = $event->getIsAllDay();
$is_recurring = $event->getIsRecurring();
$frequency = idx($event->getRecurrenceFrequency(), 'rule');
$icon = $event->getIcon();
$current_policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($event)
->execute();
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);
$description = $request->getStr('description');
$subscribers = $request->getArr('subscribers');
$edit_policy = $request->getStr('editPolicy');
$view_policy = $request->getStr('viewPolicy');
$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 ($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));
$xactions[] = id(new PhabricatorCalendarEventTransaction())
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE)
->setNewValue($recurrence_end_date_value);
}
$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'));
$editor = id(new PhabricatorCalendarEventEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true);
try {
$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);
$event->setViewPolicy($view_policy);
$event->setEditPolicy($edit_policy);
}
}
$is_recurring_checkbox = null;
$recurrence_end_date_control = null;
$recurrence_frequency_select = null;
$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)
->setAllowNull(true)
->setIsDisabled(!$is_recurring);
$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(!$is_recurring);
}
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);
$description = id(new AphrontFormTextAreaControl())
->setLabel(pht('Description'))
->setName('description')
->setValue($description);
$view_policies = id(new AphrontFormPolicyControl())
->setUser($viewer)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicyObject($event)
->setPolicies($current_policies)
->setName('viewPolicy');
$edit_policies = id(new AphrontFormPolicyControl())
->setUser($viewer)
->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 ($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($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 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/controller/PhabricatorCalendarEventViewController.php b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
index c397340395..2cbc8dcdb2 100644
--- a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
@@ -1,326 +1,364 @@
<?php
final class PhabricatorCalendarEventViewController
extends PhabricatorCalendarController {
private $id;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$sequence = $request->getURIData('sequence');
$event = id(new PhabricatorCalendarEventQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->executeOne();
if (!$event) {
return new Aphront404Response();
}
- if ($sequence && $event->getIsRecurring()) {
- $parent_event = $event;
- $event = $event->generateNthGhost($sequence, $viewer);
- $event->attachParentEvent($parent_event);
- } else if ($sequence) {
- return new Aphront404Response();
+ if ($sequence) {
+ $result = $this->getEventAtIndexForGhostPHID(
+ $viewer,
+ $event->getPHID(),
+ $sequence);
+
+ if ($result) {
+ $parent_event = $event;
+ $event = $result;
+ $event->attachParentEvent($parent_event);
+ return id(new AphrontRedirectResponse())
+ ->setURI('/E'.$result->getID());
+ } else if ($sequence && $event->getIsRecurring()) {
+ $parent_event = $event;
+ $event = $event->generateNthGhost($sequence, $viewer);
+ $event->attachParentEvent($parent_event);
+ } else if ($sequence) {
+ return new Aphront404Response();
+ }
}
$title = 'E'.$event->getID();
$page_title = $title.' '.$event->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($title, '/E'.$event->getID());
$timeline = $this->buildTransactionTimeline(
$event,
new PhabricatorCalendarEventTransactionQuery());
$header = $this->buildHeaderView($event);
$actions = $this->buildActionView($event);
$properties = $this->buildPropertyView($event);
$properties->setActionList($actions);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$add_comment_header = $is_serious
? pht('Add Comment')
: pht('Add To Plate');
$draft = PhabricatorDraft::newFromUserAndKey($viewer, $event->getPHID());
$add_comment_form = id(new PhabricatorApplicationTransactionCommentView())
->setUser($viewer)
->setObjectPHID($event->getPHID())
->setDraft($draft)
->setHeaderText($add_comment_header)
->setAction(
$this->getApplicationURI('/event/comment/'.$event->getID().'/'))
->setSubmitButtonName(pht('Add Comment'));
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$timeline,
$add_comment_form,
),
array(
'title' => $page_title,
));
}
private function buildHeaderView(PhabricatorCalendarEvent $event) {
$viewer = $this->getRequest()->getUser();
$id = $event->getID();
$is_cancelled = $event->getIsCancelled();
$icon = $is_cancelled ? ('fa-times') : ('fa-calendar');
$color = $is_cancelled ? ('grey') : ('green');
$status = $is_cancelled ? pht('Cancelled') : pht('Active');
$invite_status = $event->getUserInviteStatus($viewer->getPHID());
$status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED;
$is_invite_pending = ($invite_status == $status_invited);
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($event->getName())
->setStatus($icon, $color, $status)
->setPolicyObject($event);
if ($is_invite_pending) {
$decline_button = id(new PHUIButtonView())
->setTag('a')
->setIcon(id(new PHUIIconView())
->setIconFont('fa-times grey'))
->setHref($this->getApplicationURI("/event/decline/{$id}/"))
->setWorkflow(true)
->setText(pht('Decline'));
$accept_button = id(new PHUIButtonView())
->setTag('a')
->setIcon(id(new PHUIIconView())
->setIconFont('fa-check green'))
->setHref($this->getApplicationURI("/event/accept/{$id}/"))
->setWorkflow(true)
->setText(pht('Accept'));
$header->addActionLink($decline_button)
->addActionLink($accept_button);
}
return $header;
}
private function buildActionView(PhabricatorCalendarEvent $event) {
$viewer = $this->getRequest()->getUser();
$id = $event->getID();
$is_cancelled = $event->getIsCancelled();
$is_attending = $event->getIsUserAttending($viewer->getPHID());
$actions = id(new PhabricatorActionListView())
->setObjectURI($this->getApplicationURI('event/'.$id.'/'))
->setUser($viewer)
->setObject($event);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$event,
PhabricatorPolicyCapability::CAN_EDIT);
$edit_label = false;
$edit_uri = false;
if ($event->getIsGhostEvent()) {
$index = $event->getSequenceIndex();
$edit_label = pht('Edit This Instance');
$edit_uri = "event/edit/{$id}/{$index}/";
} else if ($event->getInstanceOfEventPHID() && !$event->getIsGhostEvent()) {
$edit_label = pht('Edit This Instance');
$edit_uri = "event/edit/{$id}/";
} else if (!$event->getIsRecurring()) {
$edit_label = pht('Edit');
$edit_uri = "event/edit/{$id}/";
}
if ($edit_label && $edit_uri) {
$actions->addAction(
id(new PhabricatorActionView())
->setName($edit_label)
->setIcon('fa-pencil')
->setHref($this->getApplicationURI($edit_uri))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
}
if ($is_attending) {
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Decline Event'))
->setIcon('fa-user-times')
->setHref($this->getApplicationURI("event/join/{$id}/"))
->setWorkflow(true));
} else {
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Join Event'))
->setIcon('fa-user-plus')
->setHref($this->getApplicationURI("event/join/{$id}/"))
->setWorkflow(true));
}
+ $cancel_uri = $this->getApplicationURI("event/cancel/{$id}/");
+
+ if ($event->getIsGhostEvent()) {
+ $index = $event->getSequenceIndex();
+ $can_reinstate = $event->getIsParentCancelled();
+
+ $cancel_label = pht('Cancel This Instance');
+ $reinstate_label = pht('Reinstate This Instance');
+ $cancel_disabled = (!$can_edit || $can_reinstate);
+ $cancel_uri = $this->getApplicationURI("event/cancel/{$id}/{$index}/");
+ } else if ($event->getInstanceOfEventPHID()) {
+ $can_reinstate = $event->getIsParentCancelled();
+ $cancel_label = pht('Cancel This Instance');
+ $reinstate_label = pht('Reinstate This Instance');
+ $cancel_disabled = (!$can_edit || $can_reinstate);
+ } else if ($event->getIsRecurring()) {
+ $cancel_label = pht('Cancel Recurrence');
+ $reinstate_label = pht('Reinstate Recurrence');
+ $cancel_disabled = !$can_edit;
+ } else {
+ $cancel_label = pht('Cancel Event');
+ $reinstate_label = pht('Reinstate Event');
+ $cancel_disabled = !$can_edit;
+ }
+
if ($is_cancelled) {
$actions->addAction(
id(new PhabricatorActionView())
- ->setName(pht('Reinstate Event'))
+ ->setName($reinstate_label)
->setIcon('fa-plus')
- ->setHref($this->getApplicationURI("event/cancel/{$id}/"))
- ->setDisabled(!$can_edit)
+ ->setHref($cancel_uri)
+ ->setDisabled($cancel_disabled)
->setWorkflow(true));
} else {
$actions->addAction(
id(new PhabricatorActionView())
- ->setName(pht('Cancel Event'))
+ ->setName($cancel_label)
->setIcon('fa-times')
- ->setHref($this->getApplicationURI("event/cancel/{$id}/"))
- ->setDisabled(!$can_edit)
+ ->setHref($cancel_uri)
+ ->setDisabled($cancel_disabled)
->setWorkflow(true));
}
return $actions;
}
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'),
$viewer->renderHandle($event->getInstanceOfEventPHID()));
}
}
$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);
$properties->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$properties->addTextContent($event->getDescription());
return $properties;
}
}
diff --git a/src/applications/calendar/storage/PhabricatorCalendarEvent.php b/src/applications/calendar/storage/PhabricatorCalendarEvent.php
index 1beff60851..73dee4e399 100644
--- a/src/applications/calendar/storage/PhabricatorCalendarEvent.php
+++ b/src/applications/calendar/storage/PhabricatorCalendarEvent.php
@@ -1,506 +1,510 @@
<?php
final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
implements PhabricatorPolicyInterface,
PhabricatorMarkupInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorSubscribableInterface,
PhabricatorTokenReceiverInterface,
PhabricatorDestructibleInterface,
PhabricatorMentionableInterface,
PhabricatorFlaggableInterface {
protected $name;
protected $userPHID;
protected $dateFrom;
protected $dateTo;
protected $description;
protected $isCancelled;
protected $isAllDay;
protected $icon;
protected $mailKey;
protected $isRecurring = 0;
protected $recurrenceFrequency = array();
protected $recurrenceEndDate;
private $isGhostEvent = false;
protected $instanceOfEventPHID;
protected $sequenceIndex;
protected $viewPolicy;
protected $editPolicy;
const DEFAULT_ICON = 'fa-calendar';
private $parentEvent = self::ATTACHABLE;
private $invitees = self::ATTACHABLE;
private $appliedViewer;
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();
} else if ($mode == 'recurring') {
$is_recurring = true;
} else {
$view_policy = $actor->getPHID();
}
return id(new PhabricatorCalendarEvent())
->setUserPHID($actor->getPHID())
->setIsCancelled(0)
->setIsAllDay(0)
->setIsRecurring($is_recurring)
->setIcon(self::DEFAULT_ICON)
->setViewPolicy($view_policy)
->setEditPolicy($actor->getPHID())
->attachInvitees(array())
->applyViewerTimezone($actor);
}
public function applyViewerTimezone(PhabricatorUser $viewer) {
if ($this->appliedViewer) {
throw new Exception(pht('Viewer timezone is already applied!'));
}
$this->appliedViewer = $viewer;
if (!$this->getIsAllDay()) {
return $this;
}
$zone = $viewer->getTimeZone();
$this->setDateFrom(
$this->getDateEpochForTimeZone(
$this->getDateFrom(),
new DateTimeZone('Pacific/Kiritimati'),
'Y-m-d',
null,
$zone));
$this->setDateTo(
$this->getDateEpochForTimeZone(
$this->getDateTo(),
new DateTimeZone('Pacific/Midway'),
'Y-m-d 23:59:00',
'-1 day',
$zone));
return $this;
}
public function removeViewerTimezone(PhabricatorUser $viewer) {
if (!$this->appliedViewer) {
throw new Exception(pht('Viewer timezone is not applied!'));
}
if ($viewer->getPHID() != $this->appliedViewer->getPHID()) {
throw new Exception(pht('Removed viewer must match applied viewer!'));
}
$this->appliedViewer = null;
if (!$this->getIsAllDay()) {
return $this;
}
$zone = $viewer->getTimeZone();
$this->setDateFrom(
$this->getDateEpochForTimeZone(
$this->getDateFrom(),
$zone,
'Y-m-d',
null,
new DateTimeZone('Pacific/Kiritimati')));
$this->setDateTo(
$this->getDateEpochForTimeZone(
$this->getDateTo(),
$zone,
'Y-m-d',
'+1 day',
new DateTimeZone('Pacific/Midway')));
return $this;
}
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->appliedViewer) {
throw new Exception(
pht(
'Can not save event with viewer timezone still applied!'));
}
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. 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->getDateFrom() - 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?',
),
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 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 generateNthGhost(
$sequence_index,
PhabricatorUser $actor) {
$frequency = $this->getFrequencyUnit();
$modify_key = '+'.$sequence_index.' '.$frequency;
$instance_of = ($this->getPHID()) ?
$this->getPHID() : $this->instanceOfEventPHID;
$date = $this->dateFrom;
$date_time = PhabricatorTime::getDateTimeFromEpoch($date, $actor);
$date_time->modify($modify_key);
$date = $date_time->format('U');
$duration = $this->dateTo - $this->dateFrom;
$edit_policy = PhabricatorPolicies::POLICY_NOONE;
$ghost_event = id(clone $this)
->setIsGhostEvent(true)
->setDateFrom($date)
->setDateTo($date + $duration)
->setIsRecurring(true)
->setRecurrenceFrequency($this->recurrenceFrequency)
->setInstanceOfEventPHID($instance_of)
->setSequenceIndex($sequence_index)
->setEditPolicy($edit_policy);
return $ghost_event;
}
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 'yearly';
default:
return 'day';
}
}
public function getURI() {
$uri = '/'.$this->getMonogram();
if ($this->isGhostEvent) {
$uri = $uri.'/'.$this->sequenceIndex;
}
return $uri;
}
public function getParentEvent() {
return $this->assertAttached($this->parentEvent);
}
public function attachParentEvent($event) {
$this->parentEvent = $event;
return $this;
}
public function getIsCancelled() {
$instance_of = $this->instanceOfEventPHID;
if ($instance_of != null && $this->getIsParentCancelled()) {
return true;
}
return $this->isCancelled;
}
public function getIsParentCancelled() {
+ if ($this->instanceOfEventPHID == null) {
+ return false;
+ }
+
$recurring_event = $this->getParentEvent();
if ($recurring_event->getIsCancelled()) {
return true;
}
return false;
}
/* -( 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());
}
public function shouldShowSubscribersProperty() {
return true;
}
public function shouldAllowSubscription($phid) {
return true;
}
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
public function getUsersToNotifyOfTokenGiven() {
return array($this->getUserPHID());
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$this->delete();
$this->saveTransaction();
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 16:01 (2 w, 6 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1126286
Default Alt Text
(55 KB)

Event Timeline