2014-02-06 19:10:18 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class PhabricatorCalendarEventSearchEngine
|
|
|
|
extends PhabricatorApplicationSearchEngine {
|
|
|
|
|
2015-05-03 01:17:05 +02:00
|
|
|
private $calendarYear;
|
|
|
|
private $calendarMonth;
|
2015-05-04 01:57:18 +02:00
|
|
|
private $calendarDay;
|
2015-05-03 01:17:05 +02:00
|
|
|
|
2014-06-12 22:22:20 +02:00
|
|
|
public function getResultTypeDescription() {
|
|
|
|
return pht('Calendar Events');
|
|
|
|
}
|
|
|
|
|
2015-02-05 00:47:48 +01:00
|
|
|
public function getApplicationClassName() {
|
2014-07-23 02:03:09 +02:00
|
|
|
return 'PhabricatorCalendarApplication';
|
2014-05-08 18:18:02 +02:00
|
|
|
}
|
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
public function newQuery() {
|
|
|
|
return new PhabricatorCalendarEventQuery();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function shouldShowOrderField() {
|
|
|
|
return false;
|
|
|
|
}
|
2014-02-06 19:10:18 +01:00
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
protected function buildCustomSearchFields() {
|
|
|
|
return array(
|
|
|
|
id(new PhabricatorSearchDatasourceField())
|
2016-07-14 21:54:36 +02:00
|
|
|
->setLabel(pht('Hosts'))
|
|
|
|
->setKey('hostPHIDs')
|
|
|
|
->setAliases(array('host', 'hostPHID', 'hosts'))
|
2015-06-22 22:27:37 +02:00
|
|
|
->setDatasource(new PhabricatorPeopleUserFunctionDatasource()),
|
|
|
|
id(new PhabricatorSearchDatasourceField())
|
|
|
|
->setLabel(pht('Invited'))
|
|
|
|
->setKey('invitedPHIDs')
|
|
|
|
->setDatasource(new PhabricatorPeopleUserFunctionDatasource()),
|
|
|
|
id(new PhabricatorSearchDateControlField())
|
|
|
|
->setLabel(pht('Occurs After'))
|
|
|
|
->setKey('rangeStart'),
|
|
|
|
id(new PhabricatorSearchDateControlField())
|
|
|
|
->setLabel(pht('Occurs Before'))
|
|
|
|
->setKey('rangeEnd')
|
|
|
|
->setAliases(array('rangeEnd')),
|
|
|
|
id(new PhabricatorSearchCheckboxesField())
|
|
|
|
->setKey('upcoming')
|
|
|
|
->setOptions(array(
|
|
|
|
'upcoming' => pht('Show only upcoming events.'),
|
|
|
|
)),
|
|
|
|
id(new PhabricatorSearchSelectField())
|
|
|
|
->setLabel(pht('Cancelled Events'))
|
|
|
|
->setKey('isCancelled')
|
|
|
|
->setOptions($this->getCancelledOptions())
|
|
|
|
->setDefault('active'),
|
2016-10-13 17:51:27 +02:00
|
|
|
id(new PhabricatorPHIDsSearchField())
|
|
|
|
->setLabel(pht('Import Sources'))
|
|
|
|
->setKey('importSourcePHIDs')
|
|
|
|
->setAliases(array('importSourcePHID')),
|
2015-06-22 22:27:37 +02:00
|
|
|
id(new PhabricatorSearchSelectField())
|
|
|
|
->setLabel(pht('Display Options'))
|
|
|
|
->setKey('display')
|
|
|
|
->setOptions($this->getViewOptions())
|
|
|
|
->setDefault('month'),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getCancelledOptions() {
|
|
|
|
return array(
|
|
|
|
'active' => pht('Active Events Only'),
|
|
|
|
'cancelled' => pht('Cancelled Events Only'),
|
|
|
|
'both' => pht('Both Cancelled and Active Events'),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getViewOptions() {
|
|
|
|
return array(
|
|
|
|
'month' => pht('Month View'),
|
|
|
|
'day' => pht('Day View'),
|
|
|
|
'list' => pht('List View'),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function buildQueryFromParameters(array $map) {
|
|
|
|
$query = $this->newQuery();
|
|
|
|
$viewer = $this->requireViewer();
|
|
|
|
|
2016-07-14 21:54:36 +02:00
|
|
|
if ($map['hostPHIDs']) {
|
|
|
|
$query->withHostPHIDs($map['hostPHIDs']);
|
2015-06-22 22:27:37 +02:00
|
|
|
}
|
2014-02-06 19:10:18 +01:00
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
if ($map['invitedPHIDs']) {
|
|
|
|
$query->withInvitedPHIDs($map['invitedPHIDs']);
|
|
|
|
}
|
2014-02-06 19:10:18 +01:00
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
$range_start = $map['rangeStart'];
|
|
|
|
$range_end = $map['rangeEnd'];
|
|
|
|
$display = $map['display'];
|
2014-02-06 19:10:18 +01:00
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
if ($map['upcoming'] && $map['upcoming'][0] == 'upcoming') {
|
|
|
|
$upcoming = true;
|
|
|
|
} else {
|
|
|
|
$upcoming = false;
|
|
|
|
}
|
2014-02-06 19:10:18 +01:00
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
list($range_start, $range_end) = $this->getQueryDateRange(
|
|
|
|
$range_start,
|
|
|
|
$range_end,
|
|
|
|
$display,
|
|
|
|
$upcoming);
|
2014-02-06 19:10:18 +01:00
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
$query->withDateRange($range_start, $range_end);
|
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 17:39:39 +02:00
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
switch ($map['isCancelled']) {
|
|
|
|
case 'active':
|
|
|
|
$query->withIsCancelled(false);
|
|
|
|
break;
|
|
|
|
case 'cancelled':
|
|
|
|
$query->withIsCancelled(true);
|
|
|
|
break;
|
|
|
|
}
|
2015-05-03 00:27:33 +02:00
|
|
|
|
2016-10-13 17:51:27 +02:00
|
|
|
if ($map['importSourcePHIDs']) {
|
|
|
|
$query->withImportSourcePHIDs($map['importSourcePHIDs']);
|
|
|
|
}
|
|
|
|
|
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 16:19:58 +02:00
|
|
|
// Generate ghosts (and ignore stub events) if we aren't querying for
|
2016-10-06 21:31:57 +02:00
|
|
|
// specific events or exporting.
|
|
|
|
if (!empty($map['export'])) {
|
|
|
|
// This is a specific mode enabled by event exports.
|
|
|
|
$query
|
|
|
|
->withIsStub(false);
|
|
|
|
} else if (!$map['ids'] && !$map['phids']) {
|
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 16:19:58 +02:00
|
|
|
$query
|
|
|
|
->withIsStub(false)
|
|
|
|
->setGenerateGhosts(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $query;
|
2014-02-06 19:10:18 +01:00
|
|
|
}
|
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
private function getQueryDateRange(
|
|
|
|
$start_date_wild,
|
|
|
|
$end_date_wild,
|
|
|
|
$display,
|
|
|
|
$upcoming) {
|
|
|
|
|
|
|
|
$start_date_value = $this->getSafeDate($start_date_wild);
|
|
|
|
$end_date_value = $this->getSafeDate($end_date_wild);
|
|
|
|
|
2015-05-03 01:17:05 +02:00
|
|
|
$viewer = $this->requireViewer();
|
2015-05-06 01:04:00 +02:00
|
|
|
$timezone = new DateTimeZone($viewer->getTimezoneIdentifier());
|
2015-06-22 22:27:37 +02:00
|
|
|
$min_range = null;
|
|
|
|
$max_range = null;
|
2014-02-06 19:10:18 +01:00
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
$min_range = $start_date_value->getEpoch();
|
|
|
|
$max_range = $end_date_value->getEpoch();
|
2015-06-16 22:38:11 +02:00
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
if ($display == 'month' || $display == 'day') {
|
2015-05-06 01:04:00 +02:00
|
|
|
list($start_year, $start_month, $start_day) =
|
2015-06-22 22:27:37 +02:00
|
|
|
$this->getDisplayYearAndMonthAndDay($min_range, $max_range, $display);
|
2015-05-06 01:04:00 +02:00
|
|
|
|
|
|
|
$start_day = new DateTime(
|
|
|
|
"{$start_year}-{$start_month}-{$start_day}",
|
|
|
|
$timezone);
|
|
|
|
$next = clone $start_day;
|
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
if ($display == 'month') {
|
2015-05-06 01:04:00 +02:00
|
|
|
$next->modify('+1 month');
|
2015-06-22 22:27:37 +02:00
|
|
|
} else if ($display == 'day') {
|
|
|
|
$next->modify('+7 day');
|
2015-05-06 01:04:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$display_start = $start_day->format('U');
|
|
|
|
$display_end = $next->format('U');
|
|
|
|
|
Convert some loadPreferences() to getUserSetting()
Summary:
Ref T4103. This doesn't get everything, but takes care of most of the easy stuff.
The tricky-ish bit here is that I need to move timezones, pronouns and translations to proper settings. I expect to pursue that next.
Test Plan:
- Grepped for `loadPreferences` to identify callsites.
- Changed start-of-week setting, loaded Calendar, saw correct start.
- Visited welcome page, read "Adjust Settings" point.
- Loaded Conpherence -- I changed behavior here slightly (switching threads drops the title glyph) but it wasn't consistent to start with and this seems like a good thing to push to the next version of Conpherence.
- Enabled Filetree, toggled in Differential.
- Disabled Filetree, no longer visible in Differential.
- Changed "Unified Diffs" preference to "Small Screens" vs "Always".
- Toggled filetree in Diffusion.
- Edited a task, saw sensible projects in policy dropdown.
- Viewed user profile, uncollapsed/collapsed side nav, reloaded page, sticky'd.
- Toggled "monospaced textareas", used a comment box, got appropriate fonts.
- Toggled durable column.
- Disabled title glyphs.
- Changed monospaced font to 18px/36px impact.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T4103
Differential Revision: https://secure.phabricator.com/D16004
2016-06-01 22:59:08 +02:00
|
|
|
$start_of_week = $viewer->getUserSetting(
|
|
|
|
PhabricatorWeekStartDaySetting::SETTINGKEY);
|
2015-05-17 05:13:25 +02:00
|
|
|
|
|
|
|
$end_of_week = ($start_of_week + 6) % 7;
|
2015-05-14 04:02:33 +02:00
|
|
|
|
|
|
|
$first_of_month = $start_day->format('w');
|
|
|
|
$last_of_month = id(clone $next)->modify('-1 day')->format('w');
|
|
|
|
|
2015-05-06 01:04:00 +02:00
|
|
|
if (!$min_range || ($min_range < $display_start)) {
|
|
|
|
$min_range = $display_start;
|
2015-05-14 04:02:33 +02:00
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
if ($display == 'month' &&
|
2015-05-17 05:13:25 +02:00
|
|
|
$first_of_month !== $start_of_week) {
|
|
|
|
$interim_day_num = ($first_of_month + 7 - $start_of_week) % 7;
|
2015-05-14 04:02:33 +02:00
|
|
|
$min_range = id(clone $start_day)
|
2015-05-17 05:13:25 +02:00
|
|
|
->modify('-'.$interim_day_num.' days')
|
2015-05-14 04:02:33 +02:00
|
|
|
->format('U');
|
|
|
|
}
|
2015-05-03 01:17:05 +02:00
|
|
|
}
|
2015-05-06 01:04:00 +02:00
|
|
|
if (!$max_range || ($max_range > $display_end)) {
|
|
|
|
$max_range = $display_end;
|
2015-05-14 04:02:33 +02:00
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
if ($display == 'month' &&
|
2015-05-17 05:13:25 +02:00
|
|
|
$last_of_month !== $end_of_week) {
|
|
|
|
$interim_day_num = ($end_of_week + 7 - $last_of_month) % 7;
|
2015-05-14 04:02:33 +02:00
|
|
|
$max_range = id(clone $next)
|
2015-05-17 05:13:25 +02:00
|
|
|
->modify('+'.$interim_day_num.' days')
|
2015-05-14 04:02:33 +02:00
|
|
|
->format('U');
|
|
|
|
}
|
2015-05-03 01:17:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
if ($upcoming) {
|
2016-07-13 18:29:02 +02:00
|
|
|
$now = PhabricatorTime::getNow();
|
2014-02-06 19:10:18 +01:00
|
|
|
if ($min_range) {
|
2016-07-13 18:29:02 +02:00
|
|
|
$min_range = max($now, $min_range);
|
2014-02-06 19:10:18 +01:00
|
|
|
} else {
|
2016-07-13 18:29:02 +02:00
|
|
|
$min_range = $now;
|
2014-02-06 19:10:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
return array($min_range, $max_range);
|
2014-02-06 19:10:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function getURI($path) {
|
2015-05-03 00:27:33 +02:00
|
|
|
return '/calendar/'.$path;
|
2014-02-06 19:10:18 +01:00
|
|
|
}
|
|
|
|
|
2015-01-06 21:34:51 +01:00
|
|
|
protected function getBuiltinQueryNames() {
|
2014-02-06 19:10:18 +01:00
|
|
|
$names = array(
|
2015-05-03 00:27:33 +02:00
|
|
|
'month' => pht('Month View'),
|
2015-05-08 06:18:48 +02:00
|
|
|
'day' => pht('Day View'),
|
2014-02-06 19:10:18 +01:00
|
|
|
'upcoming' => pht('Upcoming Events'),
|
2015-05-03 00:27:33 +02:00
|
|
|
'all' => pht('All Events'),
|
2014-02-06 19:10:18 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
return $names;
|
|
|
|
}
|
|
|
|
|
2015-05-05 21:51:32 +02:00
|
|
|
public function setCalendarYearAndMonthAndDay($year, $month, $day = null) {
|
2015-05-03 01:17:05 +02:00
|
|
|
$this->calendarYear = $year;
|
|
|
|
$this->calendarMonth = $month;
|
2015-05-05 21:51:32 +02:00
|
|
|
$this->calendarDay = $day;
|
2015-05-03 01:17:05 +02:00
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2014-02-06 19:10:18 +01:00
|
|
|
public function buildSavedQueryFromBuiltin($query_key) {
|
|
|
|
$query = $this->newSavedQuery();
|
|
|
|
$query->setQueryKey($query_key);
|
|
|
|
|
|
|
|
switch ($query_key) {
|
2015-05-03 00:27:33 +02:00
|
|
|
case 'month':
|
|
|
|
return $query->setParameter('display', 'month');
|
2015-05-08 06:18:48 +02:00
|
|
|
case 'day':
|
|
|
|
return $query->setParameter('display', 'day');
|
2014-02-06 19:10:18 +01:00
|
|
|
case 'upcoming':
|
2015-06-25 23:40:06 +02:00
|
|
|
return $query
|
|
|
|
->setParameter('display', 'list')
|
|
|
|
->setParameter('upcoming', array(
|
|
|
|
0 => 'upcoming',
|
|
|
|
));
|
2014-02-06 19:10:18 +01:00
|
|
|
case 'all':
|
|
|
|
return $query;
|
|
|
|
}
|
|
|
|
|
|
|
|
return parent::buildSavedQueryFromBuiltin($query_key);
|
|
|
|
}
|
|
|
|
|
2014-05-08 18:18:02 +02:00
|
|
|
protected function renderResultList(
|
|
|
|
array $events,
|
|
|
|
PhabricatorSavedQuery $query,
|
|
|
|
array $handles) {
|
2015-05-03 00:27:33 +02:00
|
|
|
|
2015-05-12 03:15:27 +02:00
|
|
|
if ($this->isMonthView($query)) {
|
2016-10-06 01:22:54 +02:00
|
|
|
$result = $this->buildCalendarMonthView($events, $query);
|
2015-05-12 03:15:27 +02:00
|
|
|
} else if ($this->isDayView($query)) {
|
2016-10-06 01:22:54 +02:00
|
|
|
$result = $this->buildCalendarDayView($events, $query);
|
|
|
|
} else {
|
|
|
|
$result = $this->buildCalendarListView($events, $query);
|
2015-05-03 00:27:33 +02:00
|
|
|
}
|
|
|
|
|
2016-10-06 01:22:54 +02:00
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function buildCalendarListView(
|
|
|
|
array $events,
|
|
|
|
PhabricatorSavedQuery $query) {
|
|
|
|
|
2014-05-08 18:18:02 +02:00
|
|
|
assert_instances_of($events, 'PhabricatorCalendarEvent');
|
|
|
|
$viewer = $this->requireViewer();
|
|
|
|
$list = new PHUIObjectItemListView();
|
2015-06-22 22:27:37 +02:00
|
|
|
|
2014-05-08 18:18:02 +02:00
|
|
|
foreach ($events as $event) {
|
2015-08-03 16:01:42 +02:00
|
|
|
if ($event->getIsGhostEvent()) {
|
2016-07-14 13:44:13 +02:00
|
|
|
$monogram = $event->getParentEvent()->getMonogram();
|
|
|
|
$index = $event->getSequenceIndex();
|
|
|
|
$monogram = "{$monogram}/{$index}";
|
2015-08-03 16:01:42 +02:00
|
|
|
} else {
|
2016-07-14 13:44:13 +02:00
|
|
|
$monogram = $event->getMonogram();
|
2015-08-03 16:01:42 +02:00
|
|
|
}
|
|
|
|
|
2014-05-08 18:18:02 +02:00
|
|
|
$item = id(new PHUIObjectItemView())
|
Events should offer Spaces as the view policy options
Summary: Ref T8687, Events should offer Spaces as the view policy options
Test Plan: Create event, choose default Space, save, edit, choose different Space, save, new policy should be reflected on Event.
Reviewers: epriestley, #blessed_reviewers
Reviewed By: epriestley, #blessed_reviewers
Subscribers: epriestley, Korvin
Maniphest Tasks: T8687
Differential Revision: https://secure.phabricator.com/D13459
2015-06-27 19:26:24 +02:00
|
|
|
->setUser($viewer)
|
|
|
|
->setObject($event)
|
2016-07-14 13:44:13 +02:00
|
|
|
->setObjectName($monogram)
|
|
|
|
->setHeader($event->getName())
|
2016-07-14 14:26:44 +02:00
|
|
|
->setHref($event->getURI());
|
|
|
|
|
|
|
|
$item->addAttribute($event->renderEventDate($viewer, false));
|
|
|
|
|
2016-10-31 21:42:10 +01:00
|
|
|
if ($event->getIsCancelled()) {
|
2016-07-14 14:26:44 +02:00
|
|
|
$item->setDisabled(true);
|
|
|
|
}
|
2016-02-07 19:00:14 +01:00
|
|
|
|
2016-07-14 18:47:46 +02:00
|
|
|
$status_icon = $event->getDisplayIcon($viewer);
|
|
|
|
$status_color = $event->getDisplayIconColor($viewer);
|
|
|
|
$status_label = $event->getDisplayIconLabel($viewer);
|
|
|
|
|
|
|
|
$item->setStatusIcon("{$status_icon} {$status_color}", $status_label);
|
2016-02-07 19:00:14 +01:00
|
|
|
|
2016-07-14 14:26:44 +02:00
|
|
|
$host = pht(
|
|
|
|
'Hosted by %s',
|
|
|
|
$viewer->renderHandle($event->getHostPHID()));
|
|
|
|
$item->addByline($host);
|
2016-02-07 19:00:14 +01:00
|
|
|
|
2014-05-08 18:18:02 +02:00
|
|
|
$list->addItem($item);
|
|
|
|
}
|
|
|
|
|
2016-10-18 23:24:17 +02:00
|
|
|
return $this->newResultView()
|
|
|
|
->setObjectList($list)
|
|
|
|
->setNoDataString(pht('No events found.'));
|
2014-05-08 18:18:02 +02:00
|
|
|
}
|
|
|
|
|
2016-07-27 15:58:28 +02:00
|
|
|
private function buildCalendarMonthView(
|
2016-07-14 18:47:46 +02:00
|
|
|
array $events,
|
|
|
|
PhabricatorSavedQuery $query) {
|
|
|
|
assert_instances_of($events, 'PhabricatorCalendarEvent');
|
2015-06-22 22:27:37 +02:00
|
|
|
|
2015-05-03 00:27:33 +02:00
|
|
|
$viewer = $this->requireViewer();
|
2016-07-14 18:47:46 +02:00
|
|
|
$now = PhabricatorTime::getNow();
|
2015-05-03 00:27:33 +02:00
|
|
|
|
2015-05-05 21:51:32 +02:00
|
|
|
list($start_year, $start_month) =
|
2015-06-22 22:27:37 +02:00
|
|
|
$this->getDisplayYearAndMonthAndDay(
|
|
|
|
$this->getQueryDateFrom($query)->getEpoch(),
|
|
|
|
$this->getQueryDateTo($query)->getEpoch(),
|
|
|
|
$query->getParameter('display'));
|
2015-05-03 00:27:33 +02:00
|
|
|
|
2016-07-14 18:47:46 +02:00
|
|
|
$now_year = phabricator_format_local_time($now, $viewer, 'Y');
|
2015-05-03 00:27:33 +02:00
|
|
|
$now_month = phabricator_format_local_time($now, $viewer, 'm');
|
2016-07-14 18:47:46 +02:00
|
|
|
$now_day = phabricator_format_local_time($now, $viewer, 'j');
|
2015-05-03 00:27:33 +02:00
|
|
|
|
2015-05-03 01:17:05 +02:00
|
|
|
if ($start_month == $now_month && $start_year == $now_year) {
|
|
|
|
$month_view = new PHUICalendarMonthView(
|
2015-06-22 22:27:37 +02:00
|
|
|
$this->getQueryDateFrom($query),
|
|
|
|
$this->getQueryDateTo($query),
|
2015-05-03 01:17:05 +02:00
|
|
|
$start_month,
|
|
|
|
$start_year,
|
|
|
|
$now_day);
|
2015-05-03 00:27:33 +02:00
|
|
|
} else {
|
2015-05-03 01:17:05 +02:00
|
|
|
$month_view = new PHUICalendarMonthView(
|
2015-06-22 22:27:37 +02:00
|
|
|
$this->getQueryDateFrom($query),
|
|
|
|
$this->getQueryDateTo($query),
|
2015-05-03 01:17:05 +02:00
|
|
|
$start_month,
|
|
|
|
$start_year);
|
2015-05-03 00:27:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$month_view->setUser($viewer);
|
|
|
|
|
2016-07-14 18:47:46 +02:00
|
|
|
foreach ($events as $event) {
|
2016-10-04 18:47:38 +02:00
|
|
|
$epoch_min = $event->getStartDateTimeEpoch();
|
|
|
|
$epoch_max = $event->getEndDateTimeEpoch();
|
2016-07-14 18:47:46 +02:00
|
|
|
|
|
|
|
$event_view = id(new AphrontCalendarEventView())
|
|
|
|
->setHostPHID($event->getHostPHID())
|
|
|
|
->setEpochRange($epoch_min, $epoch_max)
|
2016-10-31 21:42:10 +01:00
|
|
|
->setIsCancelled($event->getIsCancelled())
|
2016-07-14 18:47:46 +02:00
|
|
|
->setName($event->getName())
|
|
|
|
->setURI($event->getURI())
|
|
|
|
->setIsAllDay($event->getIsAllDay())
|
|
|
|
->setIcon($event->getDisplayIcon($viewer))
|
|
|
|
->setIconColor($event->getDisplayIconColor($viewer));
|
|
|
|
|
|
|
|
$month_view->addEvent($event_view);
|
2015-05-03 00:27:33 +02:00
|
|
|
}
|
|
|
|
|
2015-05-03 01:17:05 +02:00
|
|
|
$month_view->setBrowseURI(
|
|
|
|
$this->getURI('query/'.$query->getQueryKey().'/'));
|
|
|
|
|
2016-07-27 18:07:29 +02:00
|
|
|
$from = $this->getQueryDateFrom($query)->getDateTime();
|
|
|
|
|
|
|
|
$crumbs = array();
|
|
|
|
$crumbs[] = id(new PHUICrumbView())
|
|
|
|
->setName($from->format('F Y'));
|
|
|
|
|
|
|
|
$header = id(new PHUIHeaderView())
|
2016-08-01 21:06:35 +02:00
|
|
|
->setProfileHeader(true)
|
2016-07-27 18:07:29 +02:00
|
|
|
->setHeader($from->format('F Y'));
|
|
|
|
|
2016-10-18 23:24:17 +02:00
|
|
|
return $this->newResultView($month_view)
|
2016-07-27 18:07:29 +02:00
|
|
|
->setCrumbs($crumbs)
|
2016-10-18 23:24:17 +02:00
|
|
|
->setHeader($header);
|
2015-05-03 01:17:05 +02:00
|
|
|
}
|
|
|
|
|
2015-05-04 01:57:18 +02:00
|
|
|
private function buildCalendarDayView(
|
2016-07-15 23:02:24 +02:00
|
|
|
array $events,
|
|
|
|
PhabricatorSavedQuery $query) {
|
2015-06-22 22:27:37 +02:00
|
|
|
|
2015-05-04 01:57:18 +02:00
|
|
|
$viewer = $this->requireViewer();
|
2015-06-22 22:27:37 +02:00
|
|
|
|
2015-05-05 21:51:32 +02:00
|
|
|
list($start_year, $start_month, $start_day) =
|
2015-06-22 22:27:37 +02:00
|
|
|
$this->getDisplayYearAndMonthAndDay(
|
|
|
|
$this->getQueryDateFrom($query)->getEpoch(),
|
|
|
|
$this->getQueryDateTo($query)->getEpoch(),
|
|
|
|
$query->getParameter('display'));
|
2015-05-04 01:57:18 +02:00
|
|
|
|
2015-05-24 20:22:33 +02:00
|
|
|
$day_view = id(new PHUICalendarDayView(
|
2016-07-27 18:07:29 +02:00
|
|
|
$this->getQueryDateFrom($query),
|
|
|
|
$this->getQueryDateTo($query),
|
2015-05-04 01:57:18 +02:00
|
|
|
$start_year,
|
2015-05-05 21:51:32 +02:00
|
|
|
$start_month,
|
2015-05-24 20:22:33 +02:00
|
|
|
$start_day))
|
|
|
|
->setQuery($query->getQueryKey());
|
2015-05-04 01:57:18 +02:00
|
|
|
|
|
|
|
$day_view->setUser($viewer);
|
|
|
|
|
2016-07-15 23:02:24 +02:00
|
|
|
$phids = mpull($events, 'getHostPHID');
|
2015-05-15 04:20:44 +02:00
|
|
|
|
2016-07-15 23:02:24 +02:00
|
|
|
foreach ($events as $event) {
|
2015-05-24 20:22:33 +02:00
|
|
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
|
|
|
$viewer,
|
2016-07-15 23:02:24 +02:00
|
|
|
$event,
|
2015-05-24 20:22:33 +02:00
|
|
|
PhabricatorPolicyCapability::CAN_EDIT);
|
|
|
|
|
2016-10-04 18:47:38 +02:00
|
|
|
$epoch_min = $event->getStartDateTimeEpoch();
|
|
|
|
$epoch_max = $event->getEndDateTimeEpoch();
|
2016-07-15 23:02:24 +02:00
|
|
|
|
|
|
|
$status_icon = $event->getDisplayIcon($viewer);
|
|
|
|
$status_color = $event->getDisplayIconColor($viewer);
|
|
|
|
|
|
|
|
$event_view = id(new AphrontCalendarEventView())
|
|
|
|
->setCanEdit($can_edit)
|
|
|
|
->setEventID($event->getID())
|
|
|
|
->setEpochRange($epoch_min, $epoch_max)
|
|
|
|
->setIsAllDay($event->getIsAllDay())
|
|
|
|
->setIcon($status_icon)
|
|
|
|
->setIconColor($status_color)
|
|
|
|
->setName($event->getName())
|
|
|
|
->setURI($event->getURI())
|
2016-10-31 21:42:10 +01:00
|
|
|
->setIsCancelled($event->getIsCancelled());
|
2016-07-15 23:02:24 +02:00
|
|
|
|
|
|
|
$day_view->addEvent($event_view);
|
2015-05-04 01:57:18 +02:00
|
|
|
}
|
|
|
|
|
2016-07-27 18:07:29 +02:00
|
|
|
$browse_uri = $this->getURI('query/'.$query->getQueryKey().'/');
|
|
|
|
$day_view->setBrowseURI($browse_uri);
|
|
|
|
|
|
|
|
$from = $this->getQueryDateFrom($query)->getDateTime();
|
|
|
|
$month_uri = $browse_uri.$from->format('Y/m/');
|
|
|
|
|
|
|
|
$crumbs = array(
|
|
|
|
id(new PHUICrumbView())
|
|
|
|
->setName($from->format('F Y'))
|
|
|
|
->setHref($month_uri),
|
|
|
|
id(new PHUICrumbView())
|
|
|
|
->setName($from->format('D jS')),
|
|
|
|
);
|
|
|
|
|
|
|
|
$header = id(new PHUIHeaderView())
|
2016-08-01 21:06:35 +02:00
|
|
|
->setProfileHeader(true)
|
2016-07-27 18:07:29 +02:00
|
|
|
->setHeader($from->format('D, F jS'));
|
2015-05-05 21:51:32 +02:00
|
|
|
|
2016-10-18 23:24:17 +02:00
|
|
|
return $this->newResultView($day_view)
|
2016-07-27 18:07:29 +02:00
|
|
|
->setCrumbs($crumbs)
|
2016-10-18 23:24:17 +02:00
|
|
|
->setHeader($header);
|
2015-05-04 01:57:18 +02:00
|
|
|
}
|
|
|
|
|
2015-05-05 21:51:32 +02:00
|
|
|
private function getDisplayYearAndMonthAndDay(
|
2015-06-22 22:27:37 +02:00
|
|
|
$range_start,
|
|
|
|
$range_end,
|
|
|
|
$display) {
|
|
|
|
|
2015-05-03 01:17:05 +02:00
|
|
|
$viewer = $this->requireViewer();
|
2015-06-22 22:27:37 +02:00
|
|
|
$epoch = null;
|
|
|
|
|
2015-05-03 01:17:05 +02:00
|
|
|
if ($this->calendarYear && $this->calendarMonth) {
|
|
|
|
$start_year = $this->calendarYear;
|
|
|
|
$start_month = $this->calendarMonth;
|
2015-05-05 21:51:32 +02:00
|
|
|
$start_day = $this->calendarDay ? $this->calendarDay : 1;
|
2015-05-04 01:57:18 +02:00
|
|
|
} else {
|
2015-06-22 22:27:37 +02:00
|
|
|
if ($range_start) {
|
|
|
|
$epoch = $range_start;
|
|
|
|
} else if ($range_end) {
|
|
|
|
$epoch = $range_end;
|
|
|
|
} else {
|
|
|
|
$epoch = time();
|
2015-05-04 01:57:18 +02:00
|
|
|
}
|
2015-06-22 22:27:37 +02:00
|
|
|
if ($display == 'month') {
|
2015-05-12 03:15:27 +02:00
|
|
|
$day = 1;
|
|
|
|
} else {
|
|
|
|
$day = phabricator_format_local_time($epoch, $viewer, 'd');
|
|
|
|
}
|
2015-05-04 01:57:18 +02:00
|
|
|
$start_year = phabricator_format_local_time($epoch, $viewer, 'Y');
|
|
|
|
$start_month = phabricator_format_local_time($epoch, $viewer, 'm');
|
2015-05-12 03:15:27 +02:00
|
|
|
$start_day = $day;
|
2015-05-04 01:57:18 +02:00
|
|
|
}
|
|
|
|
return array($start_year, $start_month, $start_day);
|
|
|
|
}
|
|
|
|
|
2015-05-03 01:17:05 +02:00
|
|
|
public function getPageSize(PhabricatorSavedQuery $saved) {
|
2015-06-03 03:26:51 +02:00
|
|
|
if ($this->isMonthView($saved) || $this->isDayView($saved)) {
|
|
|
|
return $saved->getParameter('limit', 1000);
|
|
|
|
} else {
|
|
|
|
return $saved->getParameter('limit', 100);
|
|
|
|
}
|
2015-05-03 00:27:33 +02:00
|
|
|
}
|
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
private function getQueryDateFrom(PhabricatorSavedQuery $saved) {
|
2016-07-27 18:07:29 +02:00
|
|
|
if ($this->calendarYear && $this->calendarMonth) {
|
|
|
|
$viewer = $this->requireViewer();
|
|
|
|
|
|
|
|
$start_year = $this->calendarYear;
|
|
|
|
$start_month = $this->calendarMonth;
|
|
|
|
$start_day = $this->calendarDay ? $this->calendarDay : 1;
|
|
|
|
|
|
|
|
return AphrontFormDateControlValue::newFromDictionary(
|
|
|
|
$viewer,
|
|
|
|
array(
|
|
|
|
'd' => "{$start_year}-{$start_month}-{$start_day}",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
return $this->getQueryDate($saved, 'rangeStart');
|
2015-05-04 19:08:49 +02:00
|
|
|
}
|
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
private function getQueryDateTo(PhabricatorSavedQuery $saved) {
|
|
|
|
return $this->getQueryDate($saved, 'rangeEnd');
|
2015-05-04 19:08:49 +02:00
|
|
|
}
|
|
|
|
|
2015-06-22 22:27:37 +02:00
|
|
|
private function getQueryDate(PhabricatorSavedQuery $saved, $key) {
|
2015-05-04 19:08:49 +02:00
|
|
|
$viewer = $this->requireViewer();
|
|
|
|
|
|
|
|
$wild = $saved->getParameter($key);
|
2015-06-22 22:27:37 +02:00
|
|
|
return $this->getSafeDate($wild);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getSafeDate($value) {
|
|
|
|
$viewer = $this->requireViewer();
|
|
|
|
if ($value) {
|
|
|
|
// ideally this would be consistent and always pass in the same type
|
|
|
|
if ($value instanceof AphrontFormDateControlValue) {
|
|
|
|
return $value;
|
|
|
|
} else {
|
|
|
|
$value = AphrontFormDateControlValue::newFromWild($viewer, $value);
|
|
|
|
}
|
2015-05-04 19:08:49 +02:00
|
|
|
} else {
|
|
|
|
$value = AphrontFormDateControlValue::newFromEpoch(
|
|
|
|
$viewer,
|
2015-05-07 03:41:48 +02:00
|
|
|
PhabricatorTime::getTodayMidnightDateTime($viewer)->format('U'));
|
2015-05-04 19:08:49 +02:00
|
|
|
$value->setEnabled(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
$value->setOptional(true);
|
|
|
|
|
|
|
|
return $value;
|
|
|
|
}
|
|
|
|
|
2015-05-12 03:15:27 +02:00
|
|
|
private function isMonthView(PhabricatorSavedQuery $query) {
|
|
|
|
if ($this->isDayView($query)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ($query->getParameter('display') == 'month') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function isDayView(PhabricatorSavedQuery $query) {
|
|
|
|
if ($query->getParameter('display') == 'day') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if ($this->calendarDay) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2015-06-23 03:44:08 +02:00
|
|
|
|
2016-10-06 01:22:54 +02:00
|
|
|
public function newUseResultsActions(PhabricatorSavedQuery $saved) {
|
|
|
|
$viewer = $this->requireViewer();
|
|
|
|
$can_export = $viewer->isLoggedIn();
|
|
|
|
|
|
|
|
return array(
|
|
|
|
id(new PhabricatorActionView())
|
|
|
|
->setIcon('fa-download')
|
|
|
|
->setName(pht('Export Query as .ics'))
|
|
|
|
->setDisabled(!$can_export)
|
|
|
|
->setHref('/calendar/export/edit/?queryKey='.$saved->getQueryKey()),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-10-18 23:24:17 +02:00
|
|
|
|
|
|
|
private function newResultView($content = null) {
|
|
|
|
// If we aren't rendering a dashboard panel, activate global drag-and-drop
|
|
|
|
// so you can import ".ics" files by dropping them directly onto the
|
|
|
|
// calendar.
|
|
|
|
if (!$this->isPanelContext()) {
|
|
|
|
$drop_upload = id(new PhabricatorGlobalUploadTargetView())
|
|
|
|
->setViewer($this->requireViewer())
|
|
|
|
->setHintText("\xE2\x87\xAA ".pht('Drop .ics Files to Import'))
|
|
|
|
->setSubmitURI('/calendar/import/drop/')
|
|
|
|
->setViewPolicy(PhabricatorPolicies::POLICY_NOONE);
|
|
|
|
|
|
|
|
$content = array(
|
|
|
|
$drop_upload,
|
|
|
|
$content,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return id(new PhabricatorApplicationSearchResultView())
|
|
|
|
->setContent($content);
|
|
|
|
}
|
|
|
|
|
2014-02-06 19:10:18 +01:00
|
|
|
}
|