Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13197808
D16248.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
44 KB
Referenced Files
None
Subscribers
None
D16248.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D16248: Generate "stub" events earlier, so more infrastructure works with Calendar
Attached
Detach File
Event Timeline
Log In to Comment