mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-09 16:32:39 +01:00
Generate "stub" events earlier, so more infrastructure works with Calendar
Summary: Ref T9275. When you create a recurring event which recurs forever, we want to avoid writing an infinite number of rows to the database. Currently, we write a row to the database right before you edit the event. Until then, we refer to it as `E123/999` or whatever ("instance 999 of event 123"). This creates a big mess with trying to make recurring events work with EditEngine, Subscriptions, Projects, Flags, Tokens, etc -- all of this stuff assumes that whatever you're working with has a PHID. I poked at letting this stuff work without a PHID a little bit, but that looked like a gigantic mess. Instead, generate an event "stub" a little sooner (when you look at the event detail page). This is basically just an ID/PHID to refer to the instance. Then, when you edit the stub, "materialize" it into a real event. This still has some issues, but I think it's more promising than the other approach was. Also: - Removes dead user profile calendar controller. - Replaces comments with EditEngine comments. Test Plan: - Commented on a recurring event. - Awarded tokens to a recurring event. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9275 Differential Revision: https://secure.phabricator.com/D16248
This commit is contained in:
parent
91a8a6d618
commit
3ab6a7e19f
17 changed files with 404 additions and 505 deletions
2
resources/sql/autopatches/20160707.calendar.01.stub.sql
Normal file
2
resources/sql/autopatches/20160707.calendar.01.stub.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
|
||||
ADD isStub BOOL NOT NULL;
|
|
@ -2022,7 +2022,6 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFulltextInterface',
|
||||
),
|
||||
'PhabricatorCalendarEventCancelController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarEventCommentController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarEventDragController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarEventEditController' => 'PhabricatorCalendarController',
|
||||
'PhabricatorCalendarEventEditProController' => 'ManiphestController',
|
||||
|
@ -7741,7 +7738,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPeopleAnyOwnerDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorPeopleApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorPeopleApproveController' => 'PhabricatorPeopleController',
|
||||
'PhabricatorPeopleCalendarController' => 'PhabricatorPeopleProfileController',
|
||||
'PhabricatorPeopleController' => 'PhabricatorController',
|
||||
'PhabricatorPeopleCreateController' => 'PhabricatorPeopleController',
|
||||
'PhabricatorPeopleDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
|
|
|
@ -40,7 +40,7 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication {
|
|||
|
||||
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 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication {
|
|||
=> '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',
|
||||
),
|
||||
),
|
||||
|
|
|
@ -30,49 +30,4 @@ abstract class PhabricatorCalendarController extends PhabricatorController {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ final class PhabricatorCalendarEventCancelController
|
|||
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 @@ final class PhabricatorCalendarEventCancelController
|
|||
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();
|
||||
|
||||
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 @@ final class PhabricatorCalendarEventCancelController
|
|||
}
|
||||
|
||||
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_parent) {
|
||||
$title = pht('Reinstate Recurrence');
|
||||
} else if ($is_child) {
|
||||
$title = pht('Reinstate Instance');
|
||||
$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 this instance of this recurring event?');
|
||||
$cancel = pht('Back');
|
||||
$submit = pht('Reinstate Instance');
|
||||
} else if ($is_parent) {
|
||||
$title = pht('Reinstate Recurring Event');
|
||||
$paragraph = pht(
|
||||
'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');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -85,33 +85,10 @@ final class PhabricatorCalendarEventEditController
|
|||
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 @@ final class PhabricatorCalendarEventEditController
|
|||
}
|
||||
}
|
||||
|
||||
$cancel_uri = '/'.$event->getMonogram();
|
||||
$cancel_uri = $event->getURI();
|
||||
}
|
||||
|
||||
if ($this->isCreate()) {
|
||||
|
@ -153,7 +130,7 @@ final class PhabricatorCalendarEventEditController
|
|||
$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();
|
||||
|
|
|
@ -20,12 +20,11 @@ final class PhabricatorCalendarEventJoinController
|
|||
->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());
|
||||
|
|
|
@ -9,89 +9,48 @@ final class PhabricatorCalendarEventViewController
|
|||
|
||||
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();
|
||||
// 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);
|
||||
}
|
||||
|
||||
$title = $event->getMonogram().' ('.$sequence.')';
|
||||
$page_title = $title.' '.$event->getName();
|
||||
$monogram = $event->getMonogram();
|
||||
$page_title = $monogram.' '.$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->addTextCrumb($monogram);
|
||||
$crumbs->setBorder(true);
|
||||
}
|
||||
|
||||
if (!$event->getIsGhostEvent()) {
|
||||
$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 @@ final class PhabricatorCalendarEventViewController
|
|||
->setTitle($page_title)
|
||||
->setCrumbs($crumbs)
|
||||
->setPageObjectPHIDs(array($event->getPHID()))
|
||||
->appendChild(
|
||||
array(
|
||||
$view,
|
||||
));
|
||||
->appendChild($view);
|
||||
}
|
||||
|
||||
private function buildHeaderView(
|
||||
|
@ -152,7 +108,7 @@ final class PhabricatorCalendarEventViewController
|
|||
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 @@ final class PhabricatorCalendarEventViewController
|
|||
$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_label = pht('Edit This Instance');
|
||||
$edit_uri = "event/edit/{$id}/";
|
||||
if ($event->isChildEvent()) {
|
||||
$edit_label = pht('Edit This Instance');
|
||||
} else {
|
||||
$edit_label = pht('Edit');
|
||||
$edit_uri = "event/edit/{$id}/";
|
||||
}
|
||||
|
||||
$curtain = $this->newCurtainView($event);
|
||||
|
@ -204,28 +152,21 @@ final class PhabricatorCalendarEventViewController
|
|||
}
|
||||
|
||||
$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 @@ final class PhabricatorCalendarEventViewController
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -55,6 +55,10 @@ final class PhabricatorCalendarEditEngine
|
|||
return $object->getURI();
|
||||
}
|
||||
|
||||
protected function getEditorURI() {
|
||||
return $this->getApplication()->getApplicationURI('event/editpro/');
|
||||
}
|
||||
|
||||
protected function buildCustomEditFields($object) {
|
||||
$fields = array(
|
||||
id(new PhabricatorTextEditField())
|
||||
|
|
|
@ -11,6 +11,47 @@ final class PhabricatorCalendarEventEditor
|
|||
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 @@ final class PhabricatorCalendarEventEditor
|
|||
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) {
|
||||
|
|
|
@ -12,6 +12,7 @@ final class PhabricatorCalendarEventQuery
|
|||
private $isCancelled;
|
||||
private $eventsWithNoParent;
|
||||
private $instanceSequencePairs;
|
||||
private $isStub;
|
||||
|
||||
private $generateGhosts = false;
|
||||
|
||||
|
@ -55,6 +56,11 @@ final class PhabricatorCalendarEventQuery
|
|||
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 @@ final class PhabricatorCalendarEventQuery
|
|||
$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 @@ final class PhabricatorCalendarEventQuery
|
|||
}
|
||||
}
|
||||
|
||||
$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) {
|
||||
if ($event->getIsGhostEvent()) {
|
||||
$index = $event->getSequenceIndex();
|
||||
$instance_sequence_pairs[] = array($event->getPHID(), $index);
|
||||
$map[$event->getPHID()][$index] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($instance_sequence_pairs) > 0) {
|
||||
$sub_query = id(new PhabricatorCalendarEventQuery())
|
||||
->setViewer($viewer)
|
||||
->setParentQuery($this)
|
||||
->withInstanceSequencePairs($instance_sequence_pairs)
|
||||
->execute();
|
||||
|
||||
foreach ($sub_query as $edited_ghost) {
|
||||
$indexes = idx($map, $edited_ghost->getInstanceOfEventPHID());
|
||||
$key = idx($indexes, $edited_ghost->getSequenceIndex());
|
||||
$events[$key] = $edited_ghost;
|
||||
}
|
||||
|
||||
$id_map = array();
|
||||
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()) {
|
||||
continue;
|
||||
}
|
||||
if (isset($id_map[$event->getID()])) {
|
||||
unset($events[$key]);
|
||||
} else {
|
||||
$id_map[$event->getID()] = true;
|
||||
|
||||
$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 ($parent_pairs) {
|
||||
$instances = id(new self())
|
||||
->setViewer($viewer)
|
||||
->setParentQuery($this)
|
||||
->withInstanceSequencePairs($parent_pairs)
|
||||
->execute();
|
||||
|
||||
foreach ($instances as $instance) {
|
||||
$parent_phid = $instance->getInstanceOfEventPHID();
|
||||
$sequence = $instance->getSequenceIndex();
|
||||
|
||||
$indexes = idx($map, $parent_phid);
|
||||
$key = idx($indexes, $sequence);
|
||||
|
||||
// Replace the ghost with the corresponding concrete event.
|
||||
$events[$key] = $instance;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,6 +361,13 @@ final class PhabricatorCalendarEventQuery
|
|||
implode(' OR ', $sql));
|
||||
}
|
||||
|
||||
if ($this->isStub !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'event.isStub = %d',
|
||||
(int)$this->isStub);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
|
|
|
@ -113,7 +113,15 @@ final class PhabricatorCalendarEventSearchEngine
|
|||
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(
|
||||
|
|
|
@ -22,6 +22,7 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
protected $isAllDay;
|
||||
protected $icon;
|
||||
protected $mailKey;
|
||||
protected $isStub;
|
||||
|
||||
protected $isRecurring = 0;
|
||||
protected $recurrenceFrequency = array();
|
||||
|
@ -71,6 +72,7 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
->setUserPHID($actor->getPHID())
|
||||
->setIsCancelled(0)
|
||||
->setIsAllDay(0)
|
||||
->setIsStub(0)
|
||||
->setIsRecurring($is_recurring)
|
||||
->setIcon(self::DEFAULT_ICON)
|
||||
->setViewPolicy($view_policy)
|
||||
|
@ -80,6 +82,116 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
->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 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
'recurrenceEndDate' => 'epoch?',
|
||||
'instanceOfEventPHID' => 'phid?',
|
||||
'sequenceIndex' => 'uint32?',
|
||||
'isStub' => 'bool',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'userPHID_dateFrom' => array(
|
||||
|
@ -285,38 +398,6 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
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 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
}
|
||||
|
||||
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 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
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;
|
||||
}
|
||||
|
||||
$recurring_event = $this->getParentEvent();
|
||||
if ($recurring_event->getIsCancelled()) {
|
||||
if ($this->isChildEvent()) {
|
||||
if ($this->getParentEvent()->getIsCancelled()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -408,6 +479,7 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* -( Markup Interface )--------------------------------------------------- */
|
||||
|
||||
|
||||
|
|
|
@ -314,6 +314,8 @@ final class ConpherenceThreadQuery
|
|||
|
||||
$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)
|
||||
|
|
|
@ -69,7 +69,6 @@ final class PhabricatorPeopleApplication extends PhabricatorApplication {
|
|||
'' => 'PhabricatorPeopleProfileViewController',
|
||||
'panel/'
|
||||
=> $this->getPanelRouting('PhabricatorPeopleProfilePanelController'),
|
||||
'calendar/' => 'PhabricatorPeopleCalendarController',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue