2014-02-06 10:07:42 -08:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class PhabricatorCalendarEventQuery
|
|
|
|
extends PhabricatorCursorPagedPolicyAwareQuery {
|
|
|
|
|
|
|
|
private $ids;
|
2014-02-06 10:10:43 -08:00
|
|
|
private $phids;
|
2014-02-06 10:07:42 -08:00
|
|
|
private $rangeBegin;
|
|
|
|
private $rangeEnd;
|
2015-05-06 11:12:24 -07:00
|
|
|
private $inviteePHIDs;
|
2016-07-14 12:54:36 -07:00
|
|
|
private $hostPHIDs;
|
Canceling calendar events should deactivate the event
Summary: Closes T7943, Canceling calendar event should deactivate the event instead of destroying data.
Test Plan: Create an event, cancel it, see changed status icon, query for active events, event should not appear, query for deactivated events, event should appear in results.
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: Korvin, epriestley
Maniphest Tasks: T7943
Differential Revision: https://secure.phabricator.com/D12604
2015-04-29 08:39:39 -07:00
|
|
|
private $isCancelled;
|
2015-06-03 09:27:39 -07:00
|
|
|
private $eventsWithNoParent;
|
2015-05-31 15:04:48 -07:00
|
|
|
private $instanceSequencePairs;
|
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
2016-07-07 07:19:58 -07:00
|
|
|
private $isStub;
|
2015-05-31 15:04:48 -07:00
|
|
|
|
2015-05-28 17:27:25 -07:00
|
|
|
private $generateGhosts = false;
|
|
|
|
|
2015-06-22 13:27:37 -07:00
|
|
|
public function newResultObject() {
|
|
|
|
return new PhabricatorCalendarEvent();
|
|
|
|
}
|
|
|
|
|
2015-05-28 17:27:25 -07:00
|
|
|
public function setGenerateGhosts($generate_ghosts) {
|
|
|
|
$this->generateGhosts = $generate_ghosts;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2014-02-06 10:07:42 -08:00
|
|
|
public function withIDs(array $ids) {
|
|
|
|
$this->ids = $ids;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2014-02-06 10:10:43 -08:00
|
|
|
public function withPHIDs(array $phids) {
|
|
|
|
$this->phids = $phids;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2014-02-06 10:07:42 -08:00
|
|
|
public function withDateRange($begin, $end) {
|
|
|
|
$this->rangeBegin = $begin;
|
|
|
|
$this->rangeEnd = $end;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function withInvitedPHIDs(array $phids) {
|
2015-05-06 11:12:24 -07:00
|
|
|
$this->inviteePHIDs = $phids;
|
2014-02-06 10:07:42 -08:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2016-07-14 12:54:36 -07:00
|
|
|
public function withHostPHIDs(array $phids) {
|
|
|
|
$this->hostPHIDs = $phids;
|
2014-02-06 10:10:18 -08:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
Canceling calendar events should deactivate the event
Summary: Closes T7943, Canceling calendar event should deactivate the event instead of destroying data.
Test Plan: Create an event, cancel it, see changed status icon, query for active events, event should not appear, query for deactivated events, event should appear in results.
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: Korvin, epriestley
Maniphest Tasks: T7943
Differential Revision: https://secure.phabricator.com/D12604
2015-04-29 08:39:39 -07:00
|
|
|
public function withIsCancelled($is_cancelled) {
|
|
|
|
$this->isCancelled = $is_cancelled;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
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
2016-07-07 07:19:58 -07:00
|
|
|
public function withIsStub($is_stub) {
|
|
|
|
$this->isStub = $is_stub;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2015-06-03 09:27:39 -07:00
|
|
|
public function withEventsWithNoParent($events_with_no_parent) {
|
|
|
|
$this->eventsWithNoParent = $events_with_no_parent;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2015-05-31 15:04:48 -07:00
|
|
|
public function withInstanceSequencePairs(array $pairs) {
|
|
|
|
$this->instanceSequencePairs = $pairs;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2015-05-21 17:10:27 -07:00
|
|
|
protected function getDefaultOrderVector() {
|
|
|
|
return array('start', 'id');
|
|
|
|
}
|
|
|
|
|
2016-07-13 09:29:02 -07:00
|
|
|
public function getBuiltinOrders() {
|
|
|
|
return array(
|
|
|
|
'start' => array(
|
|
|
|
'vector' => array('start', 'id'),
|
|
|
|
'name' => pht('Event Start'),
|
|
|
|
),
|
|
|
|
) + parent::getBuiltinOrders();
|
|
|
|
}
|
|
|
|
|
2015-05-21 17:10:27 -07:00
|
|
|
public function getOrderableColumns() {
|
|
|
|
return array(
|
|
|
|
'start' => array(
|
|
|
|
'table' => $this->getPrimaryTableAlias(),
|
|
|
|
'column' => 'dateFrom',
|
|
|
|
'reverse' => true,
|
|
|
|
'type' => 'int',
|
|
|
|
'unique' => false,
|
|
|
|
),
|
|
|
|
) + parent::getOrderableColumns();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getPagingValueMap($cursor, array $keys) {
|
|
|
|
$event = $this->loadCursorObject($cursor);
|
|
|
|
return array(
|
2016-10-04 09:47:38 -07:00
|
|
|
'start' => $event->getStartDateTimeEpoch(),
|
2015-05-21 17:10:27 -07:00
|
|
|
'id' => $event->getID(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-07-13 09:29:02 -07:00
|
|
|
protected function shouldLimitResults() {
|
|
|
|
// When generating ghosts, we can't rely on database ordering because
|
|
|
|
// MySQL can't predict the ghost start times. We'll just load all matching
|
|
|
|
// events, then generate results from there.
|
|
|
|
if ($this->generateGhosts) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-02-06 10:07:42 -08:00
|
|
|
protected function loadPage() {
|
2016-04-07 05:10:55 -07:00
|
|
|
$events = $this->loadStandardPage($this->newResultObject());
|
2015-05-07 18:57:28 -07:00
|
|
|
|
2016-04-07 05:10:55 -07:00
|
|
|
$viewer = $this->getViewer();
|
2015-05-07 18:57:28 -07:00
|
|
|
foreach ($events as $event) {
|
2016-04-07 05:10:55 -07:00
|
|
|
$event->applyViewerTimezone($viewer);
|
2015-05-07 18:57:28 -07:00
|
|
|
}
|
|
|
|
|
2015-05-28 17:27:25 -07:00
|
|
|
if (!$this->generateGhosts) {
|
|
|
|
return $events;
|
|
|
|
}
|
|
|
|
|
2016-04-07 05:10:55 -07:00
|
|
|
$raw_limit = $this->getRawResultLimit();
|
|
|
|
|
|
|
|
if (!$raw_limit && !$this->rangeEnd) {
|
|
|
|
throw new Exception(
|
|
|
|
pht(
|
|
|
|
'Event queries which generate ghost events must include either a '.
|
|
|
|
'result limit or an end date, because they may otherwise generate '.
|
|
|
|
'an infinite number of results. This query has neither.'));
|
|
|
|
}
|
2015-05-31 15:04:48 -07:00
|
|
|
|
2015-06-02 19:44:31 -07:00
|
|
|
foreach ($events as $key => $event) {
|
2015-05-28 17:27:25 -07:00
|
|
|
$sequence_start = 0;
|
2015-06-01 18:56:11 -07:00
|
|
|
$sequence_end = null;
|
|
|
|
$end = null;
|
2015-05-28 17:27:25 -07:00
|
|
|
|
2015-06-02 19:44:31 -07:00
|
|
|
$instance_of = $event->getInstanceOfEventPHID();
|
|
|
|
|
|
|
|
if ($instance_of == null && $this->isCancelled !== null) {
|
|
|
|
if ($event->getIsCancelled() != $this->isCancelled) {
|
|
|
|
unset($events[$key]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2016-07-13 09:29:02 -07:00
|
|
|
}
|
2015-06-02 19:44:31 -07:00
|
|
|
|
2016-07-13 09:29:02 -07:00
|
|
|
// Pull out all of the parents first. We may discard them as we begin
|
|
|
|
// generating ghost events, but we still want to process all of them.
|
|
|
|
$parents = array();
|
|
|
|
foreach ($events as $key => $event) {
|
|
|
|
if ($event->isParentEvent()) {
|
|
|
|
$parents[$key] = $event;
|
|
|
|
}
|
|
|
|
}
|
2015-05-28 17:27:25 -07:00
|
|
|
|
2016-07-13 09:29:02 -07:00
|
|
|
// Now that we've picked out all the parent events, we can immediately
|
|
|
|
// discard anything outside of the time window.
|
|
|
|
$events = $this->getEventsInRange($events);
|
2015-05-28 17:27:25 -07:00
|
|
|
|
2016-07-13 09:29:02 -07:00
|
|
|
$enforced_end = null;
|
|
|
|
foreach ($parents as $key => $event) {
|
|
|
|
$sequence_start = 0;
|
|
|
|
$sequence_end = null;
|
|
|
|
$start = null;
|
2015-05-28 17:27:25 -07:00
|
|
|
|
2016-07-13 09:29:02 -07:00
|
|
|
$duration = $event->getDuration();
|
2015-05-28 17:27:25 -07:00
|
|
|
|
2016-07-13 09:29:02 -07:00
|
|
|
$frequency = $event->getFrequencyUnit();
|
|
|
|
$modify_key = '+1 '.$frequency;
|
|
|
|
|
|
|
|
if (($this->rangeBegin !== null) &&
|
2016-10-04 09:47:38 -07:00
|
|
|
($this->rangeBegin > $event->getStartDateTimeEpoch())) {
|
2016-07-13 09:29:02 -07:00
|
|
|
$max_date = $this->rangeBegin - $duration;
|
2016-10-04 09:47:38 -07:00
|
|
|
$date = $event->getStartDateTimeEpoch();
|
2015-06-01 18:56:11 -07:00
|
|
|
$datetime = PhabricatorTime::getDateTimeFromEpoch($date, $viewer);
|
2015-05-28 17:27:25 -07:00
|
|
|
|
2016-07-13 09:29:02 -07:00
|
|
|
while ($date < $max_date) {
|
|
|
|
// TODO: optimize this to not loop through all off-screen events
|
|
|
|
$sequence_start++;
|
|
|
|
$datetime = PhabricatorTime::getDateTimeFromEpoch($date, $viewer);
|
|
|
|
$date = $datetime->modify($modify_key)->format('U');
|
2015-06-01 18:56:11 -07:00
|
|
|
}
|
2015-05-28 17:27:25 -07:00
|
|
|
|
2016-07-13 09:29:02 -07:00
|
|
|
$start = $this->rangeBegin;
|
|
|
|
} else {
|
2016-10-04 09:47:38 -07:00
|
|
|
$start = $event->getStartDateTimeEpoch() - $duration;
|
2016-07-13 09:29:02 -07:00
|
|
|
}
|
2015-05-28 17:27:25 -07:00
|
|
|
|
2016-07-13 09:29:02 -07:00
|
|
|
$date = $start;
|
|
|
|
$datetime = PhabricatorTime::getDateTimeFromEpoch($date, $viewer);
|
2015-05-28 17:27:25 -07:00
|
|
|
|
2016-07-13 09:29:02 -07:00
|
|
|
// Select the minimum end time we need to generate events until.
|
|
|
|
$end_times = array();
|
|
|
|
if ($this->rangeEnd) {
|
|
|
|
$end_times[] = $this->rangeEnd;
|
|
|
|
}
|
2015-05-31 15:04:48 -07:00
|
|
|
|
2016-07-13 09:29:02 -07:00
|
|
|
if ($event->getRecurrenceEndDate()) {
|
|
|
|
$end_times[] = $event->getRecurrenceEndDate();
|
|
|
|
}
|
2016-04-07 05:10:55 -07:00
|
|
|
|
2016-07-13 09:29:02 -07:00
|
|
|
if ($enforced_end) {
|
|
|
|
$end_times[] = $enforced_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($end_times) {
|
|
|
|
$end = min($end_times);
|
|
|
|
$sequence_end = $sequence_start;
|
|
|
|
while ($date < $end) {
|
|
|
|
$sequence_end++;
|
|
|
|
$datetime->modify($modify_key);
|
|
|
|
$date = $datetime->format('U');
|
|
|
|
if ($sequence_end > $raw_limit + $sequence_start) {
|
|
|
|
break;
|
2016-04-07 05:10:55 -07:00
|
|
|
}
|
2015-05-31 15:04:48 -07:00
|
|
|
}
|
2016-07-13 09:29:02 -07:00
|
|
|
} else {
|
|
|
|
$sequence_end = $raw_limit + $sequence_start;
|
|
|
|
}
|
|
|
|
|
|
|
|
$sequence_start = max(1, $sequence_start);
|
|
|
|
for ($index = $sequence_start; $index < $sequence_end; $index++) {
|
|
|
|
$events[] = $event->newGhost($viewer, $index);
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: We're slicing results every time because this makes it cheaper
|
|
|
|
// to generate future ghosts. If we already have 100 events that occur
|
|
|
|
// before July 1, we know we never need to generate ghosts after that
|
|
|
|
// because they couldn't possibly ever appear in the result set.
|
|
|
|
|
|
|
|
if ($raw_limit) {
|
|
|
|
if (count($events) > $raw_limit) {
|
2016-10-04 09:47:38 -07:00
|
|
|
$events = msort($events, 'getStartDateTimeEpoch');
|
2016-07-13 09:29:02 -07:00
|
|
|
$events = array_slice($events, 0, $raw_limit, true);
|
2016-10-04 09:47:38 -07:00
|
|
|
$enforced_end = last($events)->getStartDateTimeEpoch();
|
2016-07-13 09:29:02 -07:00
|
|
|
}
|
2015-05-31 15:04:48 -07: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
2016-07-07 07:19:58 -07:00
|
|
|
// 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.
|
2015-06-03 13:01:26 -07: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
2016-07-07 07:19:58 -07:00
|
|
|
// 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) {
|
2015-06-03 13:01:26 -07:00
|
|
|
if ($event->getIsGhostEvent()) {
|
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
2016-07-07 07:19:58 -07:00
|
|
|
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;
|
2015-06-03 13:01:26 -07: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
2016-07-07 07:19:58 -07:00
|
|
|
|
|
|
|
// 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;
|
2015-06-03 13:01:26 -07: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
2016-07-07 07:19:58 -07:00
|
|
|
if ($parent_pairs) {
|
|
|
|
$instances = id(new self())
|
2015-05-31 15:04:48 -07:00
|
|
|
->setViewer($viewer)
|
|
|
|
->setParentQuery($this)
|
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
2016-07-07 07:19:58 -07:00
|
|
|
->withInstanceSequencePairs($parent_pairs)
|
2015-05-31 15:04:48 -07:00
|
|
|
->execute();
|
|
|
|
|
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
2016-07-07 07:19:58 -07:00
|
|
|
foreach ($instances as $instance) {
|
|
|
|
$parent_phid = $instance->getInstanceOfEventPHID();
|
|
|
|
$sequence = $instance->getSequenceIndex();
|
2015-05-31 15:04:48 -07: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
2016-07-07 07:19:58 -07:00
|
|
|
$indexes = idx($map, $parent_phid);
|
|
|
|
$key = idx($indexes, $sequence);
|
|
|
|
|
|
|
|
// Replace the ghost with the corresponding concrete event.
|
|
|
|
$events[$key] = $instance;
|
2015-05-28 17:27:25 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-04 09:47:38 -07:00
|
|
|
$events = msort($events, 'getStartDateTimeEpoch');
|
2016-07-13 09:29:02 -07:00
|
|
|
|
2015-05-07 18:57:28 -07:00
|
|
|
return $events;
|
2014-02-06 10:07:42 -08:00
|
|
|
}
|
|
|
|
|
2015-05-06 11:12:24 -07:00
|
|
|
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn_r) {
|
|
|
|
$parts = parent::buildJoinClauseParts($conn_r);
|
2016-07-13 09:29:02 -07:00
|
|
|
|
2015-05-06 11:12:24 -07:00
|
|
|
if ($this->inviteePHIDs !== null) {
|
|
|
|
$parts[] = qsprintf(
|
|
|
|
$conn_r,
|
2015-05-06 12:41:02 -07:00
|
|
|
'JOIN %T invitee ON invitee.eventPHID = event.phid
|
|
|
|
AND invitee.status != %s',
|
2015-05-06 11:12:24 -07:00
|
|
|
id(new PhabricatorCalendarEventInvitee())->getTableName(),
|
|
|
|
PhabricatorCalendarEventInvitee::STATUS_UNINVITED);
|
|
|
|
}
|
2016-07-13 09:29:02 -07:00
|
|
|
|
2015-05-06 11:12:24 -07:00
|
|
|
return $parts;
|
|
|
|
}
|
|
|
|
|
2016-04-07 05:10:55 -07:00
|
|
|
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
|
|
|
$where = parent::buildWhereClauseParts($conn);
|
2014-02-06 10:07:42 -08:00
|
|
|
|
|
|
|
if ($this->ids) {
|
|
|
|
$where[] = qsprintf(
|
2016-04-07 05:10:55 -07:00
|
|
|
$conn,
|
2015-05-06 11:12:24 -07:00
|
|
|
'event.id IN (%Ld)',
|
2014-02-06 10:07:42 -08:00
|
|
|
$this->ids);
|
|
|
|
}
|
|
|
|
|
2014-02-06 10:10:43 -08:00
|
|
|
if ($this->phids) {
|
|
|
|
$where[] = qsprintf(
|
2016-04-07 05:10:55 -07:00
|
|
|
$conn,
|
2015-05-06 11:12:24 -07:00
|
|
|
'event.phid IN (%Ls)',
|
2014-02-06 10:10:43 -08:00
|
|
|
$this->phids);
|
|
|
|
}
|
|
|
|
|
2016-07-11 15:29:11 -07:00
|
|
|
// NOTE: The date ranges we query for are larger than the requested ranges
|
|
|
|
// because we need to catch all-day events. We'll refine this range later
|
|
|
|
// after adjusting the visible range of events we load.
|
|
|
|
|
2014-02-06 10:10:18 -08:00
|
|
|
if ($this->rangeBegin) {
|
|
|
|
$where[] = qsprintf(
|
2016-04-07 05:10:55 -07:00
|
|
|
$conn,
|
2015-05-28 17:27:25 -07:00
|
|
|
'event.dateTo >= %d OR event.isRecurring = 1',
|
2016-07-11 15:29:11 -07:00
|
|
|
$this->rangeBegin - phutil_units('16 hours in seconds'));
|
2014-02-06 10:10:18 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->rangeEnd) {
|
2014-02-06 10:07:42 -08:00
|
|
|
$where[] = qsprintf(
|
2016-04-07 05:10:55 -07:00
|
|
|
$conn,
|
2015-05-06 11:12:24 -07:00
|
|
|
'event.dateFrom <= %d',
|
2016-07-11 15:29:11 -07:00
|
|
|
$this->rangeEnd + phutil_units('16 hours in seconds'));
|
2014-02-06 10:07:42 -08:00
|
|
|
}
|
|
|
|
|
2015-05-06 11:12:24 -07:00
|
|
|
if ($this->inviteePHIDs !== null) {
|
2014-02-06 10:07:42 -08:00
|
|
|
$where[] = qsprintf(
|
2016-04-07 05:10:55 -07:00
|
|
|
$conn,
|
2015-05-06 11:12:24 -07:00
|
|
|
'invitee.inviteePHID IN (%Ls)',
|
|
|
|
$this->inviteePHIDs);
|
2014-02-06 10:07:42 -08:00
|
|
|
}
|
|
|
|
|
2016-07-14 12:54:36 -07:00
|
|
|
if ($this->hostPHIDs) {
|
2014-02-06 10:10:18 -08:00
|
|
|
$where[] = qsprintf(
|
2016-04-07 05:10:55 -07:00
|
|
|
$conn,
|
2016-07-14 12:54:36 -07:00
|
|
|
'event.hostPHID IN (%Ls)',
|
|
|
|
$this->hostPHIDs);
|
2014-02-06 10:10:18 -08:00
|
|
|
}
|
|
|
|
|
Canceling calendar events should deactivate the event
Summary: Closes T7943, Canceling calendar event should deactivate the event instead of destroying data.
Test Plan: Create an event, cancel it, see changed status icon, query for active events, event should not appear, query for deactivated events, event should appear in results.
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: Korvin, epriestley
Maniphest Tasks: T7943
Differential Revision: https://secure.phabricator.com/D12604
2015-04-29 08:39:39 -07:00
|
|
|
if ($this->isCancelled !== null) {
|
|
|
|
$where[] = qsprintf(
|
2016-04-07 05:10:55 -07:00
|
|
|
$conn,
|
2015-05-06 11:12:24 -07:00
|
|
|
'event.isCancelled = %d',
|
Canceling calendar events should deactivate the event
Summary: Closes T7943, Canceling calendar event should deactivate the event instead of destroying data.
Test Plan: Create an event, cancel it, see changed status icon, query for active events, event should not appear, query for deactivated events, event should appear in results.
Reviewers: #blessed_reviewers, epriestley
Reviewed By: #blessed_reviewers, epriestley
Subscribers: Korvin, epriestley
Maniphest Tasks: T7943
Differential Revision: https://secure.phabricator.com/D12604
2015-04-29 08:39:39 -07:00
|
|
|
(int)$this->isCancelled);
|
|
|
|
}
|
|
|
|
|
2015-06-03 09:27:39 -07:00
|
|
|
if ($this->eventsWithNoParent == true) {
|
|
|
|
$where[] = qsprintf(
|
2016-04-07 05:10:55 -07:00
|
|
|
$conn,
|
2015-06-03 09:27:39 -07:00
|
|
|
'event.instanceOfEventPHID IS NULL');
|
|
|
|
}
|
|
|
|
|
2015-05-31 15:04:48 -07:00
|
|
|
if ($this->instanceSequencePairs !== null) {
|
|
|
|
$sql = array();
|
|
|
|
|
|
|
|
foreach ($this->instanceSequencePairs as $pair) {
|
|
|
|
$sql[] = qsprintf(
|
2016-04-07 05:10:55 -07:00
|
|
|
$conn,
|
2015-05-31 15:04:48 -07:00
|
|
|
'(event.instanceOfEventPHID = %s AND event.sequenceIndex = %d)',
|
|
|
|
$pair[0],
|
|
|
|
$pair[1]);
|
|
|
|
}
|
2016-04-07 05:10:55 -07:00
|
|
|
|
2015-05-31 15:04:48 -07:00
|
|
|
$where[] = qsprintf(
|
2016-04-07 05:10:55 -07:00
|
|
|
$conn,
|
2015-05-31 15:04:48 -07:00
|
|
|
'%Q',
|
|
|
|
implode(' OR ', $sql));
|
|
|
|
}
|
|
|
|
|
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
2016-07-07 07:19:58 -07:00
|
|
|
if ($this->isStub !== null) {
|
|
|
|
$where[] = qsprintf(
|
|
|
|
$conn,
|
|
|
|
'event.isStub = %d',
|
|
|
|
(int)$this->isStub);
|
|
|
|
}
|
|
|
|
|
2016-04-07 05:10:55 -07:00
|
|
|
return $where;
|
2014-02-06 10:07:42 -08:00
|
|
|
}
|
|
|
|
|
2015-05-06 11:12:24 -07:00
|
|
|
protected function getPrimaryTableAlias() {
|
|
|
|
return 'event';
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function shouldGroupQueryResultRows() {
|
|
|
|
if ($this->inviteePHIDs !== null) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return parent::shouldGroupQueryResultRows();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getApplicationSearchObjectPHIDColumn() {
|
|
|
|
return 'event.phid';
|
|
|
|
}
|
|
|
|
|
2014-02-06 10:07:42 -08:00
|
|
|
public function getQueryApplicationClass() {
|
2014-07-23 10:03:09 +10:00
|
|
|
return 'PhabricatorCalendarApplication';
|
2014-02-06 10:07:42 -08:00
|
|
|
}
|
|
|
|
|
2015-04-29 13:51:09 -07:00
|
|
|
|
|
|
|
protected function willFilterPage(array $events) {
|
2015-06-02 19:44:31 -07:00
|
|
|
$instance_of_event_phids = array();
|
|
|
|
$recurring_events = array();
|
|
|
|
$viewer = $this->getViewer();
|
2015-05-26 14:28:07 -07:00
|
|
|
|
2016-07-13 09:29:02 -07:00
|
|
|
$events = $this->getEventsInRange($events);
|
2015-05-26 14:28:07 -07:00
|
|
|
|
2015-04-29 13:51:09 -07:00
|
|
|
$phids = array();
|
|
|
|
|
|
|
|
foreach ($events as $event) {
|
|
|
|
$phids[] = $event->getPHID();
|
2015-06-02 19:44:31 -07:00
|
|
|
$instance_of = $event->getInstanceOfEventPHID();
|
|
|
|
|
|
|
|
if ($instance_of) {
|
2015-06-03 09:27:39 -07:00
|
|
|
$instance_of_event_phids[] = $instance_of;
|
2015-06-02 19:44:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count($instance_of_event_phids) > 0) {
|
|
|
|
$recurring_events = id(new PhabricatorCalendarEventQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withPHIDs($instance_of_event_phids)
|
2015-06-03 09:27:39 -07:00
|
|
|
->withEventsWithNoParent(true)
|
2015-06-02 19:44:31 -07:00
|
|
|
->execute();
|
|
|
|
|
|
|
|
$recurring_events = mpull($recurring_events, null, 'getPHID');
|
2015-04-29 13:51:09 -07:00
|
|
|
}
|
|
|
|
|
2015-05-29 07:17:09 -07:00
|
|
|
if ($events) {
|
|
|
|
$invitees = id(new PhabricatorCalendarEventInviteeQuery())
|
2015-06-02 19:44:31 -07:00
|
|
|
->setViewer($viewer)
|
2015-05-29 07:17:09 -07:00
|
|
|
->withEventPHIDs($phids)
|
|
|
|
->execute();
|
|
|
|
$invitees = mgroup($invitees, 'getEventPHID');
|
|
|
|
} else {
|
|
|
|
$invitees = array();
|
|
|
|
}
|
2015-04-29 13:51:09 -07:00
|
|
|
|
2015-06-02 19:44:31 -07:00
|
|
|
foreach ($events as $key => $event) {
|
2015-04-29 13:51:09 -07:00
|
|
|
$event_invitees = idx($invitees, $event->getPHID(), array());
|
|
|
|
$event->attachInvitees($event_invitees);
|
2015-06-02 19:44:31 -07:00
|
|
|
|
|
|
|
$instance_of = $event->getInstanceOfEventPHID();
|
|
|
|
if (!$instance_of) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$parent = idx($recurring_events, $instance_of);
|
|
|
|
|
|
|
|
// should never get here
|
|
|
|
if (!$parent) {
|
|
|
|
unset($events[$key]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$event->attachParentEvent($parent);
|
|
|
|
|
|
|
|
if ($this->isCancelled !== null) {
|
|
|
|
if ($event->getIsCancelled() != $this->isCancelled) {
|
|
|
|
unset($events[$key]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2015-04-29 13:51:09 -07:00
|
|
|
}
|
|
|
|
|
2016-10-04 09:47:38 -07:00
|
|
|
$events = msort($events, 'getStartDateTimeEpoch');
|
2015-05-26 14:28:07 -07:00
|
|
|
|
2015-04-29 13:51:09 -07:00
|
|
|
return $events;
|
|
|
|
}
|
|
|
|
|
2016-07-13 09:29:02 -07:00
|
|
|
private function getEventsInRange(array $events) {
|
|
|
|
$range_start = $this->rangeBegin;
|
|
|
|
$range_end = $this->rangeEnd;
|
|
|
|
|
|
|
|
foreach ($events as $key => $event) {
|
2016-10-04 09:47:38 -07:00
|
|
|
$event_start = $event->getStartDateTimeEpoch();
|
|
|
|
$event_end = $event->getEndDateTimeEpoch();
|
2016-07-13 09:29:02 -07:00
|
|
|
|
|
|
|
if ($range_start && $event_end < $range_start) {
|
|
|
|
unset($events[$key]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($range_end && $event_start > $range_end) {
|
|
|
|
unset($events[$key]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $events;
|
|
|
|
}
|
|
|
|
|
2014-02-06 10:07:42 -08:00
|
|
|
}
|