mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-10 14:51:06 +01:00
ba00022730
Summary: Ref T11326. This doesn't go quite as far as the mock in T11326#185932, but gets rid of the easy margins. Also cleans up some of the border rules so they're simpler and more consistent (no weird ragged edges on the far right). Test Plan: {F1738951} Reviewers: chad Reviewed By: chad Maniphest Tasks: T11326 Differential Revision: https://secure.phabricator.com/D16335
524 lines
15 KiB
PHP
524 lines
15 KiB
PHP
<?php
|
|
|
|
final class PhabricatorCalendarEventSearchEngine
|
|
extends PhabricatorApplicationSearchEngine {
|
|
|
|
private $calendarYear;
|
|
private $calendarMonth;
|
|
private $calendarDay;
|
|
|
|
public function getResultTypeDescription() {
|
|
return pht('Calendar Events');
|
|
}
|
|
|
|
public function getApplicationClassName() {
|
|
return 'PhabricatorCalendarApplication';
|
|
}
|
|
|
|
public function newQuery() {
|
|
return new PhabricatorCalendarEventQuery();
|
|
}
|
|
|
|
protected function shouldShowOrderField() {
|
|
return false;
|
|
}
|
|
|
|
protected function buildCustomSearchFields() {
|
|
return array(
|
|
id(new PhabricatorSearchDatasourceField())
|
|
->setLabel(pht('Hosts'))
|
|
->setKey('hostPHIDs')
|
|
->setAliases(array('host', 'hostPHID', 'hosts'))
|
|
->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'),
|
|
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();
|
|
|
|
if ($map['hostPHIDs']) {
|
|
$query->withHostPHIDs($map['hostPHIDs']);
|
|
}
|
|
|
|
if ($map['invitedPHIDs']) {
|
|
$query->withInvitedPHIDs($map['invitedPHIDs']);
|
|
}
|
|
|
|
$range_start = $map['rangeStart'];
|
|
$range_end = $map['rangeEnd'];
|
|
$display = $map['display'];
|
|
|
|
if ($map['upcoming'] && $map['upcoming'][0] == 'upcoming') {
|
|
$upcoming = true;
|
|
} else {
|
|
$upcoming = false;
|
|
}
|
|
|
|
list($range_start, $range_end) = $this->getQueryDateRange(
|
|
$range_start,
|
|
$range_end,
|
|
$display,
|
|
$upcoming);
|
|
|
|
$query->withDateRange($range_start, $range_end);
|
|
|
|
switch ($map['isCancelled']) {
|
|
case 'active':
|
|
$query->withIsCancelled(false);
|
|
break;
|
|
case 'cancelled':
|
|
$query->withIsCancelled(true);
|
|
break;
|
|
}
|
|
|
|
// 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(
|
|
$start_date_wild,
|
|
$end_date_wild,
|
|
$display,
|
|
$upcoming) {
|
|
|
|
$start_date_value = $this->getSafeDate($start_date_wild);
|
|
$end_date_value = $this->getSafeDate($end_date_wild);
|
|
|
|
$viewer = $this->requireViewer();
|
|
$timezone = new DateTimeZone($viewer->getTimezoneIdentifier());
|
|
$min_range = null;
|
|
$max_range = null;
|
|
|
|
$min_range = $start_date_value->getEpoch();
|
|
$max_range = $end_date_value->getEpoch();
|
|
|
|
if ($display == 'month' || $display == 'day') {
|
|
list($start_year, $start_month, $start_day) =
|
|
$this->getDisplayYearAndMonthAndDay($min_range, $max_range, $display);
|
|
|
|
$start_day = new DateTime(
|
|
"{$start_year}-{$start_month}-{$start_day}",
|
|
$timezone);
|
|
$next = clone $start_day;
|
|
|
|
if ($display == 'month') {
|
|
$next->modify('+1 month');
|
|
} else if ($display == 'day') {
|
|
$next->modify('+7 day');
|
|
}
|
|
|
|
$display_start = $start_day->format('U');
|
|
$display_end = $next->format('U');
|
|
|
|
$start_of_week = $viewer->getUserSetting(
|
|
PhabricatorWeekStartDaySetting::SETTINGKEY);
|
|
|
|
$end_of_week = ($start_of_week + 6) % 7;
|
|
|
|
$first_of_month = $start_day->format('w');
|
|
$last_of_month = id(clone $next)->modify('-1 day')->format('w');
|
|
|
|
if (!$min_range || ($min_range < $display_start)) {
|
|
$min_range = $display_start;
|
|
|
|
if ($display == 'month' &&
|
|
$first_of_month !== $start_of_week) {
|
|
$interim_day_num = ($first_of_month + 7 - $start_of_week) % 7;
|
|
$min_range = id(clone $start_day)
|
|
->modify('-'.$interim_day_num.' days')
|
|
->format('U');
|
|
}
|
|
}
|
|
if (!$max_range || ($max_range > $display_end)) {
|
|
$max_range = $display_end;
|
|
|
|
if ($display == 'month' &&
|
|
$last_of_month !== $end_of_week) {
|
|
$interim_day_num = ($end_of_week + 7 - $last_of_month) % 7;
|
|
$max_range = id(clone $next)
|
|
->modify('+'.$interim_day_num.' days')
|
|
->format('U');
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($upcoming) {
|
|
$now = PhabricatorTime::getNow();
|
|
if ($min_range) {
|
|
$min_range = max($now, $min_range);
|
|
} else {
|
|
$min_range = $now;
|
|
}
|
|
}
|
|
|
|
return array($min_range, $max_range);
|
|
}
|
|
|
|
protected function getURI($path) {
|
|
return '/calendar/'.$path;
|
|
}
|
|
|
|
protected function getBuiltinQueryNames() {
|
|
$names = array(
|
|
'month' => pht('Month View'),
|
|
'day' => pht('Day View'),
|
|
'upcoming' => pht('Upcoming Events'),
|
|
'all' => pht('All Events'),
|
|
);
|
|
|
|
return $names;
|
|
}
|
|
|
|
public function setCalendarYearAndMonthAndDay($year, $month, $day = null) {
|
|
$this->calendarYear = $year;
|
|
$this->calendarMonth = $month;
|
|
$this->calendarDay = $day;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function buildSavedQueryFromBuiltin($query_key) {
|
|
$query = $this->newSavedQuery();
|
|
$query->setQueryKey($query_key);
|
|
|
|
switch ($query_key) {
|
|
case 'month':
|
|
return $query->setParameter('display', 'month');
|
|
case 'day':
|
|
return $query->setParameter('display', 'day');
|
|
case 'upcoming':
|
|
return $query
|
|
->setParameter('display', 'list')
|
|
->setParameter('upcoming', array(
|
|
0 => 'upcoming',
|
|
));
|
|
case 'all':
|
|
return $query;
|
|
}
|
|
|
|
return parent::buildSavedQueryFromBuiltin($query_key);
|
|
}
|
|
|
|
protected function renderResultList(
|
|
array $events,
|
|
PhabricatorSavedQuery $query,
|
|
array $handles) {
|
|
|
|
if ($this->isMonthView($query)) {
|
|
return $this->buildCalendarMonthView($events, $query);
|
|
} else if ($this->isDayView($query)) {
|
|
return $this->buildCalendarDayView($events, $query);
|
|
}
|
|
|
|
assert_instances_of($events, 'PhabricatorCalendarEvent');
|
|
$viewer = $this->requireViewer();
|
|
$list = new PHUIObjectItemListView();
|
|
|
|
foreach ($events as $event) {
|
|
if ($event->getIsGhostEvent()) {
|
|
$monogram = $event->getParentEvent()->getMonogram();
|
|
$index = $event->getSequenceIndex();
|
|
$monogram = "{$monogram}/{$index}";
|
|
} else {
|
|
$monogram = $event->getMonogram();
|
|
}
|
|
|
|
$item = id(new PHUIObjectItemView())
|
|
->setUser($viewer)
|
|
->setObject($event)
|
|
->setObjectName($monogram)
|
|
->setHeader($event->getName())
|
|
->setHref($event->getURI());
|
|
|
|
$item->addAttribute($event->renderEventDate($viewer, false));
|
|
|
|
if ($event->isCancelledEvent()) {
|
|
$item->setDisabled(true);
|
|
}
|
|
|
|
$status_icon = $event->getDisplayIcon($viewer);
|
|
$status_color = $event->getDisplayIconColor($viewer);
|
|
$status_label = $event->getDisplayIconLabel($viewer);
|
|
|
|
$item->setStatusIcon("{$status_icon} {$status_color}", $status_label);
|
|
|
|
$host = pht(
|
|
'Hosted by %s',
|
|
$viewer->renderHandle($event->getHostPHID()));
|
|
$item->addByline($host);
|
|
|
|
$list->addItem($item);
|
|
}
|
|
|
|
$result = new PhabricatorApplicationSearchResultView();
|
|
$result->setObjectList($list);
|
|
$result->setNoDataString(pht('No events found.'));
|
|
|
|
return $result;
|
|
}
|
|
|
|
private function buildCalendarMonthView(
|
|
array $events,
|
|
PhabricatorSavedQuery $query) {
|
|
assert_instances_of($events, 'PhabricatorCalendarEvent');
|
|
|
|
$viewer = $this->requireViewer();
|
|
$now = PhabricatorTime::getNow();
|
|
|
|
list($start_year, $start_month) =
|
|
$this->getDisplayYearAndMonthAndDay(
|
|
$this->getQueryDateFrom($query)->getEpoch(),
|
|
$this->getQueryDateTo($query)->getEpoch(),
|
|
$query->getParameter('display'));
|
|
|
|
$now_year = phabricator_format_local_time($now, $viewer, 'Y');
|
|
$now_month = phabricator_format_local_time($now, $viewer, 'm');
|
|
$now_day = phabricator_format_local_time($now, $viewer, 'j');
|
|
|
|
if ($start_month == $now_month && $start_year == $now_year) {
|
|
$month_view = new PHUICalendarMonthView(
|
|
$this->getQueryDateFrom($query),
|
|
$this->getQueryDateTo($query),
|
|
$start_month,
|
|
$start_year,
|
|
$now_day);
|
|
} else {
|
|
$month_view = new PHUICalendarMonthView(
|
|
$this->getQueryDateFrom($query),
|
|
$this->getQueryDateTo($query),
|
|
$start_month,
|
|
$start_year);
|
|
}
|
|
|
|
$month_view->setUser($viewer);
|
|
|
|
foreach ($events as $event) {
|
|
$epoch_min = $event->getViewerDateFrom();
|
|
$epoch_max = $event->getViewerDateTo();
|
|
|
|
$event_view = id(new AphrontCalendarEventView())
|
|
->setHostPHID($event->getHostPHID())
|
|
->setEpochRange($epoch_min, $epoch_max)
|
|
->setIsCancelled($event->isCancelledEvent())
|
|
->setName($event->getName())
|
|
->setURI($event->getURI())
|
|
->setIsAllDay($event->getIsAllDay())
|
|
->setIcon($event->getDisplayIcon($viewer))
|
|
->setIconColor($event->getDisplayIconColor($viewer));
|
|
|
|
$month_view->addEvent($event_view);
|
|
}
|
|
|
|
$month_view->setBrowseURI(
|
|
$this->getURI('query/'.$query->getQueryKey().'/'));
|
|
|
|
return id(new PhabricatorApplicationSearchResultView())
|
|
->setContent($month_view)
|
|
->setCollapsed(true);
|
|
}
|
|
|
|
private function buildCalendarDayView(
|
|
array $events,
|
|
PhabricatorSavedQuery $query) {
|
|
|
|
$viewer = $this->requireViewer();
|
|
|
|
list($start_year, $start_month, $start_day) =
|
|
$this->getDisplayYearAndMonthAndDay(
|
|
$this->getQueryDateFrom($query)->getEpoch(),
|
|
$this->getQueryDateTo($query)->getEpoch(),
|
|
$query->getParameter('display'));
|
|
|
|
$day_view = id(new PHUICalendarDayView(
|
|
$this->getQueryDateFrom($query)->getEpoch(),
|
|
$this->getQueryDateTo($query)->getEpoch(),
|
|
$start_year,
|
|
$start_month,
|
|
$start_day))
|
|
->setQuery($query->getQueryKey());
|
|
|
|
$day_view->setUser($viewer);
|
|
|
|
$phids = mpull($events, 'getHostPHID');
|
|
|
|
foreach ($events as $event) {
|
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
|
$viewer,
|
|
$event,
|
|
PhabricatorPolicyCapability::CAN_EDIT);
|
|
|
|
$epoch_min = $event->getViewerDateFrom();
|
|
$epoch_max = $event->getViewerDateTo();
|
|
|
|
$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())
|
|
->setIsCancelled($event->isCancelledEvent());
|
|
|
|
$day_view->addEvent($event_view);
|
|
}
|
|
|
|
$day_view->setBrowseURI(
|
|
$this->getURI('query/'.$query->getQueryKey().'/'));
|
|
|
|
return id(new PhabricatorApplicationSearchResultView())
|
|
->setContent($day_view)
|
|
->setCollapsed(true);
|
|
}
|
|
|
|
private function getDisplayYearAndMonthAndDay(
|
|
$range_start,
|
|
$range_end,
|
|
$display) {
|
|
|
|
$viewer = $this->requireViewer();
|
|
$epoch = null;
|
|
|
|
if ($this->calendarYear && $this->calendarMonth) {
|
|
$start_year = $this->calendarYear;
|
|
$start_month = $this->calendarMonth;
|
|
$start_day = $this->calendarDay ? $this->calendarDay : 1;
|
|
} else {
|
|
if ($range_start) {
|
|
$epoch = $range_start;
|
|
} else if ($range_end) {
|
|
$epoch = $range_end;
|
|
} else {
|
|
$epoch = time();
|
|
}
|
|
if ($display == 'month') {
|
|
$day = 1;
|
|
} else {
|
|
$day = phabricator_format_local_time($epoch, $viewer, 'd');
|
|
}
|
|
$start_year = phabricator_format_local_time($epoch, $viewer, 'Y');
|
|
$start_month = phabricator_format_local_time($epoch, $viewer, 'm');
|
|
$start_day = $day;
|
|
}
|
|
return array($start_year, $start_month, $start_day);
|
|
}
|
|
|
|
public function getPageSize(PhabricatorSavedQuery $saved) {
|
|
if ($this->isMonthView($saved) || $this->isDayView($saved)) {
|
|
return $saved->getParameter('limit', 1000);
|
|
} else {
|
|
return $saved->getParameter('limit', 100);
|
|
}
|
|
}
|
|
|
|
private function getQueryDateFrom(PhabricatorSavedQuery $saved) {
|
|
return $this->getQueryDate($saved, 'rangeStart');
|
|
}
|
|
|
|
private function getQueryDateTo(PhabricatorSavedQuery $saved) {
|
|
return $this->getQueryDate($saved, 'rangeEnd');
|
|
}
|
|
|
|
private function getQueryDate(PhabricatorSavedQuery $saved, $key) {
|
|
$viewer = $this->requireViewer();
|
|
|
|
$wild = $saved->getParameter($key);
|
|
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);
|
|
}
|
|
} else {
|
|
$value = AphrontFormDateControlValue::newFromEpoch(
|
|
$viewer,
|
|
PhabricatorTime::getTodayMidnightDateTime($viewer)->format('U'));
|
|
$value->setEnabled(false);
|
|
}
|
|
|
|
$value->setOptional(true);
|
|
|
|
return $value;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
}
|