@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Use ApplicationSearch in the calendar event list view

Summary: Ref T4375. Basic ApplicationSearch integration to power this more flexibly.

Test Plan: {F108762}

Reviewers: btrahan, chad

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T4375

Differential Revision: https://secure.phabricator.com/D8148

+255 -82
+7 -1
src/__phutil_library_map__.php
··· 1267 1267 'PhabricatorCalendarEventListController' => 'applications/calendar/controller/PhabricatorCalendarEventListController.php', 1268 1268 'PhabricatorCalendarEventOverlapException' => 'applications/calendar/exception/PhabricatorCalendarEventOverlapException.php', 1269 1269 'PhabricatorCalendarEventQuery' => 'applications/calendar/query/PhabricatorCalendarEventQuery.php', 1270 + 'PhabricatorCalendarEventSearchEngine' => 'applications/calendar/query/PhabricatorCalendarEventSearchEngine.php', 1270 1271 'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php', 1271 1272 'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php', 1272 1273 'PhabricatorCampfireProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorCampfireProtocolAdapter.php', ··· 3907 3908 'PhabricatorCalendarEventDeleteController' => 'PhabricatorCalendarController', 3908 3909 'PhabricatorCalendarEventEditController' => 'PhabricatorCalendarController', 3909 3910 'PhabricatorCalendarEventInvalidEpochException' => 'Exception', 3910 - 'PhabricatorCalendarEventListController' => 'PhabricatorCalendarController', 3911 + 'PhabricatorCalendarEventListController' => 3912 + array( 3913 + 0 => 'PhabricatorCalendarController', 3914 + 1 => 'PhabricatorApplicationSearchResultsControllerInterface', 3915 + ), 3911 3916 'PhabricatorCalendarEventOverlapException' => 'Exception', 3912 3917 'PhabricatorCalendarEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 3918 + 'PhabricatorCalendarEventSearchEngine' => 'PhabricatorApplicationSearchEngine', 3913 3919 'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO', 3914 3920 'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase', 3915 3921 'PhabricatorCampfireProtocolAdapter' => 'PhabricatorBotBaseStreamingProtocolAdapter',
+2 -1
src/applications/calendar/application/PhabricatorApplicationCalendar.php
··· 37 37 '/calendar/' => array( 38 38 '' => 'PhabricatorCalendarBrowseController', 39 39 'event/' => array( 40 - '' => 'PhabricatorCalendarEventListController', 40 + '(?:query/(?P<queryKey>[^/]+)/)?' => 41 + 'PhabricatorCalendarEventListController', 41 42 'create/' => 42 43 'PhabricatorCalendarEventEditController', 43 44 'edit/(?P<id>[1-9]\d*)/' =>
+46 -76
src/applications/calendar/controller/PhabricatorCalendarEventListController.php
··· 1 1 <?php 2 2 3 3 final class PhabricatorCalendarEventListController 4 - extends PhabricatorCalendarController { 4 + extends PhabricatorCalendarController 5 + implements PhabricatorApplicationSearchResultsControllerInterface { 5 6 6 - private $phid; 7 + private $queryKey; 7 8 8 9 public function willProcessRequest(array $data) { 9 - $user = $this->getRequest()->getUser(); 10 - $this->phid = idx($data, 'phid', $user->getPHID()); 11 - $this->loadHandles(array($this->phid)); 10 + $this->queryKey = idx($data, 'queryKey'); 12 11 } 13 12 14 13 public function processRequest() { 15 - $request = $this->getRequest(); 16 - $user = $request->getUser(); 17 - $handle = $this->getHandle($this->phid); 14 + $request = $this->getRequest(); 15 + $controller = id(new PhabricatorApplicationSearchController($request)) 16 + ->setQueryKey($this->queryKey) 17 + ->setSearchEngine(new PhabricatorCalendarEventSearchEngine()) 18 + ->setNavigation($this->buildSideNav()); 19 + return $this->delegateToController($controller); 20 + } 21 + 22 + public function buildSideNav() { 23 + $user = $this->getRequest()->getUser(); 24 + 25 + $nav = new AphrontSideNavFilterView(); 26 + $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); 18 27 19 - $statuses = id(new PhabricatorCalendarEventQuery()) 28 + id(new PhabricatorCalendarEventSearchEngine()) 20 29 ->setViewer($user) 21 - ->withInvitedPHIDs(array($this->phid)) 22 - ->withDateRange(time(), strtotime('2037-01-01 12:00:00')) 23 - ->execute(); 30 + ->addNavigationItems($nav->getMenu()); 24 31 25 - $nav = $this->buildSideNavView(); 26 - $nav->selectFilter($this->getFilter()); 32 + $nav->selectFilter(null); 27 33 28 - $page_title = $this->getPageTitle(); 34 + return $nav; 35 + } 29 36 30 - $status_list = $this->buildStatusList($statuses); 31 - $status_list->setNoDataString($this->getNoDataString()); 37 + public function buildApplicationCrumbs() { 38 + $crumbs = parent::buildApplicationCrumbs(); 32 39 33 - $nav->appendChild( 34 - array( 35 - id(new PHUIHeaderView())->setHeader($page_title), 36 - $status_list, 37 - )); 40 + $crumbs->addAction( 41 + id(new PHUIListItemView()) 42 + ->setIcon('create') 43 + ->setName(pht('Create Event')) 44 + ->setHref($this->getApplicationURI().'create/')); 38 45 39 - return $this->buildApplicationPage( 40 - $nav, 41 - array( 42 - 'title' => $page_title, 43 - 'device' => true 44 - )); 46 + return $crumbs; 45 47 } 46 48 47 - private function buildStatusList(array $statuses) { 48 - assert_instances_of($statuses, 'PhabricatorCalendarEvent'); 49 + public function renderResultsList( 50 + array $events, 51 + PhabricatorSavedQuery $query) { 52 + assert_instances_of($events, 'PhabricatorCalendarEvent'); 49 53 50 - $user = $this->getRequest()->getUser(); 54 + $viewer = $this->getRequest()->getUser(); 51 55 52 56 $list = new PHUIObjectItemListView(); 53 - foreach ($statuses as $status) { 54 - if ($status->getUserPHID() == $user->getPHID()) { 55 - $href = $this->getApplicationURI('/event/edit/'.$status->getID().'/'); 57 + foreach ($events as $event) { 58 + if ($event->getUserPHID() == $viewer->getPHID()) { 59 + $href = $this->getApplicationURI('/event/edit/'.$event->getID().'/'); 56 60 } else { 57 - $from = $status->getDateFrom(); 58 - $month = phabricator_format_local_time($from, $user, 'm'); 59 - $year = phabricator_format_local_time($from, $user, 'Y'); 61 + $from = $event->getDateFrom(); 62 + $month = phabricator_format_local_time($from, $viewer, 'm'); 63 + $year = phabricator_format_local_time($from, $viewer, 'Y'); 60 64 $uri = new PhutilURI($this->getApplicationURI()); 61 65 $uri->setQueryParams( 62 66 array( ··· 65 69 )); 66 70 $href = (string) $uri; 67 71 } 68 - $from = phabricator_datetime($status->getDateFrom(), $user); 69 - $to = phabricator_datetime($status->getDateTo(), $user); 72 + $from = phabricator_datetime($event->getDateFrom(), $viewer); 73 + $to = phabricator_datetime($event->getDateTo(), $viewer); 70 74 71 - $color = ($status->getStatus() == PhabricatorCalendarEvent::STATUS_AWAY) 75 + $color = ($event->getStatus() == PhabricatorCalendarEvent::STATUS_AWAY) 72 76 ? 'red' 73 77 : 'yellow'; 74 78 75 79 $item = id(new PHUIObjectItemView()) 76 - ->setHeader($status->getTerseSummary($user)) 80 + ->setHeader($event->getTerseSummary($viewer)) 77 81 ->setHref($href) 78 82 ->setBarColor($color) 79 83 ->addAttribute(pht('From %s to %s', $from, $to)) 80 84 ->addAttribute( 81 - phutil_utf8_shorten($status->getDescription(), 64)); 85 + phutil_utf8_shorten($event->getDescription(), 64)); 82 86 83 87 $list->addItem($item); 84 88 } 85 89 86 90 return $list; 87 - } 88 - 89 - private function getNoDataString() { 90 - if ($this->isUserRequest()) { 91 - $no_data = 92 - pht('You do not have any upcoming status events.'); 93 - } else { 94 - $no_data = 95 - pht('%s does not have any upcoming status events.', 96 - $this->getHandle($this->phid)->getName()); 97 - } 98 - return $no_data; 99 - } 100 - 101 - private function getFilter() { 102 - $filter = 'event/'; 103 - 104 - return $filter; 105 - } 106 - 107 - private function getPageTitle() { 108 - if ($this->isUserRequest()) { 109 - $page_title = pht('Upcoming Statuses'); 110 - } else { 111 - $page_title = pht( 112 - 'Upcoming Statuses for %s', 113 - $this->getHandle($this->phid)->getName()); 114 - } 115 - return $page_title; 116 - } 117 - 118 - private function isUserRequest() { 119 - $user = $this->getRequest()->getUser(); 120 - return $this->phid == $user->getPHID(); 121 91 } 122 92 123 93 }
+25 -3
src/applications/calendar/query/PhabricatorCalendarEventQuery.php
··· 7 7 private $rangeBegin; 8 8 private $rangeEnd; 9 9 private $invitedPHIDs; 10 + private $creatorPHIDs; 10 11 11 12 public function withIDs(array $ids) { 12 13 $this->ids = $ids; ··· 21 22 22 23 public function withInvitedPHIDs(array $phids) { 23 24 $this->invitedPHIDs = $phids; 25 + return $this; 26 + } 27 + 28 + public function withCreatorPHIDs(array $phids) { 29 + $this->creatorPHIDs = $phids; 24 30 return $this; 25 31 } 26 32 ··· 49 55 $this->ids); 50 56 } 51 57 52 - if ($this->rangeBegin || $this->rangeEnd) { 58 + if ($this->rangeBegin) { 53 59 $where[] = qsprintf( 54 60 $conn_r, 55 - 'dateTo >= %d AND dateFrom <= %d', 56 - $this->rangeBegin, 61 + 'dateTo >= %d', 62 + $this->rangeBegin); 63 + } 64 + 65 + if ($this->rangeEnd) { 66 + $where[] = qsprintf( 67 + $conn_r, 68 + 'dateFrom <= %d', 57 69 $this->rangeEnd); 58 70 } 71 + 72 + // TODO: Currently, the creator is always the only invitee, but you can 73 + // query them separately since this won't always be true. 59 74 60 75 if ($this->invitedPHIDs) { 61 76 $where[] = qsprintf( 62 77 $conn_r, 63 78 'userPHID IN (%Ls)', 64 79 $this->invitedPHIDs); 80 + } 81 + 82 + if ($this->creatorPHIDs) { 83 + $where[] = qsprintf( 84 + $conn_r, 85 + 'userPHID IN (%Ls)', 86 + $this->creatorPHIDs); 65 87 } 66 88 67 89 $where[] = $this->buildPagingClause($conn_r);
+163
src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php
··· 1 + <?php 2 + 3 + final class PhabricatorCalendarEventSearchEngine 4 + extends PhabricatorApplicationSearchEngine { 5 + 6 + public function buildSavedQueryFromRequest(AphrontRequest $request) { 7 + $saved = new PhabricatorSavedQuery(); 8 + 9 + $saved->setParameter( 10 + 'rangeStart', 11 + $this->readDateFromRequest($request, 'rangeStart')); 12 + 13 + $saved->setParameter( 14 + 'rangeEnd', 15 + $this->readDateFromRequest($request, 'rangeEnd')); 16 + 17 + $saved->setParameter( 18 + 'upcoming', 19 + $this->readBoolFromRequest($request, 'upcoming')); 20 + 21 + $saved->setParameter( 22 + 'invitedPHIDs', 23 + $this->readUsersFromRequest($request, 'invited')); 24 + 25 + $saved->setParameter( 26 + 'creatorPHIDs', 27 + $this->readUsersFromRequest($request, 'creators')); 28 + 29 + return $saved; 30 + } 31 + 32 + public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 33 + $query = id(new PhabricatorCalendarEventQuery()); 34 + 35 + $min_range = null; 36 + $max_range = null; 37 + 38 + if ($saved->getParameter('rangeStart')) { 39 + $min_range = $saved->getParameter('rangeStart'); 40 + } 41 + 42 + if ($saved->getParameter('rangeEnd')) { 43 + $max_range = $saved->getParameter('rangeEnd'); 44 + } 45 + 46 + if ($saved->getParameter('upcoming')) { 47 + if ($min_range) { 48 + $min_range = max(time(), $min_range); 49 + } else { 50 + $min_range = time(); 51 + } 52 + } 53 + 54 + if ($min_range || $max_range) { 55 + $query->withDateRange($min_range, $max_range); 56 + } 57 + 58 + $invited_phids = $saved->getParameter('invitedPHIDs'); 59 + if ($invited_phids) { 60 + $query->withInvitedPHIDs($invited_phids); 61 + } 62 + 63 + $creator_phids = $saved->getParameter('creatorPHIDs'); 64 + if ($creator_phids) { 65 + $query->withCreatorPHIDs($creator_phids); 66 + } 67 + 68 + return $query; 69 + } 70 + 71 + public function buildSearchForm( 72 + AphrontFormView $form, 73 + PhabricatorSavedQuery $saved) { 74 + 75 + $range_start = $saved->getParameter('rangeStart'); 76 + $range_end = $saved->getParameter('rangeEnd'); 77 + $upcoming = $saved->getParameter('upcoming'); 78 + 79 + $invited_phids = $saved->getParameter('invitedPHIDs', array()); 80 + $creator_phids = $saved->getParameter('creatorPHIDs', array()); 81 + 82 + $all_phids = array_merge( 83 + $invited_phids, 84 + $creator_phids); 85 + 86 + if ($all_phids) { 87 + $handles = id(new PhabricatorHandleQuery()) 88 + ->setViewer($this->requireViewer()) 89 + ->withPHIDs($all_phids) 90 + ->execute(); 91 + } else { 92 + $handles = array(); 93 + } 94 + 95 + $invited_handles = array_select_keys($handles, $invited_phids); 96 + $creator_handles = array_select_keys($handles, $creator_phids); 97 + 98 + $form 99 + ->appendChild( 100 + id(new AphrontFormTokenizerControl()) 101 + ->setDatasource('/typeahead/common/accounts/') 102 + ->setName('creators') 103 + ->setLabel(pht('Created By')) 104 + ->setValue($creator_handles)) 105 + ->appendChild( 106 + id(new AphrontFormTokenizerControl()) 107 + ->setDatasource('/typeahead/common/accounts/') 108 + ->setName('invited') 109 + ->setLabel(pht('Invited')) 110 + ->setValue($invited_handles)) 111 + ->appendChild( 112 + id(new AphrontFormDateControl()) 113 + ->setLabel(pht('Occurs After')) 114 + ->setUser($this->requireViewer()) 115 + ->setName('rangeStart') 116 + ->setAllowNull(true) 117 + ->setValue($range_start)) 118 + ->appendChild( 119 + id(new AphrontFormDateControl()) 120 + ->setLabel(pht('Occurs Before')) 121 + ->setUser($this->requireViewer()) 122 + ->setName('rangeEnd') 123 + ->setAllowNull(true) 124 + ->setValue($range_end)) 125 + ->appendChild( 126 + id(new AphrontFormCheckboxControl()) 127 + ->addCheckbox( 128 + 'upcoming', 129 + 1, 130 + pht('Show only upcoming events.'), 131 + $upcoming)); 132 + 133 + } 134 + 135 + protected function getURI($path) { 136 + return '/calendar/event/'.$path; 137 + } 138 + 139 + public function getBuiltinQueryNames() { 140 + $names = array( 141 + 'upcoming' => pht('Upcoming Events'), 142 + 'all' => pht('All Events'), 143 + ); 144 + 145 + return $names; 146 + } 147 + 148 + public function buildSavedQueryFromBuiltin($query_key) { 149 + 150 + $query = $this->newSavedQuery(); 151 + $query->setQueryKey($query_key); 152 + 153 + switch ($query_key) { 154 + case 'upcoming': 155 + return $query->setParameter('upcoming', true); 156 + case 'all': 157 + return $query; 158 + } 159 + 160 + return parent::buildSavedQueryFromBuiltin($query_key); 161 + } 162 + 163 + }
+11
src/applications/search/engine/PhabricatorApplicationSearchEngine.php
··· 368 368 return $list; 369 369 } 370 370 371 + protected function readDateFromRequest( 372 + AphrontRequest $request, 373 + $key) { 374 + 375 + return id(new AphrontFormDateControl()) 376 + ->setUser($this->requireViewer()) 377 + ->setName($key) 378 + ->setAllowNull(true) 379 + ->readValueFromRequest($request); 380 + } 381 + 371 382 protected function readBoolFromRequest( 372 383 AphrontRequest $request, 373 384 $key) {
+1 -1
src/view/form/control/AphrontFormDateControl.php
··· 87 87 $result = parent::setValue($epoch); 88 88 89 89 if ($epoch === null) { 90 - return; 90 + return $result; 91 91 } 92 92 93 93 $readable = $this->formatTime($epoch, 'Y!m!d!g:i A');