@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

Update Phrequent to use new search infrastructure.

Summary:
This updates Phrequent to use new the search infrastructure. Now it looks like:

{F60141}

I've also added the policy infrastructure stubs, but it's probably not even close to being right in terms of enforcing policies (in particular being able to see time tracked against objects the user wouldn't normally be able to see).

At some point I'd like to be able to filter on the objects that the time is tracked against, but I don't believe there's a tokenizer / readahead control that allows you to type any kind of object.

Test Plan: Clicked around the new interface, created some custom queries and saved them.

Reviewers: epriestley

CC: Korvin, aran

Maniphest Tasks: T3870

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

authored by

James Rhodes and committed by
epriestley
e4a07e01 27df293a

+329 -310
+13 -3
src/__phutil_library_map__.php
··· 1917 1917 'PhrequentController' => 'applications/phrequent/controller/PhrequentController.php', 1918 1918 'PhrequentDAO' => 'applications/phrequent/storage/PhrequentDAO.php', 1919 1919 'PhrequentListController' => 'applications/phrequent/controller/PhrequentListController.php', 1920 + 'PhrequentSearchEngine' => 'applications/phrequent/query/PhrequentSearchEngine.php', 1920 1921 'PhrequentTrackController' => 'applications/phrequent/controller/PhrequentTrackController.php', 1921 1922 'PhrequentTrackableInterface' => 'applications/phrequent/interface/PhrequentTrackableInterface.php', 1922 1923 'PhrequentUIEventListener' => 'applications/phrequent/event/PhrequentUIEventListener.php', ··· 4130 4131 'PhortuneWePayPaymentProvider' => 'PhortunePaymentProvider', 4131 4132 'PhrequentController' => 'PhabricatorController', 4132 4133 'PhrequentDAO' => 'PhabricatorLiskDAO', 4133 - 'PhrequentListController' => 'PhrequentController', 4134 + 'PhrequentListController' => 4135 + array( 4136 + 0 => 'PhrequentController', 4137 + 1 => 'PhabricatorApplicationSearchResultsControllerInterface', 4138 + ), 4139 + 'PhrequentSearchEngine' => 'PhabricatorApplicationSearchEngine', 4134 4140 'PhrequentTrackController' => 'PhrequentController', 4135 4141 'PhrequentUIEventListener' => 'PhutilEventListener', 4136 - 'PhrequentUserTime' => 'PhrequentDAO', 4137 - 'PhrequentUserTimeQuery' => 'PhabricatorOffsetPagedQuery', 4142 + 'PhrequentUserTime' => 4143 + array( 4144 + 0 => 'PhrequentDAO', 4145 + 1 => 'PhabricatorPolicyInterface', 4146 + ), 4147 + 'PhrequentUserTimeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4138 4148 'PhrictionActionConstants' => 'PhrictionConstants', 4139 4149 'PhrictionActionMenuEventListener' => 'PhutilEventListener', 4140 4150 'PhrictionChangeType' => 'PhrictionConstants',
+1 -2
src/applications/phrequent/application/PhabricatorApplicationPhrequent.php
··· 35 35 public function getRoutes() { 36 36 return array( 37 37 '/phrequent/' => array( 38 - '' => 'PhrequentListController', 39 - 'view/(?P<view>\w+)/' => 'PhrequentListController', 38 + '(?:query/(?P<queryKey>[^/]+)/)?' => 'PhrequentListController', 40 39 'track/(?P<verb>[a-z]+)/(?P<phid>[^/]+)/' 41 40 => 'PhrequentTrackController' 42 41 ),
+8 -9
src/applications/phrequent/controller/PhrequentController.php
··· 2 2 3 3 abstract class PhrequentController extends PhabricatorController { 4 4 5 - protected function buildNav($view) { 5 + protected function buildSideNavView() { 6 + $user = $this->getRequest()->getUser(); 7 + 6 8 $nav = new AphrontSideNavFilterView(); 7 - $nav->setBaseURI(new PhutilURI('/phrequent/view/')); 9 + $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); 8 10 9 - $nav->addLabel(pht('User Times')); 10 - $nav->addFilter('current', pht('Currently Tracking')); 11 - $nav->addFilter('recent', pht('Recent Activity')); 12 - $nav->addLabel('All Times'); 13 - $nav->addFilter('allcurrent', pht('Currently Tracking')); 14 - $nav->addFilter('allrecent', pht('Recent Activity')); 11 + id(new PhrequentSearchEngine()) 12 + ->setViewer($user) 13 + ->addNavigationItems($nav->getMenu()); 15 14 16 - $nav->selectFilter($view); 15 + $nav->selectFilter(null); 17 16 18 17 return $nav; 19 18 }
+60 -255
src/applications/phrequent/controller/PhrequentListController.php
··· 1 1 <?php 2 2 3 - final class PhrequentListController extends PhrequentController { 3 + final class PhrequentListController extends PhrequentController 4 + implements PhabricatorApplicationSearchResultsControllerInterface { 4 5 5 - private $view; 6 + private $queryKey; 6 7 7 - public function willProcessRequest(array $data) { 8 - $this->view = idx($data, 'view', "current"); 8 + public function shouldAllowPublic() { 9 + return true; 9 10 } 10 11 11 - private function getArrToStrList($key) { 12 - $arr = $this->getRequest()->getArr($key); 13 - $arr = implode(',', $arr); 14 - return nonempty($arr, null); 12 + public function willProcessRequest(array $data) { 13 + $this->queryKey = idx($data, 'queryKey'); 15 14 } 16 15 17 16 public function processRequest() { 18 17 $request = $this->getRequest(); 19 - $user = $request->getUser(); 20 - 21 - if ($request->isFormPost()) { 22 - // Redirect to GET so URIs can be copy/pasted. 23 - 24 - $order = $request->getStr('o'); 25 - $order = nonempty($order, null); 26 - 27 - $ended = $request->getStr('e'); 28 - $ended = nonempty($ended, null); 29 - 30 - $uri = $request->getRequestURI() 31 - ->alter('users', $this->getArrToStrList('set_users')) 32 - ->alter('o', $order) 33 - ->alter('e', $ended); 34 - 35 - return id(new AphrontRedirectResponse())->setURI($uri); 36 - } 37 - 38 - $nav = $this->buildNav($this->view); 39 - 40 - $has_user_filter = array( 41 - "current" => true, 42 - "recent" => true, 43 - ); 44 - 45 - $user_phids = $request->getStrList('users', array()); 46 - if (isset($has_user_filter[$this->view])) { 47 - $user_phids = array($user->getPHID()); 48 - } 49 - 50 - switch ($this->view) { 51 - case "current": 52 - case "allcurrent": 53 - $order_key_default = "s"; 54 - $ended_key_default = "n"; 55 - break; 56 - case "recent": 57 - case "allrecent": 58 - $order_key_default = "s"; 59 - $ended_key_default = "y"; 60 - break; 61 - default: 62 - $order_key_default = "s"; 63 - $ended_key_default = "a"; 64 - break; 65 - } 66 - 67 - switch ($request->getStr('o', $order_key_default)) { 68 - case 's': 69 - $order = PhrequentUserTimeQuery::ORDER_STARTED; 70 - break; 71 - case 'e': 72 - $order = PhrequentUserTimeQuery::ORDER_ENDED; 73 - break; 74 - case 'd': 75 - $order = PhrequentUserTimeQuery::ORDER_DURATION; 76 - break; 77 - default: 78 - throw new Exception("Unknown order!"); 79 - } 80 - 81 - switch ($request->getStr('e', $ended_key_default)) { 82 - case 'a': 83 - $ended = PhrequentUserTimeQuery::ENDED_ALL; 84 - break; 85 - case 'y': 86 - $ended = PhrequentUserTimeQuery::ENDED_YES; 87 - break; 88 - case 'n': 89 - $ended = PhrequentUserTimeQuery::ENDED_NO; 90 - break; 91 - default: 92 - throw new Exception("Unknown ended!"); 93 - } 94 - 95 - $filter = new AphrontListFilterView(); 96 - $filter->appendChild( 97 - $this->buildForm($user_phids, $order_key_default, $ended_key_default)); 98 - 99 - $query = new PhrequentUserTimeQuery(); 100 - $query->setOrder($order); 101 - $query->setEnded($ended); 102 - $query->setUsers($user_phids); 103 - 104 - $pager = new AphrontPagerView(); 105 - $pager->setPageSize(500); 106 - $pager->setOffset($request->getInt('offset')); 107 - $pager->setURI($request->getRequestURI(), 'offset'); 108 - 109 - $logs = $query->executeWithOffsetPager($pager); 110 - 111 - $title = pht('Time Tracked'); 112 - 113 - $table = $this->buildTableView($logs); 114 - $table->appendChild($pager); 18 + $controller = id(new PhabricatorApplicationSearchController($request)) 19 + ->setQueryKey($this->queryKey) 20 + ->setSearchEngine(new PhrequentSearchEngine()) 21 + ->setNavigation($this->buildSideNavView()); 115 22 116 - $nav->appendChild( 117 - array( 118 - $filter, 119 - $table, 120 - $pager, 121 - )); 122 - 123 - $crumbs = $this->buildApplicationCrumbs(); 124 - $crumbs->addCrumb( 125 - id(new PhabricatorCrumbView()) 126 - ->setName($title) 127 - ->setHref($this->getApplicationURI('/'))); 128 - 129 - $nav->setCrumbs($crumbs); 130 - 131 - return $this->buildApplicationPage( 132 - $nav, 133 - array( 134 - 'title' => $title, 135 - 'device' => true, 136 - )); 137 - 23 + return $this->delegateToController($controller); 138 24 } 139 25 140 - protected function buildForm(array $user_phids, $order_key_default, 141 - $ended_key_default) { 142 - $request = $this->getRequest(); 143 - $user = $request->getUser(); 26 + public function renderResultsList( 27 + array $usertimes, 28 + PhabricatorSavedQuery $query) { 29 + assert_instances_of($usertimes, 'PhrequentUserTime'); 30 + $viewer = $this->getRequest()->getUser(); 144 31 145 - $form = id(new AphrontFormView()) 146 - ->setUser($user) 147 - ->setAction($this->getApplicationURI("/view/custom/")); 32 + $phids = array(); 33 + $phids[] = mpull($usertimes, 'getUserPHID'); 34 + $phids[] = mpull($usertimes, 'getObjectPHID'); 35 + $phids = array_mergev($phids); 148 36 149 - $user_handles = id(new PhabricatorHandleQuery()) 150 - ->setViewer($user) 151 - ->withPHIDs($user_phids) 152 - ->execute(); 153 - $tokens = array(); 154 - foreach ($user_phids as $phid) { 155 - $tokens[$phid] = $user_handles[$phid]->getFullName(); 156 - } 157 - $form->appendChild( 158 - id(new AphrontFormTokenizerControl()) 159 - ->setDatasource('/typeahead/common/searchowner/') 160 - ->setName('set_users') 161 - ->setLabel(pht('Users')) 162 - ->setValue($tokens)); 37 + $handles = $this->loadViewerHandles($phids); 163 38 164 - $form->appendChild( 165 - id(new AphrontFormToggleButtonsControl()) 166 - ->setName('o') 167 - ->setLabel(pht('Sort Order')) 168 - ->setBaseURI($request->getRequestURI(), 'o') 169 - ->setValue($request->getStr('o', $order_key_default)) 170 - ->setButtons( 171 - array( 172 - 's' => pht('Started'), 173 - 'e' => pht('Ended'), 174 - 'd' => pht('Duration'), 175 - ))); 39 + $view = id(new PHUIObjectItemListView()) 40 + ->setUser($viewer); 176 41 177 - $form->appendChild( 178 - id(new AphrontFormToggleButtonsControl()) 179 - ->setName('e') 180 - ->setLabel(pht('Ended')) 181 - ->setBaseURI($request->getRequestURI(), 'e') 182 - ->setValue($request->getStr('e', $ended_key_default)) 183 - ->setButtons( 184 - array( 185 - 'y' => pht('Yes'), 186 - 'n' => pht('No'), 187 - 'a' => pht('All'), 188 - ))); 42 + foreach ($usertimes as $usertime) { 43 + $item = new PHUIObjectItemView(); 189 44 190 - $form->appendChild( 191 - id(new AphrontFormSubmitControl())->setValue(pht('Filter Objects'))); 45 + if ($usertime->getObjectPHID() === null) { 46 + $item->setHeader($usertime->getNote()); 47 + } else { 48 + $obj = $handles[$usertime->getObjectPHID()]; 49 + $item->setHeader($obj->getLinkName()); 50 + $item->setHref($obj->getURI()); 51 + } 52 + $item->setObject($usertime); 192 53 193 - return $form; 194 - } 54 + $item->addByline( 55 + pht( 56 + 'Tracked: %s', 57 + $handles[$usertime->getUserPHID()]->renderLink())); 195 58 196 - protected function buildTableView(array $usertimes) { 197 - assert_instances_of($usertimes, 'PhrequentUserTime'); 198 - 199 - $user = $this->getRequest()->getUser(); 200 - 201 - $phids = array(); 202 - foreach ($usertimes as $usertime) { 203 - $phids[] = $usertime->getUserPHID(); 204 - $phids[] = $usertime->getObjectPHID(); 205 - } 206 - $handles = $this->loadViewerHandles($phids); 207 - 208 - $rows = array(); 209 - foreach ($usertimes as $usertime) { 59 + $started_date = phabricator_date($usertime->getDateStarted(), $viewer); 60 + $item->addIcon('none', $started_date); 210 61 211 62 if ($usertime->getDateEnded() !== null) { 212 63 $time_spent = $usertime->getDateEnded() - $usertime->getDateStarted(); 213 - $time_ended = phabricator_datetime($usertime->getDateEnded(), $user); 64 + $time_ended = phabricator_datetime($usertime->getDateEnded(), $viewer); 214 65 } else { 215 66 $time_spent = time() - $usertime->getDateStarted(); 216 - $time_ended = phutil_tag( 217 - 'em', 218 - array(), 219 - pht('Ongoing')); 220 67 } 221 68 222 - $usertime_user = $handles[$usertime->getUserPHID()]; 223 - $usertime_object = null; 224 - $object = null; 225 - if ($usertime->getObjectPHID() !== null) { 226 - $usertime_object = $handles[$usertime->getObjectPHID()]; 227 - $object = phutil_tag( 228 - 'a', 229 - array( 230 - 'href' => $usertime_object->getURI() 231 - ), 232 - $usertime_object->getFullName()); 69 + $time_spent = $time_spent == 0 ? 'none' : 70 + phabricator_format_relative_time_detailed($time_spent); 71 + 72 + if ($usertime->getDateEnded() !== null) { 73 + $item->addAttribute( 74 + pht( 75 + 'Tracked %s', 76 + $time_spent)); 77 + $item->addAttribute( 78 + pht( 79 + 'Ended on %s', 80 + $time_ended)); 233 81 } else { 234 - $object = phutil_tag( 235 - 'em', 236 - array(), 237 - pht('None')); 82 + $item->addAttribute( 83 + pht( 84 + 'Tracked %s so far', 85 + $time_spent)); 86 + $item->setBarColor('green'); 238 87 } 239 88 240 - $rows[] = array( 241 - $object, 242 - phutil_tag( 243 - 'a', 244 - array( 245 - 'href' => $usertime_user->getURI() 246 - ), 247 - $usertime_user->getFullName()), 248 - phabricator_datetime($usertime->getDateStarted(), $user), 249 - $time_ended, 250 - $time_spent == 0 ? 'none' : 251 - phabricator_format_relative_time_detailed($time_spent), 252 - $usertime->getNote() 253 - ); 89 + $view->addItem($item); 254 90 } 255 91 256 - $table = new AphrontTableView($rows); 257 - $table->setDeviceReadyTable(true); 258 - $table->setHeaders( 259 - array( 260 - 'Object', 261 - 'User', 262 - 'Started', 263 - 'Ended', 264 - 'Duration', 265 - 'Note' 266 - )); 267 - $table->setShortHeaders( 268 - array( 269 - 'O', 270 - 'U', 271 - 'S', 272 - 'E', 273 - 'D', 274 - 'Note', 275 - '', 276 - )); 277 - $table->setColumnClasses( 278 - array( 279 - '', 280 - '', 281 - '', 282 - '', 283 - '', 284 - 'wide' 285 - )); 286 - 287 - return $table; 92 + return $view; 288 93 } 289 94 290 95 }
+114
src/applications/phrequent/query/PhrequentSearchEngine.php
··· 1 + <?php 2 + 3 + final class PhrequentSearchEngine 4 + extends PhabricatorApplicationSearchEngine { 5 + 6 + public function getPageSize(PhabricatorSavedQuery $saved) { 7 + return $saved->getParameter('limit', 1000); 8 + } 9 + 10 + public function buildSavedQueryFromRequest(AphrontRequest $request) { 11 + $saved = new PhabricatorSavedQuery(); 12 + 13 + $saved->setParameter( 14 + 'userPHIDs', 15 + $this->readUsersFromRequest($request, 'users')); 16 + 17 + $saved->setParameter('ended', $request->getStr('ended')); 18 + 19 + $saved->setParameter('order', $request->getStr('order')); 20 + 21 + return $saved; 22 + } 23 + 24 + public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 25 + $query = id(new PhrequentUserTimeQuery()); 26 + 27 + $user_phids = $saved->getParameter('userPHIDs'); 28 + if ($user_phids) { 29 + $query->withUserPHIDs($user_phids); 30 + } 31 + 32 + $ended = $saved->getParameter('ended'); 33 + if ($ended != null) { 34 + $query->withEnded($ended); 35 + } 36 + 37 + $order = $saved->getParameter('order'); 38 + if ($order != null) { 39 + $query->setOrder($order); 40 + } 41 + 42 + return $query; 43 + } 44 + 45 + public function buildSearchForm( 46 + AphrontFormView $form, 47 + PhabricatorSavedQuery $saved_query) { 48 + 49 + $user_phids = $saved_query->getParameter('userPHIDs', array()); 50 + $ended = $saved_query->getParameter( 51 + 'ended', PhrequentUserTimeQuery::ENDED_ALL); 52 + $order = $saved_query->getParameter( 53 + 'order', PhrequentUserTimeQuery::ORDER_ENDED_DESC); 54 + 55 + $phids = array_merge($user_phids); 56 + $handles = id(new PhabricatorHandleQuery()) 57 + ->setViewer($this->requireViewer()) 58 + ->withPHIDs($phids) 59 + ->execute(); 60 + 61 + $form 62 + ->appendChild( 63 + id(new AphrontFormTokenizerControl()) 64 + ->setDatasource('/typeahead/common/users/') 65 + ->setName('users') 66 + ->setLabel(pht('Users')) 67 + ->setValue($handles)) 68 + ->appendChild( 69 + id(new AphrontFormSelectControl()) 70 + ->setLabel(pht('Ended')) 71 + ->setName('ended') 72 + ->setValue($ended) 73 + ->setOptions(PhrequentUserTimeQuery::getEndedSearchOptions())) 74 + ->appendChild( 75 + id(new AphrontFormSelectControl()) 76 + ->setLabel(pht('Order')) 77 + ->setName('order') 78 + ->setValue($order) 79 + ->setOptions(PhrequentUserTimeQuery::getOrderSearchOptions())); 80 + } 81 + 82 + protected function getURI($path) { 83 + return '/phrequent/'.$path; 84 + } 85 + 86 + public function getBuiltinQueryNames() { 87 + $names = array( 88 + 'tracking' => pht('Currently Tracking'), 89 + 'all' => pht('All Tracked'), 90 + ); 91 + 92 + return $names; 93 + } 94 + 95 + public function buildSavedQueryFromBuiltin($query_key) { 96 + 97 + $query = $this->newSavedQuery(); 98 + $query->setQueryKey($query_key); 99 + 100 + switch ($query_key) { 101 + case 'all': 102 + return $query 103 + ->setParameter('order', PhrequentUserTimeQuery::ORDER_ENDED_DESC); 104 + case 'tracking': 105 + return $query 106 + ->setParameter('ended', PhrequentUserTimeQuery::ENDED_NO) 107 + ->setParameter('order', PhrequentUserTimeQuery::ORDER_ENDED_DESC); 108 + } 109 + 110 + return parent::buildSavedQueryFromBuiltin($query_key); 111 + } 112 + 113 + } 114 +
+103 -40
src/applications/phrequent/query/PhrequentUserTimeQuery.php
··· 1 1 <?php 2 2 3 - final class PhrequentUserTimeQuery extends PhabricatorOffsetPagedQuery { 3 + final class PhrequentUserTimeQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 4 5 5 - const ORDER_ID = 'order-id'; 6 - const ORDER_STARTED = 'order-started'; 7 - const ORDER_ENDED = 'order-ended'; 8 - const ORDER_DURATION = 'order-duration'; 6 + const ORDER_ID_ASC = 0; 7 + const ORDER_ID_DESC = 1; 8 + const ORDER_STARTED_ASC = 2; 9 + const ORDER_STARTED_DESC = 3; 10 + const ORDER_ENDED_ASC = 4; 11 + const ORDER_ENDED_DESC = 5; 12 + const ORDER_DURATION_ASC = 6; 13 + const ORDER_DURATION_DESC = 7; 9 14 10 - const ENDED_YES = "ended-yes"; 11 - const ENDED_NO = "ended-no"; 12 - const ENDED_ALL = "ended-all"; 15 + const ENDED_YES = 0; 16 + const ENDED_NO = 1; 17 + const ENDED_ALL = 2; 13 18 14 19 private $userPHIDs; 15 20 private $objectPHIDs; 16 - private $order = self::ORDER_ID; 21 + private $order = self::ORDER_ID_ASC; 17 22 private $ended = self::ENDED_ALL; 18 23 19 - public function setUsers($user_phids) { 24 + public function withUserPHIDs($user_phids) { 20 25 $this->userPHIDs = $user_phids; 21 26 return $this; 22 27 } 23 28 24 - public function setObjects($object_phids) { 29 + public function withObjectPHIDs($object_phids) { 25 30 $this->objectPHIDs = $object_phids; 26 31 return $this; 27 32 } 28 33 29 - public function setOrder($order) { 30 - $this->order = $order; 31 - return $this; 32 - } 33 - 34 - public function setEnded($ended) { 34 + public function withEnded($ended) { 35 35 $this->ended = $ended; 36 36 return $this; 37 37 } 38 38 39 - public function execute() { 40 - $usertime_dao = new PhrequentUserTime(); 41 - $conn = $usertime_dao->establishConnection('r'); 42 - 43 - $data = queryfx_all( 44 - $conn, 45 - 'SELECT usertime.* FROM %T usertime %Q %Q %Q', 46 - $usertime_dao->getTableName(), 47 - $this->buildWhereClause($conn), 48 - $this->buildOrderClause($conn), 49 - $this->buildLimitClause($conn)); 50 - 51 - return $usertime_dao->loadAllFromArray($data); 39 + public function setOrder($order) { 40 + $this->order = $order; 41 + return $this; 52 42 } 53 43 54 44 private function buildWhereClause(AphrontDatabaseConnection $conn) { ··· 85 75 throw new Exception("Unknown ended '{$this->ended}'!"); 86 76 } 87 77 78 + $where[] = $this->buildPagingClause($conn); 79 + 88 80 return $this->formatWhereClause($where); 89 81 } 90 82 91 - private function buildOrderClause(AphrontDatabaseConnection $conn) { 83 + protected function getPagingColumn() { 84 + switch ($this->order) { 85 + case self::ORDER_ID_ASC: 86 + case self::ORDER_ID_DESC: 87 + return 'id'; 88 + case self::ORDER_STARTED_ASC: 89 + case self::ORDER_STARTED_DESC: 90 + return 'dateStarted'; 91 + case self::ORDER_ENDED_ASC: 92 + case self::ORDER_ENDED_DESC: 93 + return 'dateEnded'; 94 + case self::ORDER_DURATION_ASC: 95 + case self::ORDER_DURATION_DESC: 96 + return 'COALESCE(dateEnded, UNIX_TIMESTAMP()) - dateStarted'; 97 + default: 98 + throw new Exception("Unknown order '{$this->order}'!"); 99 + } 100 + } 101 + 102 + protected function getPagingValue($result) { 92 103 switch ($this->order) { 93 - case self::ORDER_ID: 94 - return 'ORDER BY id ASC'; 95 - case self::ORDER_STARTED: 96 - return 'ORDER BY dateStarted DESC'; 97 - case self::ORDER_ENDED: 98 - return 'ORDER BY dateEnded IS NULL, dateEnded DESC, dateStarted DESC'; 99 - case self::ORDER_DURATION: 100 - return 'ORDER BY COALESCE(dateEnded, UNIX_TIMESTAMP()) - dateStarted '. 101 - 'DESC'; 104 + case self::ORDER_ID_ASC: 105 + case self::ORDER_ID_DESC: 106 + return $result->getID(); 107 + case self::ORDER_STARTED_ASC: 108 + case self::ORDER_STARTED_DESC: 109 + return $result->getDateStarted(); 110 + case self::ORDER_ENDED_ASC: 111 + case self::ORDER_ENDED_DESC: 112 + return $result->getDateEnded(); 113 + case self::ORDER_DURATION_ASC: 114 + case self::ORDER_DURATION_DESC: 115 + return ($result->getDateEnded() || time()) - $result->getDateStarted(); 116 + default: 117 + throw new Exception("Unknown order '{$this->order}'!"); 118 + } 119 + } 120 + 121 + protected function getReversePaging() { 122 + switch ($this->order) { 123 + case self::ORDER_ID_ASC: 124 + case self::ORDER_STARTED_ASC: 125 + case self::ORDER_ENDED_ASC: 126 + case self::ORDER_DURATION_ASC: 127 + return true; 128 + case self::ORDER_ID_DESC: 129 + case self::ORDER_STARTED_DESC: 130 + case self::ORDER_ENDED_DESC: 131 + case self::ORDER_DURATION_DESC: 132 + return false; 102 133 default: 103 134 throw new Exception("Unknown order '{$this->order}'!"); 104 135 } 136 + } 137 + 138 + protected function loadPage() { 139 + $usertime = new PhrequentUserTime(); 140 + $conn = $usertime->establishConnection('r'); 141 + 142 + $data = queryfx_all( 143 + $conn, 144 + 'SELECT usertime.* FROM %T usertime %Q %Q %Q', 145 + $usertime->getTableName(), 146 + $this->buildWhereClause($conn), 147 + $this->buildOrderClause($conn), 148 + $this->buildLimitClause($conn)); 149 + 150 + return $usertime->loadAllFromArray($data); 105 151 } 106 152 107 153 /* -( Helper Functions ) --------------------------------------------------- */ 154 + 155 + public static function getEndedSearchOptions() { 156 + return array( 157 + self::ENDED_ALL => pht('All'), 158 + self::ENDED_NO => pht('No'), 159 + self::ENDED_YES => pht('Yes')); 160 + } 161 + 162 + public static function getOrderSearchOptions() { 163 + return array( 164 + self::ORDER_STARTED_ASC => pht('by furthest start date'), 165 + self::ORDER_STARTED_DESC => pht('by nearest start date'), 166 + self::ORDER_ENDED_ASC => pht('by furthest end date'), 167 + self::ORDER_ENDED_DESC => pht('by nearest end date'), 168 + self::ORDER_DURATION_ASC => pht('by smallest duration'), 169 + self::ORDER_DURATION_DESC => pht('by largest duration')); 170 + } 108 171 109 172 public static function getUserTotalObjectsTracked( 110 173 PhabricatorUser $user) {
+30 -1
src/applications/phrequent/storage/PhrequentUserTime.php
··· 3 3 /** 4 4 * @group phrequent 5 5 */ 6 - final class PhrequentUserTime extends PhrequentDAO { 6 + final class PhrequentUserTime extends PhrequentDAO 7 + implements PhabricatorPolicyInterface { 7 8 8 9 protected $userPHID; 9 10 protected $objectPHID; 10 11 protected $note; 11 12 protected $dateStarted; 12 13 protected $dateEnded; 14 + 15 + public function getCapabilities() { 16 + return array( 17 + PhabricatorPolicyCapability::CAN_VIEW, 18 + ); 19 + } 20 + 21 + public function getPolicy($capability) { 22 + $policy = PhabricatorPolicies::POLICY_NOONE; 23 + 24 + switch ($capability) { 25 + case PhabricatorPolicyCapability::CAN_VIEW: 26 + $policy = PhabricatorPolicies::POLICY_USER; 27 + break; 28 + } 29 + 30 + return $policy; 31 + } 32 + 33 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 34 + return ($viewer->getPHID() == $this->getUserPHID()); 35 + } 36 + 37 + 38 + public function describeAutomaticCapability($capability) { 39 + return pht( 40 + 'The user who tracked time can always view it.'); 41 + } 13 42 14 43 }