Page MenuHomePhabricator

D16248.diff
No OneTemporary

D16248.diff

diff --git a/resources/sql/autopatches/20160707.calendar.01.stub.sql b/resources/sql/autopatches/20160707.calendar.01.stub.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20160707.calendar.01.stub.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_calendar.calendar_event
+ ADD isStub BOOL NOT NULL;
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2022,7 +2022,6 @@
'PhabricatorCalendarEditEngine' => 'applications/calendar/editor/PhabricatorCalendarEditEngine.php',
'PhabricatorCalendarEvent' => 'applications/calendar/storage/PhabricatorCalendarEvent.php',
'PhabricatorCalendarEventCancelController' => 'applications/calendar/controller/PhabricatorCalendarEventCancelController.php',
- 'PhabricatorCalendarEventCommentController' => 'applications/calendar/controller/PhabricatorCalendarEventCommentController.php',
'PhabricatorCalendarEventDragController' => 'applications/calendar/controller/PhabricatorCalendarEventDragController.php',
'PhabricatorCalendarEventEditController' => 'applications/calendar/controller/PhabricatorCalendarEventEditController.php',
'PhabricatorCalendarEventEditProController' => 'applications/calendar/controller/PhabricatorCalendarEventEditProController.php',
@@ -2994,7 +2993,6 @@
'PhabricatorPeopleAnyOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php',
'PhabricatorPeopleApplication' => 'applications/people/application/PhabricatorPeopleApplication.php',
'PhabricatorPeopleApproveController' => 'applications/people/controller/PhabricatorPeopleApproveController.php',
- 'PhabricatorPeopleCalendarController' => 'applications/people/controller/PhabricatorPeopleCalendarController.php',
'PhabricatorPeopleController' => 'applications/people/controller/PhabricatorPeopleController.php',
'PhabricatorPeopleCreateController' => 'applications/people/controller/PhabricatorPeopleCreateController.php',
'PhabricatorPeopleDatasource' => 'applications/people/typeahead/PhabricatorPeopleDatasource.php',
@@ -6633,7 +6631,6 @@
'PhabricatorFulltextInterface',
),
'PhabricatorCalendarEventCancelController' => 'PhabricatorCalendarController',
- 'PhabricatorCalendarEventCommentController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventDragController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventEditController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventEditProController' => 'ManiphestController',
@@ -7741,7 +7738,6 @@
'PhabricatorPeopleAnyOwnerDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorPeopleApplication' => 'PhabricatorApplication',
'PhabricatorPeopleApproveController' => 'PhabricatorPeopleController',
- 'PhabricatorPeopleCalendarController' => 'PhabricatorPeopleProfileController',
'PhabricatorPeopleController' => 'PhabricatorController',
'PhabricatorPeopleCreateController' => 'PhabricatorPeopleController',
'PhabricatorPeopleDatasource' => 'PhabricatorTypeaheadDatasource',
diff --git a/src/applications/calendar/application/PhabricatorCalendarApplication.php b/src/applications/calendar/application/PhabricatorCalendarApplication.php
--- a/src/applications/calendar/application/PhabricatorCalendarApplication.php
+++ b/src/applications/calendar/application/PhabricatorCalendarApplication.php
@@ -40,7 +40,7 @@
public function getRoutes() {
return array(
- '/E(?P<id>[1-9]\d*)(?:/(?P<sequence>\d+))?'
+ '/E(?P<id>[1-9]\d*)(?:/(?P<sequence>\d+)/)?'
=> 'PhabricatorCalendarEventViewController',
'/calendar/' => array(
'(?:query/(?P<queryKey>[^/]+)/(?:(?P<year>\d+)/'.
@@ -51,15 +51,15 @@
=> 'PhabricatorCalendarEventEditProController',
'create/'
=> 'PhabricatorCalendarEventEditController',
- 'edit/(?P<id>[1-9]\d*)/(?:(?P<sequence>\d+)/)?'
+ 'edit/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventEditController',
'drag/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventDragController',
- 'cancel/(?P<id>[1-9]\d*)/(?:(?P<sequence>\d+)/)?'
+ 'cancel/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventCancelController',
'(?P<action>join|decline|accept)/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventJoinController',
- 'comment/(?P<id>[1-9]\d*)/(?:(?P<sequence>\d+)/)?'
+ 'comment/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventCommentController',
),
),
diff --git a/src/applications/calendar/controller/PhabricatorCalendarController.php b/src/applications/calendar/controller/PhabricatorCalendarController.php
--- a/src/applications/calendar/controller/PhabricatorCalendarController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarController.php
@@ -30,49 +30,4 @@
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)
- ->setViewPolicy($event->getViewPolicy())
- ->setEditPolicy($event->getEditPolicy())
- ->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
--- a/src/applications/calendar/controller/PhabricatorCalendarEventCancelController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarEventCancelController.php
@@ -6,7 +6,6 @@
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
- $sequence = $request->getURIData('sequence');
$event = id(new PhabricatorCalendarEventQuery())
->setViewer($viewer)
@@ -17,40 +16,24 @@
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
-
- if ($sequence) {
- $parent_event = $event;
- $event = $parent_event->generateNthGhost($sequence, $viewer);
- $event->attachParentEvent($parent_event);
- }
-
if (!$event) {
return new Aphront404Response();
}
- if (!$sequence) {
- $cancel_uri = '/E'.$event->getID();
- } else {
- $cancel_uri = '/E'.$event->getID().'/'.$sequence;
- }
+ $cancel_uri = $event->getURI();
+ $is_parent = $event->isParentEvent();
+ $is_child = $event->isChildEvent();
$is_cancelled = $event->getIsCancelled();
- $is_parent_cancelled = $event->getIsParentCancelled();
- $is_parent = $event->getIsRecurrenceParent();
- $validation_exception = null;
+ if ($is_child) {
+ $is_parent_cancelled = $event->getParentEvent()->getIsCancelled();
+ } else {
+ $is_parent_cancelled = false;
+ }
+ $validation_exception = null;
if ($request->isFormPost()) {
- if ($is_cancelled && $sequence) {
- return id(new AphrontRedirectResponse())->setURI($cancel_uri);
- } else if ($sequence) {
- $event = $this->createEventFromGhost(
- $viewer,
- $event,
- $sequence);
- $event->applyViewerTimezone($viewer);
- }
-
$xactions = array();
$xaction = id(new PhabricatorCalendarEventTransaction())
@@ -73,43 +56,47 @@
}
if ($is_cancelled) {
- if ($sequence || $is_parent_cancelled) {
+ if ($is_parent_cancelled) {
$title = pht('Cannot Reinstate Instance');
$paragraph = pht(
- 'Cannot reinstate an instance of a cancelled recurring event.');
- $cancel = pht('Cancel');
+ 'You cannot reinstate an instance of a cancelled recurring event.');
+ $cancel = pht('Back');
$submit = null;
+ } else if ($is_child) {
+ $title = pht('Reinstate Instance');
+ $paragraph = pht(
+ 'Reinstate this instance of this recurring event?');
+ $cancel = pht('Back');
+ $submit = pht('Reinstate Instance');
} else if ($is_parent) {
- $title = pht('Reinstate Recurrence');
+ $title = pht('Reinstate Recurring Event');
$paragraph = pht(
- 'Reinstate all instances of this recurrence
- that have not been individually cancelled?');
- $cancel = pht("Don't Reinstate Recurrence");
- $submit = pht('Reinstate Recurrence');
+ 'Reinstate all instances of this recurring event which have not '.
+ 'been individually cancelled?');
+ $cancel = pht('Back');
+ $submit = pht('Reinstate Recurring Event');
} else {
$title = pht('Reinstate Event');
$paragraph = pht('Reinstate this event?');
- $cancel = pht("Don't Reinstate Event");
+ $cancel = pht('Back');
$submit = pht('Reinstate Event');
}
} else {
- if ($sequence) {
+ if ($is_child) {
$title = pht('Cancel Instance');
- $paragraph = pht(
- 'Cancel just this instance of a recurring event.');
- $cancel = pht("Don't Cancel Instance");
+ $paragraph = pht('Cancel this instance of this recurring event?');
+ $cancel = pht('Back');
$submit = pht('Cancel Instance');
} else if ($is_parent) {
- $title = pht('Cancel Recurrence');
- $paragraph = pht(
- 'Cancel the entire series of recurring events?');
- $cancel = pht("Don't Cancel Recurrence");
- $submit = pht('Cancel Recurrence');
+ $title = pht('Cancel Recurrin Event');
+ $paragraph = pht('Cancel this entire series of recurring events?');
+ $cancel = pht('Back');
+ $submit = pht('Cancel Recurring Event');
} else {
$title = pht('Cancel Event');
$paragraph = pht(
- 'You can always reinstate the event later.');
- $cancel = pht("Don't Cancel Event");
+ 'Cancel this event? You can always reinstate the event later.');
+ $cancel = pht('Back');
$submit = pht('Cancel Event');
}
}
diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventCommentController.php b/src/applications/calendar/controller/PhabricatorCalendarEventCommentController.php
deleted file mode 100644
--- a/src/applications/calendar/controller/PhabricatorCalendarEventCommentController.php
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php
-
-final class PhabricatorCalendarEventCommentController
- extends PhabricatorCalendarController {
-
- public function handleRequest(AphrontRequest $request) {
- if (!$request->isFormPost()) {
- return new Aphront400Response();
- }
-
- $viewer = $request->getViewer();
- $id = $request->getURIData('id');
-
- $is_preview = $request->isPreviewRequest();
- $draft = PhabricatorDraft::buildFromRequest($request);
-
- $event = id(new PhabricatorCalendarEventQuery())
- ->setViewer($viewer)
- ->withIDs(array($id))
- ->executeOne();
- if (!$event) {
- return new Aphront404Response();
- }
-
- $index = $request->getURIData('sequence');
- if ($index && !$is_preview) {
- $result = $this->getEventAtIndexForGhostPHID(
- $viewer,
- $event->getPHID(),
- $index);
-
- if ($result) {
- $event = $result;
- } else {
- $event = $this->createEventFromGhost(
- $viewer,
- $event,
- $index);
- $event->applyViewerTimezone($viewer);
- }
- }
-
- $view_uri = '/'.$event->getMonogram();
-
- $xactions = array();
- $xactions[] = id(new PhabricatorCalendarEventTransaction())
- ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
- ->attachComment(
- id(new PhabricatorCalendarEventTransactionComment())
- ->setContent($request->getStr('comment')));
-
- $editor = id(new PhabricatorCalendarEventEditor())
- ->setActor($viewer)
- ->setContinueOnNoEffect($request->isContinueRequest())
- ->setContentSourceFromRequest($request)
- ->setIsPreview($is_preview);
-
- try {
- $xactions = $editor->applyTransactions($event, $xactions);
- } catch (PhabricatorApplicationTransactionNoEffectException $ex) {
- return id(new PhabricatorApplicationTransactionNoEffectResponse())
- ->setCancelURI($view_uri)
- ->setException($ex);
- }
-
- if ($draft) {
- $draft->replaceOrDelete();
- }
-
- if ($request->isAjax() && $is_preview) {
- return id(new PhabricatorApplicationTransactionResponse())
- ->setViewer($viewer)
- ->setTransactions($xactions)
- ->setIsPreview($is_preview);
- } else {
- return id(new AphrontRedirectResponse())
- ->setURI($view_uri);
- }
- }
-
-}
diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php
--- a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php
@@ -77,41 +77,18 @@
$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 = $this->getEventAtIndexForGhostPHID(
- $viewer,
- $event->getPHID(),
- $index);
-
- if ($result) {
- return id(new AphrontRedirectResponse())
- ->setURI('/calendar/event/edit/'.$result->getID().'/');
- }
-
- $event = $this->createEventFromGhost(
- $viewer,
- $event,
- $index);
-
- return id(new AphrontRedirectResponse())
- ->setURI('/calendar/event/edit/'.$event->getID().'/');
- }
-
$end_value = AphrontFormDateControlValue::newFromEpoch(
$viewer,
$event->getDateTo());
@@ -137,7 +114,7 @@
}
}
- $cancel_uri = '/'.$event->getMonogram();
+ $cancel_uri = $event->getURI();
}
if ($this->isCreate()) {
@@ -153,7 +130,7 @@
$description = $event->getDescription();
$is_all_day = $event->getIsAllDay();
$is_recurring = $event->getIsRecurring();
- $is_parent = $event->getIsRecurrenceParent();
+ $is_parent = $event->isParentEvent();
$frequency = idx($event->getRecurrenceFrequency(), 'rule');
$icon = $event->getIcon();
$edit_policy = $event->getEditPolicy();
diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventJoinController.php b/src/applications/calendar/controller/PhabricatorCalendarEventJoinController.php
--- a/src/applications/calendar/controller/PhabricatorCalendarEventJoinController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarEventJoinController.php
@@ -20,12 +20,11 @@
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
-
if (!$event) {
return new Aphront404Response();
}
- $cancel_uri = '/E'.$event->getID();
+ $cancel_uri = $event->getURI();
$validation_exception = null;
$is_attending = $event->getIsUserAttending($viewer->getPHID());
diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
--- a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
@@ -9,89 +9,48 @@
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
- $id = $request->getURIData('id');
- $sequence = $request->getURIData('sequence');
- $timeline = null;
-
- $event = id(new PhabricatorCalendarEventQuery())
- ->setViewer($viewer)
- ->withIDs(array($id))
- ->executeOne();
+ $event = $this->loadEvent();
if (!$event) {
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 = $event->getMonogram().' ('.$sequence.')';
- $page_title = $title.' '.$event->getName();
- $crumbs = $this->buildApplicationCrumbs();
- $crumbs->addTextCrumb($title, '/'.$event->getMonogram().'/'.$sequence);
-
-
- } else {
- $title = 'E'.$event->getID();
- $page_title = $title.' '.$event->getName();
- $crumbs = $this->buildApplicationCrumbs();
- $crumbs->addTextCrumb($title);
- $crumbs->setBorder(true);
+ // If we looked up or generated a stub event, redirect to that event's
+ // canonical URI.
+ $id = $request->getURIData('id');
+ if ($event->getID() != $id) {
+ $uri = $event->getURI();
+ return id(new AphrontRedirectResponse())->setURI($uri);
}
- if (!$event->getIsGhostEvent()) {
- $timeline = $this->buildTransactionTimeline(
- $event,
- new PhabricatorCalendarEventTransactionQuery());
- }
+ $monogram = $event->getMonogram();
+ $page_title = $monogram.' '.$event->getName();
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb($monogram);
+ $crumbs->setBorder(true);
+
+ $timeline = $this->buildTransactionTimeline(
+ $event,
+ new PhabricatorCalendarEventTransactionQuery());
$header = $this->buildHeaderView($event);
$curtain = $this->buildCurtain($event);
$details = $this->buildPropertySection($event);
$description = $this->buildDescriptionView($event);
- $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());
- if ($sequence) {
- $comment_uri = $this->getApplicationURI(
- '/event/comment/'.$event->getID().'/'.$sequence.'/');
- } else {
- $comment_uri = $this->getApplicationURI(
- '/event/comment/'.$event->getID().'/');
- }
- $add_comment_form = id(new PhabricatorApplicationTransactionCommentView())
- ->setUser($viewer)
- ->setObjectPHID($event->getPHID())
- ->setDraft($draft)
- ->setHeaderText($add_comment_header)
- ->setAction($comment_uri)
- ->setSubmitButtonName(pht('Add Comment'));
+ $comment_view = id(new PhabricatorCalendarEditEngine())
+ ->setViewer($viewer)
+ ->buildEditEngineCommentView($event);
+
+ $timeline->setQuoteRef($monogram);
+ $comment_view->setTransactionTimeline($timeline);
$view = id(new PHUITwoColumnView())
->setHeader($header)
- ->setMainColumn(array(
+ ->setMainColumn(
+ array(
$timeline,
- $add_comment_form,
+ $comment_view,
))
->setCurtain($curtain)
->addPropertySection(pht('Details'), $details)
@@ -101,10 +60,7 @@
->setTitle($page_title)
->setCrumbs($crumbs)
->setPageObjectPHIDs(array($event->getPHID()))
- ->appendChild(
- array(
- $view,
- ));
+ ->appendChild($view);
}
private function buildHeaderView(
@@ -152,7 +108,7 @@
private function buildCurtain(PhabricatorCalendarEvent $event) {
$viewer = $this->getRequest()->getUser();
$id = $event->getID();
- $is_cancelled = $event->getIsCancelled();
+ $is_cancelled = $event->isCancelledEvent();
$is_attending = $event->getIsUserAttending($viewer->getPHID());
$can_edit = PhabricatorPolicyFilter::hasCapability(
@@ -160,19 +116,11 @@
$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->getIsRecurrenceException()) {
+ $edit_uri = "event/edit/{$id}/";
+ if ($event->isChildEvent()) {
$edit_label = pht('Edit This Instance');
- $edit_uri = "event/edit/{$id}/";
} else {
$edit_label = pht('Edit');
- $edit_uri = "event/edit/{$id}/";
}
$curtain = $this->newCurtainView($event);
@@ -204,28 +152,21 @@
}
$cancel_uri = $this->getApplicationURI("event/cancel/{$id}/");
+ $cancel_disabled = !$can_edit;
- if ($event->getIsGhostEvent()) {
- $index = $event->getSequenceIndex();
- $can_reinstate = $event->getIsParentCancelled();
-
+ if ($event->isChildEvent()) {
$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->getIsRecurrenceException()) {
- $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->getIsRecurrenceParent()) {
+
+ if ($event->getParentEvent()->getIsCancelled()) {
+ $cancel_disabled = true;
+ }
+ } else if ($event->isParentEvent()) {
$cancel_label = pht('Cancel All');
$reinstate_label = pht('Reinstate All');
- $cancel_disabled = !$can_edit;
} else {
$cancel_label = pht('Cancel Event');
$reinstate_label = pht('Reinstate Event');
- $cancel_disabled = !$can_edit;
}
if ($is_cancelled) {
@@ -385,4 +326,68 @@
return null;
}
+
+ private function loadEvent() {
+ $request = $this->getRequest();
+ $viewer = $this->getViewer();
+
+ $id = $request->getURIData('id');
+ $sequence = $request->getURIData('sequence');
+
+ // We're going to figure out which event you're trying to look at. Most of
+ // the time this is simple, but you may be looking at an instance of a
+ // recurring event which we haven't generated an object for.
+
+ // If you are, we're going to generate a "stub" event so we have a real
+ // ID and PHID to work with, since the rest of the infrastructure relies
+ // on these identifiers existing.
+
+ // Load the event identified by ID first.
+ $event = id(new PhabricatorCalendarEventQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->executeOne();
+ if (!$event) {
+ return null;
+ }
+
+ // If we aren't looking at an instance of this event, this is a completely
+ // normal request and we can just return this event.
+ if (!$sequence) {
+ return $event;
+ }
+
+ // When you view "E123/999", E123 is normally the parent event. However,
+ // you might visit a different instance first instead and then fiddle
+ // with the URI. If the event we're looking at is a child, we are going
+ // to act on the parent instead.
+ if ($event->isChildEvent()) {
+ $event = $event->getParentEvent();
+ }
+
+ // Try to load the instance. If it already exists, we're all done and
+ // can just return it.
+ $instance = id(new PhabricatorCalendarEventQuery())
+ ->setViewer($viewer)
+ ->withInstanceSequencePairs(
+ array(
+ array($event->getPHID(), $sequence),
+ ))
+ ->executeOne();
+ if ($instance) {
+ return $instance;
+ }
+
+ if (!$viewer->isLoggedIn()) {
+ throw new Exception(
+ pht(
+ 'This event instance has not been created yet. Log in to create '.
+ 'it.'));
+ }
+
+ $instance = $event->newStub($viewer, $sequence);
+
+ return $instance;
+ }
+
}
diff --git a/src/applications/calendar/editor/PhabricatorCalendarEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarEditEngine.php
--- a/src/applications/calendar/editor/PhabricatorCalendarEditEngine.php
+++ b/src/applications/calendar/editor/PhabricatorCalendarEditEngine.php
@@ -55,6 +55,10 @@
return $object->getURI();
}
+ protected function getEditorURI() {
+ return $this->getApplication()->getApplicationURI('event/editpro/');
+ }
+
protected function buildCustomEditFields($object) {
$fields = array(
id(new PhabricatorTextEditField())
diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php
--- a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php
+++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php
@@ -11,6 +11,47 @@
return pht('Calendar');
}
+ protected function shouldApplyInitialEffects(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+ return true;
+ }
+
+ protected function applyInitialEffects(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+
+ $actor = $this->requireActor();
+ $object->removeViewerTimezone($actor);
+
+ 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();
+ foreach ($invitees as $invitee) {
+ $invitee = id(new PhabricatorCalendarEventInvitee())
+ ->setEventPHID($event->getPHID())
+ ->setInviteePHID($invitee->getInviteePHID())
+ ->setInviterPHID($invitee->getInviterPHID())
+ ->setStatus($invitee->getStatus())
+ ->save();
+ }
+
+ $event->save();
+ }
+
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
@@ -196,15 +237,6 @@
return parent::applyCustomExternalTransaction($object, $xaction);
}
- protected function didApplyInternalEffects(
- PhabricatorLiskDAO $object,
- array $xactions) {
-
- $object->removeViewerTimezone($this->requireActor());
-
- return $xactions;
- }
-
protected function applyFinalEffects(
PhabricatorLiskDAO $object,
array $xactions) {
diff --git a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php
--- a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php
+++ b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php
@@ -12,6 +12,7 @@
private $isCancelled;
private $eventsWithNoParent;
private $instanceSequencePairs;
+ private $isStub;
private $generateGhosts = false;
@@ -55,6 +56,11 @@
return $this;
}
+ public function withIsStub($is_stub) {
+ $this->isStub = $is_stub;
+ return $this;
+ }
+
public function withEventsWithNoParent($events_with_no_parent) {
$this->eventsWithNoParent = $events_with_no_parent;
return $this;
@@ -183,7 +189,7 @@
$sequence_start = max(1, $sequence_start);
for ($index = $sequence_start; $index < $sequence_end; $index++) {
- $events[] = $event->generateNthGhost($index, $viewer);
+ $events[] = $event->newGhost($viewer, $index);
}
// NOTE: We're slicing results every time because this makes it cheaper
@@ -201,40 +207,66 @@
}
}
- $map = array();
- $instance_sequence_pairs = array();
+ // Now that we're done generating ghost events, we're going to remove any
+ // ghosts that we have concrete events for (or which we can load the
+ // concrete events for). These concrete events are generated when users
+ // edit a ghost, and replace the ghost events.
- foreach ($events as $key => $event) {
+ // First, generate a map of all concrete <parentPHID, sequence> events we
+ // already loaded. We don't need to load these again.
+ $have_pairs = array();
+ foreach ($events as $event) {
if ($event->getIsGhostEvent()) {
- $index = $event->getSequenceIndex();
- $instance_sequence_pairs[] = array($event->getPHID(), $index);
- $map[$event->getPHID()][$index] = $key;
+ continue;
+ }
+
+ $parent_phid = $event->getInstanceOfEventPHID();
+ $sequence = $event->getSequenceIndex();
+
+ $have_pairs[$parent_phid][$sequence] = true;
+ }
+
+ // Now, generate a map of all <parentPHID, sequence> events we generated
+ // ghosts for. We need to try to load these if we don't already have them.
+ $map = array();
+ $parent_pairs = array();
+ foreach ($events as $key => $event) {
+ if (!$event->getIsGhostEvent()) {
+ continue;
+ }
+
+ $parent_phid = $event->getInstanceOfEventPHID();
+ $sequence = $event->getSequenceIndex();
+
+ // We already loaded the concrete version of this event, so we can just
+ // throw out the ghost and move on.
+ if (isset($have_pairs[$parent_phid][$sequence])) {
+ unset($events[$key]);
+ continue;
}
+
+ // We didn't load the concrete version of this event, so we need to
+ // try to load it if it exists.
+ $parent_pairs[] = array($parent_phid, $sequence);
+ $map[$parent_phid][$sequence] = $key;
}
- if (count($instance_sequence_pairs) > 0) {
- $sub_query = id(new PhabricatorCalendarEventQuery())
+ if ($parent_pairs) {
+ $instances = id(new self())
->setViewer($viewer)
->setParentQuery($this)
- ->withInstanceSequencePairs($instance_sequence_pairs)
+ ->withInstanceSequencePairs($parent_pairs)
->execute();
- foreach ($sub_query as $edited_ghost) {
- $indexes = idx($map, $edited_ghost->getInstanceOfEventPHID());
- $key = idx($indexes, $edited_ghost->getSequenceIndex());
- $events[$key] = $edited_ghost;
- }
+ foreach ($instances as $instance) {
+ $parent_phid = $instance->getInstanceOfEventPHID();
+ $sequence = $instance->getSequenceIndex();
- $id_map = array();
- foreach ($events as $key => $event) {
- if ($event->getIsGhostEvent()) {
- continue;
- }
- if (isset($id_map[$event->getID()])) {
- unset($events[$key]);
- } else {
- $id_map[$event->getID()] = true;
- }
+ $indexes = idx($map, $parent_phid);
+ $key = idx($indexes, $sequence);
+
+ // Replace the ghost with the corresponding concrete event.
+ $events[$key] = $instance;
}
}
@@ -329,6 +361,13 @@
implode(' OR ', $sql));
}
+ if ($this->isStub !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'event.isStub = %d',
+ (int)$this->isStub);
+ }
+
return $where;
}
diff --git a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php
--- a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php
+++ b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php
@@ -113,7 +113,15 @@
break;
}
- return $query->setGenerateGhosts(true);
+ // Generate ghosts (and ignore stub events) if we aren't querying for
+ // specific events.
+ if (!$map['ids'] && !$map['phids']) {
+ $query
+ ->withIsStub(false)
+ ->setGenerateGhosts(true);
+ }
+
+ return $query;
}
private function getQueryDateRange(
diff --git a/src/applications/calendar/storage/PhabricatorCalendarEvent.php b/src/applications/calendar/storage/PhabricatorCalendarEvent.php
--- a/src/applications/calendar/storage/PhabricatorCalendarEvent.php
+++ b/src/applications/calendar/storage/PhabricatorCalendarEvent.php
@@ -22,6 +22,7 @@
protected $isAllDay;
protected $icon;
protected $mailKey;
+ protected $isStub;
protected $isRecurring = 0;
protected $recurrenceFrequency = array();
@@ -71,6 +72,7 @@
->setUserPHID($actor->getPHID())
->setIsCancelled(0)
->setIsAllDay(0)
+ ->setIsStub(0)
->setIsRecurring($is_recurring)
->setIcon(self::DEFAULT_ICON)
->setViewPolicy($view_policy)
@@ -80,6 +82,116 @@
->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 = $parent->getDateTo() - $parent->getDateFrom();
+
+ $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 applyViewerTimezone(PhabricatorUser $viewer) {
if ($this->appliedViewer) {
throw new Exception(pht('Viewer timezone is already applied!'));
@@ -211,6 +323,7 @@
'recurrenceEndDate' => 'epoch?',
'instanceOfEventPHID' => 'phid?',
'sequenceIndex' => 'uint32?',
+ 'isStub' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
'userPHID_dateFrom' => array(
@@ -285,38 +398,6 @@
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');
@@ -335,11 +416,13 @@
}
public function getURI() {
- $uri = '/'.$this->getMonogram();
- if ($this->isGhostEvent) {
- $uri = $uri.'/'.$this->sequenceIndex;
+ if ($this->getIsGhostEvent()) {
+ $base = $this->getParentEvent()->getURI();
+ $sequence = $this->getSequenceIndex();
+ return "{$base}/{$sequence}/";
}
- return $uri;
+
+ return '/'.$this->getMonogram();
}
public function getParentEvent() {
@@ -351,37 +434,25 @@
return $this;
}
- public function getIsCancelled() {
- $instance_of = $this->instanceOfEventPHID;
- if ($instance_of != null && $this->getIsParentCancelled()) {
- return true;
- }
- return $this->isCancelled;
+ public function isParentEvent() {
+ return ($this->isRecurring && !$this->instanceOfEventPHID);
}
- public function getIsRecurrenceParent() {
- if ($this->isRecurring && !$this->instanceOfEventPHID) {
- return true;
- }
- return false;
+ public function isChildEvent() {
+ return ($this->instanceOfEventPHID !== null);
}
- public function getIsRecurrenceException() {
- if ($this->instanceOfEventPHID && !$this->isGhostEvent) {
+ public function isCancelledEvent() {
+ if ($this->getIsCancelled()) {
return true;
}
- return false;
- }
- public function getIsParentCancelled() {
- if ($this->instanceOfEventPHID == null) {
- return false;
+ if ($this->isChildEvent()) {
+ if ($this->getParentEvent()->getIsCancelled()) {
+ return true;
+ }
}
- $recurring_event = $this->getParentEvent();
- if ($recurring_event->getIsCancelled()) {
- return true;
- }
return false;
}
@@ -408,6 +479,7 @@
}
}
+
/* -( Markup Interface )--------------------------------------------------- */
diff --git a/src/applications/conpherence/query/ConpherenceThreadQuery.php b/src/applications/conpherence/query/ConpherenceThreadQuery.php
--- a/src/applications/conpherence/query/ConpherenceThreadQuery.php
+++ b/src/applications/conpherence/query/ConpherenceThreadQuery.php
@@ -314,6 +314,8 @@
$events = array();
if ($participant_phids) {
+ // TODO: All of this Calendar code is probably extra-broken, but none
+ // of it is currently reachable in the UI.
$events = id(new PhabricatorCalendarEventQuery())
->setViewer($this->getViewer())
->withInvitedPHIDs($participant_phids)
diff --git a/src/applications/people/application/PhabricatorPeopleApplication.php b/src/applications/people/application/PhabricatorPeopleApplication.php
--- a/src/applications/people/application/PhabricatorPeopleApplication.php
+++ b/src/applications/people/application/PhabricatorPeopleApplication.php
@@ -69,7 +69,6 @@
'' => 'PhabricatorPeopleProfileViewController',
'panel/'
=> $this->getPanelRouting('PhabricatorPeopleProfilePanelController'),
- 'calendar/' => 'PhabricatorPeopleCalendarController',
),
);
}
diff --git a/src/applications/people/controller/PhabricatorPeopleCalendarController.php b/src/applications/people/controller/PhabricatorPeopleCalendarController.php
deleted file mode 100644
--- a/src/applications/people/controller/PhabricatorPeopleCalendarController.php
+++ /dev/null
@@ -1,97 +0,0 @@
-<?php
-
-final class PhabricatorPeopleCalendarController
- extends PhabricatorPeopleProfileController {
-
- public function shouldAllowPublic() {
- return true;
- }
-
- public function handleRequest(AphrontRequest $request) {
- $viewer = $this->getViewer();
- $username = $request->getURIData('username');
-
- $user = id(new PhabricatorPeopleQuery())
- ->setViewer($viewer)
- ->withUsernames(array($username))
- ->needProfileImage(true)
- ->executeOne();
- if (!$user) {
- return new Aphront404Response();
- }
-
- $this->setUser($user);
-
- $picture = $user->getProfileImageURI();
-
- $now = time();
- $request = $this->getRequest();
- $year_d = phabricator_format_local_time($now, $user, 'Y');
- $year = $request->getInt('year', $year_d);
- $month_d = phabricator_format_local_time($now, $user, 'm');
- $month = $request->getInt('month', $month_d);
- $day = phabricator_format_local_time($now, $user, 'j');
-
- $start_epoch = strtotime("{$year}-{$month}-01");
- $end_epoch = strtotime("{$year}-{$month}-01 next month");
-
- $statuses = id(new PhabricatorCalendarEventQuery())
- ->setViewer($user)
- ->withInvitedPHIDs(array($user->getPHID()))
- ->withDateRange(
- $start_epoch,
- $end_epoch)
- ->execute();
-
- $start_range_value = AphrontFormDateControlValue::newFromEpoch(
- $user,
- $start_epoch);
- $end_range_value = AphrontFormDateControlValue::newFromEpoch(
- $user,
- $end_epoch);
-
- if ($month == $month_d && $year == $year_d) {
- $month_view = new PHUICalendarMonthView(
- $start_range_value,
- $end_range_value,
- $month,
- $year,
- $day);
- } else {
- $month_view = new PHUICalendarMonthView(
- $start_range_value,
- $end_range_value,
- $month,
- $year);
- }
-
- $month_view->setBrowseURI($request->getRequestURI());
- $month_view->setUser($user);
- $month_view->setImage($picture);
-
- $phids = mpull($statuses, 'getUserPHID');
- $handles = $this->loadViewerHandles($phids);
-
- foreach ($statuses as $status) {
- $event = new AphrontCalendarEventView();
- $event->setEpochRange($status->getDateFrom(), $status->getDateTo());
- $event->setUserPHID($status->getUserPHID());
- $event->setName($status->getName());
- $event->setDescription($status->getDescription());
- $event->setEventID($status->getID());
- $month_view->addEvent($event);
- }
-
- $nav = $this->getProfileMenu();
- $nav->selectFilter('calendar');
-
- $crumbs = $this->buildApplicationCrumbs();
- $crumbs->addTextCrumb(pht('Calendar'));
-
- return $this->newPage()
- ->setTitle(pht('Calendar'))
- ->setNavigation($nav)
- ->setCrumbs($crumbs)
- ->appendChild($month_view);
- }
-}

File Metadata

Mime Type
text/plain
Expires
Tue, May 14, 12:39 AM (2 w, 6 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6275466
Default Alt Text
D16248.diff (44 KB)

Event Timeline