@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.

Support ordering in SearchField

Summary:
Ref T8441. Ref T7715. Automatically generate a modern "Order" control in ApplicationSearch for engines which fully support SearchField.

Notably, this allows the standard "Order" control to automatically support custom field orders. We do this in Maniphest today, but in an ad-hoc way.

Test Plan: Performed order-by queries in Almanac (Services), Pholio, Files, People, Projects, and Paste.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T7715, T8441

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

+229 -154
+2
src/__phutil_library_map__.php
··· 2520 2520 'PhabricatorSearchManagementInitWorkflow' => 'applications/search/management/PhabricatorSearchManagementInitWorkflow.php', 2521 2521 'PhabricatorSearchManagementWorkflow' => 'applications/search/management/PhabricatorSearchManagementWorkflow.php', 2522 2522 'PhabricatorSearchOrderController' => 'applications/search/controller/PhabricatorSearchOrderController.php', 2523 + 'PhabricatorSearchOrderField' => 'applications/search/field/PhabricatorSearchOrderField.php', 2523 2524 'PhabricatorSearchOwnersField' => 'applications/search/field/PhabricatorSearchOwnersField.php', 2524 2525 'PhabricatorSearchPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorSearchPreferencesSettingsPanel.php', 2525 2526 'PhabricatorSearchProjectsField' => 'applications/search/field/PhabricatorSearchProjectsField.php', ··· 6024 6025 'PhabricatorSearchManagementInitWorkflow' => 'PhabricatorSearchManagementWorkflow', 6025 6026 'PhabricatorSearchManagementWorkflow' => 'PhabricatorManagementWorkflow', 6026 6027 'PhabricatorSearchOrderController' => 'PhabricatorSearchBaseController', 6028 + 'PhabricatorSearchOrderField' => 'PhabricatorSearchField', 6027 6029 'PhabricatorSearchOwnersField' => 'PhabricatorSearchTokenizerField', 6028 6030 'PhabricatorSearchPreferencesSettingsPanel' => 'PhabricatorSettingsPanel', 6029 6031 'PhabricatorSearchProjectsField' => 'PhabricatorSearchTokenizerField',
+21 -31
src/applications/almanac/query/AlmanacServiceQuery.php
··· 60 60 } 61 61 62 62 protected function loadPage() { 63 - $table = new AlmanacService(); 64 - $conn_r = $table->establishConnection('r'); 65 - 66 - $data = queryfx_all( 67 - $conn_r, 68 - 'SELECT service.* FROM %T service %Q %Q %Q %Q', 69 - $table->getTableName(), 70 - $this->buildJoinClause($conn_r), 71 - $this->buildWhereClause($conn_r), 72 - $this->buildOrderClause($conn_r), 73 - $this->buildLimitClause($conn_r)); 74 - 75 - return $table->loadAllFromArray($data); 63 + return $this->loadStandardPage(new AlmanacService()); 76 64 } 77 65 78 - protected function buildJoinClause(AphrontDatabaseConnection $conn_r) { 79 - $joins = array(); 66 + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { 67 + $joins = parent::buildJoinClauseParts($conn); 80 68 81 69 if ($this->devicePHIDs !== null) { 82 70 $joins[] = qsprintf( 83 - $conn_r, 71 + $conn, 84 72 'JOIN %T binding ON service.phid = binding.servicePHID', 85 73 id(new AlmanacBinding())->getTableName()); 86 74 } 87 75 88 - return implode(' ', $joins); 76 + return $joins; 89 77 } 90 78 91 - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { 92 - $where = array(); 79 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 80 + $where = parent::buildWhereClauseParts($conn); 93 81 94 82 if ($this->ids !== null) { 95 83 $where[] = qsprintf( 96 - $conn_r, 84 + $conn, 97 85 'service.id IN (%Ld)', 98 86 $this->ids); 99 87 } 100 88 101 89 if ($this->phids !== null) { 102 90 $where[] = qsprintf( 103 - $conn_r, 91 + $conn, 104 92 'service.phid IN (%Ls)', 105 93 $this->phids); 106 94 } ··· 112 100 } 113 101 114 102 $where[] = qsprintf( 115 - $conn_r, 103 + $conn, 116 104 'service.nameIndex IN (%Ls)', 117 105 $hashes); 118 106 } 119 107 120 108 if ($this->serviceClasses !== null) { 121 109 $where[] = qsprintf( 122 - $conn_r, 110 + $conn, 123 111 'service.serviceClass IN (%Ls)', 124 112 $this->serviceClasses); 125 113 } 126 114 127 115 if ($this->devicePHIDs !== null) { 128 116 $where[] = qsprintf( 129 - $conn_r, 117 + $conn, 130 118 'binding.devicePHID IN (%Ls)', 131 119 $this->devicePHIDs); 132 120 } 133 121 134 122 if ($this->locked !== null) { 135 123 $where[] = qsprintf( 136 - $conn_r, 124 + $conn, 137 125 'service.isLocked = %d', 138 126 (int)$this->locked); 139 127 } 140 128 141 129 if ($this->namePrefix !== null) { 142 130 $where[] = qsprintf( 143 - $conn_r, 131 + $conn, 144 132 'service.name LIKE %>', 145 133 $this->namePrefix); 146 134 } 147 135 148 136 if ($this->nameSuffix !== null) { 149 137 $where[] = qsprintf( 150 - $conn_r, 138 + $conn, 151 139 'service.name LIKE %<', 152 140 $this->nameSuffix); 153 141 } 154 142 155 - $where[] = $this->buildPagingClause($conn_r); 156 - 157 - return $this->formatWhereClause($where); 143 + return $where; 158 144 } 159 145 160 146 protected function willFilterPage(array $services) { ··· 192 178 return parent::didFilterPage($services); 193 179 } 194 180 181 + public function getPrimaryTableAlias() { 182 + return 'service'; 183 + } 184 + 195 185 public function getOrderableColumns() { 196 186 return parent::getOrderableColumns() + array( 197 187 'name' => array( 198 - 'table' => 'service', 188 + 'table' => $this->getPrimaryTableAlias(), 199 189 'column' => 'name', 200 190 'type' => 'string', 201 191 'unique' => true,
+12 -22
src/applications/almanac/query/AlmanacServiceSearchEngine.php
··· 11 11 return 'PhabricatorAlmanacApplication'; 12 12 } 13 13 14 - public function buildSavedQueryFromRequest(AphrontRequest $request) { 15 - $saved = new PhabricatorSavedQuery(); 14 + public function newQuery() { 15 + return new AlmanacServiceQuery(); 16 + } 16 17 17 - $this->saveQueryOrder($saved, $request); 18 - 19 - return $saved; 18 + public function newResultObject() { 19 + // NOTE: We need to attach a service type in order to generate custom 20 + // field definitions. 21 + return AlmanacService::initializeNewService() 22 + ->attachServiceType(new AlmanacCustomServiceType()); 20 23 } 21 24 22 - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 23 - $query = id(new AlmanacServiceQuery()); 24 - 25 - $this->setQueryOrder($query, $saved); 25 + protected function buildQueryFromParameters(array $map) { 26 + $query = $this->newQuery(); 26 27 27 28 return $query; 28 29 } 29 30 30 - public function buildSearchForm( 31 - AphrontFormView $form, 32 - PhabricatorSavedQuery $saved) { 33 31 34 - $this->appendOrderFieldsToForm( 35 - $form, 36 - $saved, 37 - new AlmanacServiceQuery()); 32 + protected function buildCustomSearchFields() { 33 + return array(); 38 34 } 39 35 40 36 protected function getURI($path) { ··· 60 56 } 61 57 62 58 return parent::buildSavedQueryFromBuiltin($query_key); 63 - } 64 - 65 - protected function getRequiredHandlePHIDsForResultList( 66 - array $services, 67 - PhabricatorSavedQuery $query) { 68 - return array(); 69 59 } 70 60 71 61 protected function renderResultList(
+4
src/applications/files/query/PhabricatorFileQuery.php
··· 117 117 return $this; 118 118 } 119 119 120 + public function newResultObject() { 121 + return new PhabricatorFile(); 122 + } 123 + 120 124 protected function loadPage() { 121 125 $files = $this->loadStandardPage(new PhabricatorFile()); 122 126
+3 -3
src/applications/files/query/PhabricatorFileSearchEngine.php
··· 11 11 return 'PhabricatorFilesApplication'; 12 12 } 13 13 14 - public function newResultObject() { 15 - return new PhabricatorFile(); 14 + public function newQuery() { 15 + return new PhabricatorFileQuery(); 16 16 } 17 17 18 18 protected function buildCustomSearchFields() { ··· 46 46 } 47 47 48 48 public function buildQueryFromParameters(array $map) { 49 - $query = id(new PhabricatorFileQuery()); 49 + $query = $this->newQuery(); 50 50 51 51 if ($map['authorPHIDs']) { 52 52 $query->withAuthorPHIDs($map['authorPHIDs']);
+1 -1
src/applications/maniphest/query/ManiphestTaskQuery.php
··· 193 193 return $this; 194 194 } 195 195 196 - protected function newResultObject() { 196 + public function newResultObject() { 197 197 return new ManiphestTask(); 198 198 } 199 199
+1 -1
src/applications/paste/query/PhabricatorPasteQuery.php
··· 67 67 return $this; 68 68 } 69 69 70 - protected function newResultObject() { 70 + public function newResultObject() { 71 71 return new PhabricatorPaste(); 72 72 } 73 73
+4 -4
src/applications/paste/query/PhabricatorPasteSearchEngine.php
··· 11 11 return 'PhabricatorPasteApplication'; 12 12 } 13 13 14 - public function newResultObject() { 15 - return new PhabricatorPaste(); 14 + public function newQuery() { 15 + return id(new PhabricatorPasteQuery()) 16 + ->needContent(true); 16 17 } 17 18 18 19 protected function buildQueryFromParameters(array $map) { 19 - $query = id(new PhabricatorPasteQuery()) 20 - ->needContent(true); 20 + $query = $this->newQuery(); 21 21 22 22 if ($map['authorPHIDs']) { 23 23 $query->withAuthorPHIDs($map['authorPHIDs']);
+30 -43
src/applications/people/query/PhabricatorPeopleQuery.php
··· 113 113 return $this; 114 114 } 115 115 116 - protected function loadPage() { 117 - $table = new PhabricatorUser(); 118 - $conn_r = $table->establishConnection('r'); 116 + public function newResultObject() { 117 + return new PhabricatorUser(); 118 + } 119 119 120 - $data = queryfx_all( 121 - $conn_r, 122 - 'SELECT * FROM %T user %Q %Q %Q %Q %Q', 123 - $table->getTableName(), 124 - $this->buildJoinsClause($conn_r), 125 - $this->buildWhereClause($conn_r), 126 - $this->buildGroupClause($conn_r), 127 - $this->buildOrderClause($conn_r), 128 - $this->buildLimitClause($conn_r)); 120 + protected function loadPage() { 121 + $table = new PhabricatorUser(); 122 + $data = $this->loadStandardPageRows($table); 129 123 130 124 if ($this->needPrimaryEmail) { 131 125 $table->putInSet(new LiskDAOSet()); ··· 225 219 return $users; 226 220 } 227 221 228 - protected function buildGroupClause(AphrontDatabaseConnection $conn) { 222 + protected function shouldGroupQueryResultRows() { 229 223 if ($this->nameTokens) { 230 - return qsprintf( 231 - $conn, 232 - 'GROUP BY user.id'); 233 - } else { 234 - return $this->buildApplicationSearchGroupClause($conn); 224 + return true; 235 225 } 226 + 227 + return parent::shouldGroupQueryResultRows(); 236 228 } 237 229 238 - private function buildJoinsClause($conn_r) { 239 - $joins = array(); 230 + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { 231 + $joins = parent::buildJoinClauseParts($conn); 240 232 241 233 if ($this->emails) { 242 234 $email_table = new PhabricatorUserEmail(); 243 235 $joins[] = qsprintf( 244 - $conn_r, 236 + $conn, 245 237 'JOIN %T email ON email.userPHID = user.PHID', 246 238 $email_table->getTableName()); 247 239 } ··· 250 242 foreach ($this->nameTokens as $key => $token) { 251 243 $token_table = 'token_'.$key; 252 244 $joins[] = qsprintf( 253 - $conn_r, 245 + $conn, 254 246 'JOIN %T %T ON %T.userID = user.id AND %T.token LIKE %>', 255 247 PhabricatorUser::NAMETOKEN_TABLE, 256 248 $token_table, ··· 260 252 } 261 253 } 262 254 263 - $joins[] = $this->buildApplicationSearchJoinClause($conn_r); 264 - 265 - $joins = implode(' ', $joins); 266 255 return $joins; 267 256 } 268 257 269 - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { 270 - $where = array(); 258 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 259 + $where = parent::buildWhereClauseParts($conn); 271 260 272 261 if ($this->usernames !== null) { 273 262 $where[] = qsprintf( 274 - $conn_r, 263 + $conn, 275 264 'user.userName IN (%Ls)', 276 265 $this->usernames); 277 266 } 278 267 279 268 if ($this->emails !== null) { 280 269 $where[] = qsprintf( 281 - $conn_r, 270 + $conn, 282 271 'email.address IN (%Ls)', 283 272 $this->emails); 284 273 } 285 274 286 275 if ($this->realnames !== null) { 287 276 $where[] = qsprintf( 288 - $conn_r, 277 + $conn, 289 278 'user.realName IN (%Ls)', 290 279 $this->realnames); 291 280 } 292 281 293 282 if ($this->phids !== null) { 294 283 $where[] = qsprintf( 295 - $conn_r, 284 + $conn, 296 285 'user.phid IN (%Ls)', 297 286 $this->phids); 298 287 } 299 288 300 289 if ($this->ids !== null) { 301 290 $where[] = qsprintf( 302 - $conn_r, 291 + $conn, 303 292 'user.id IN (%Ld)', 304 293 $this->ids); 305 294 } 306 295 307 296 if ($this->dateCreatedAfter) { 308 297 $where[] = qsprintf( 309 - $conn_r, 298 + $conn, 310 299 'user.dateCreated >= %d', 311 300 $this->dateCreatedAfter); 312 301 } 313 302 314 303 if ($this->dateCreatedBefore) { 315 304 $where[] = qsprintf( 316 - $conn_r, 305 + $conn, 317 306 'user.dateCreated <= %d', 318 307 $this->dateCreatedBefore); 319 308 } 320 309 321 310 if ($this->isAdmin !== null) { 322 311 $where[] = qsprintf( 323 - $conn_r, 312 + $conn, 324 313 'user.isAdmin = %d', 325 314 (int)$this->isAdmin); 326 315 } 327 316 328 317 if ($this->isDisabled !== null) { 329 318 $where[] = qsprintf( 330 - $conn_r, 319 + $conn, 331 320 'user.isDisabled = %d', 332 321 (int)$this->isDisabled); 333 322 } 334 323 335 324 if ($this->isApproved !== null) { 336 325 $where[] = qsprintf( 337 - $conn_r, 326 + $conn, 338 327 'user.isApproved = %d', 339 328 (int)$this->isApproved); 340 329 } 341 330 342 331 if ($this->isSystemAgent !== null) { 343 332 $where[] = qsprintf( 344 - $conn_r, 333 + $conn, 345 334 'user.isSystemAgent = %d', 346 335 (int)$this->isSystemAgent); 347 336 } 348 337 349 338 if ($this->isMailingList !== null) { 350 339 $where[] = qsprintf( 351 - $conn_r, 340 + $conn, 352 341 'user.isMailingList = %d', 353 342 (int)$this->isMailingList); 354 343 } 355 344 356 345 if (strlen($this->nameLike)) { 357 346 $where[] = qsprintf( 358 - $conn_r, 347 + $conn, 359 348 'user.username LIKE %~ OR user.realname LIKE %~', 360 349 $this->nameLike, 361 350 $this->nameLike); 362 351 } 363 352 364 - $where[] = $this->buildPagingClause($conn_r); 365 - 366 - return $this->formatWhereClause($where); 353 + return $where; 367 354 } 368 355 369 356 protected function getPrimaryTableAlias() {
+5 -5
src/applications/people/query/PhabricatorPeopleSearchEngine.php
··· 11 11 return 'PhabricatorPeopleApplication'; 12 12 } 13 13 14 - public function newResultObject() { 15 - return new PhabricatorUser(); 14 + public function newQuery() { 15 + return id(new PhabricatorPeopleQuery()) 16 + ->needPrimaryEmail(true) 17 + ->needProfileImage(true); 16 18 } 17 19 18 20 protected function buildCustomSearchFields() { ··· 77 79 } 78 80 79 81 public function buildQueryFromParameters(array $map) { 80 - $query = id(new PhabricatorPeopleQuery()) 81 - ->needPrimaryEmail(true) 82 - ->needProfileImage(true); 82 + $query = $this->newQuery(); 83 83 84 84 $viewer = $this->requireViewer(); 85 85
+4
src/applications/pholio/query/PholioMockQuery.php
··· 53 53 return $this; 54 54 } 55 55 56 + public function newResultObject() { 57 + return new PholioMock(); 58 + } 59 + 56 60 protected function loadPage() { 57 61 $mocks = $this->loadStandardPage(new PholioMock()); 58 62
+7 -7
src/applications/pholio/query/PholioMockSearchEngine.php
··· 10 10 return 'PhabricatorPholioApplication'; 11 11 } 12 12 13 - public function newResultObject() { 14 - return new PholioMock(); 13 + public function newQuery() { 14 + return id(new PholioMockQuery()) 15 + ->needCoverFiles(true) 16 + ->needImages(true) 17 + ->needTokenCounts(true); 15 18 } 16 19 17 - public function buildCustomSearchFields() { 20 + protected function buildCustomSearchFields() { 18 21 return array( 19 22 id(new PhabricatorSearchUsersField()) 20 23 ->setKey('authorPHIDs') ··· 30 33 } 31 34 32 35 public function buildQueryFromParameters(array $map) { 33 - $query = id(new PholioMockQuery()) 34 - ->needCoverFiles(true) 35 - ->needImages(true) 36 - ->needTokenCounts(true); 36 + $query = $this->newQuery(); 37 37 38 38 if ($map['authorPHIDs']) { 39 39 $query->withAuthorPHIDs($map['authorPHIDs']);
+14 -1
src/applications/project/query/PhabricatorProjectQuery.php
··· 95 95 return $this; 96 96 } 97 97 98 + public function newResultObject() { 99 + return new PhabricatorProject(); 100 + } 101 + 98 102 protected function getDefaultOrderVector() { 99 103 return array('name'); 100 104 } 101 105 106 + public function getBuiltinOrders() { 107 + return array( 108 + 'name' => array( 109 + 'vector' => array('name'), 110 + 'name' => pht('Name'), 111 + ), 112 + ) + parent::getBuiltinOrders(); 113 + } 114 + 102 115 public function getOrderableColumns() { 103 - return array( 116 + return parent::getOrderableColumns() + array( 104 117 'name' => array( 105 118 'table' => $this->getPrimaryTableAlias(), 106 119 'column' => 'name',
+4 -4
src/applications/project/query/PhabricatorProjectSearchEngine.php
··· 11 11 return 'PhabricatorProjectApplication'; 12 12 } 13 13 14 - public function newResultObject() { 15 - return new PhabricatorProject(); 14 + public function newQuery() { 15 + return id(new PhabricatorProjectQuery()) 16 + ->needImages(true); 16 17 } 17 18 18 19 protected function buildCustomSearchFields() { ··· 41 42 42 43 43 44 public function buildQueryFromParameters(array $map) { 44 - $query = id(new PhabricatorProjectQuery()) 45 - ->needImages(true); 45 + $query = $this->newQuery(); 46 46 47 47 if (strlen($map['name'])) { 48 48 $tokens = PhabricatorTypeaheadDatasource::tokenizeString($map['name']);
+56 -31
src/applications/search/engine/PhabricatorApplicationSearchEngine.php
··· 27 27 const CONTEXT_PANEL = 'panel'; 28 28 29 29 public function newResultObject() { 30 + // We may be able to get this automatically if newQuery() is implemented. 31 + $query = $this->newQuery(); 32 + if ($query) { 33 + $object = $query->newResultObject(); 34 + if ($object) { 35 + return $object; 36 + } 37 + } 38 + 39 + return null; 40 + } 41 + 42 + public function newQuery() { 30 43 return null; 31 44 } 32 45 ··· 98 111 $fields = $this->buildSearchFields(); 99 112 $viewer = $this->requireViewer(); 100 113 101 - $parameters = array(); 114 + $map = array(); 102 115 foreach ($fields as $field) { 103 116 $field->setViewer($viewer); 104 117 $field->readValueFromSavedQuery($saved); 105 118 $value = $field->getValueForQuery($field->getValue()); 106 - $parameters[$field->getKey()] = $value; 119 + $map[$field->getKey()] = $value; 107 120 } 108 121 109 - $query = $this->buildQueryFromParameters($parameters); 122 + $query = $this->buildQueryFromParameters($map); 110 123 111 124 $object = $this->newResultObject(); 112 125 if (!$object) { ··· 114 127 } 115 128 116 129 if ($object instanceof PhabricatorSubscribableInterface) { 117 - if (!empty($parameters['subscriberPHIDs'])) { 130 + if (!empty($map['subscriberPHIDs'])) { 118 131 $query->withEdgeLogicPHIDs( 119 132 PhabricatorObjectHasSubscriberEdgeType::EDGECONST, 120 133 PhabricatorQueryConstraint::OPERATOR_OR, 121 - $parameters['subscriberPHIDs']); 134 + $map['subscriberPHIDs']); 122 135 } 123 136 } 124 137 125 138 if ($object instanceof PhabricatorProjectInterface) { 126 - if (!empty($parameters['projectPHIDs'])) { 139 + if (!empty($map['projectPHIDs'])) { 127 140 $query->withEdgeLogicConstraints( 128 141 PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, 129 - $parameters['projectPHIDs']); 142 + $map['projectPHIDs']); 130 143 } 131 144 } 132 145 133 146 if ($object instanceof PhabricatorSpacesInterface) { 134 - if (!empty($parameters['spacePHIDs'])) { 135 - $query->withSpacePHIDs($parameters['spacePHIDs']); 147 + if (!empty($map['spacePHIDs'])) { 148 + $query->withSpacePHIDs($map['spacePHIDs']); 136 149 } 137 150 } 138 151 139 152 if ($object instanceof PhabricatorCustomFieldInterface) { 140 153 $this->applyCustomFieldsToQuery($query, $saved); 141 154 } 155 + 156 + $this->setQueryOrder($query, $saved); 142 157 143 158 return $query; 144 159 } ··· 185 200 } 186 201 187 202 $object = $this->newResultObject(); 188 - if (!$object) { 189 - return $fields; 190 - } 191 - 192 - if ($object instanceof PhabricatorSubscribableInterface) { 193 - $fields[] = id(new PhabricatorSearchSubscribersField()) 194 - ->setLabel(pht('Subscribers')) 195 - ->setKey('subscriberPHIDs') 196 - ->setAliases(array('subscriber', 'subscribers')); 197 - } 203 + if ($object) { 204 + if ($object instanceof PhabricatorSubscribableInterface) { 205 + $fields[] = id(new PhabricatorSearchSubscribersField()) 206 + ->setLabel(pht('Subscribers')) 207 + ->setKey('subscriberPHIDs') 208 + ->setAliases(array('subscriber', 'subscribers')); 209 + } 198 210 199 - if ($object instanceof PhabricatorProjectInterface) { 200 - $fields[] = id(new PhabricatorSearchProjectsField()) 201 - ->setKey('projectPHIDs') 202 - ->setAliases(array('project', 'projects')) 203 - ->setLabel(pht('Projects')); 204 - } 211 + if ($object instanceof PhabricatorProjectInterface) { 212 + $fields[] = id(new PhabricatorSearchProjectsField()) 213 + ->setKey('projectPHIDs') 214 + ->setAliases(array('project', 'projects')) 215 + ->setLabel(pht('Projects')); 216 + } 205 217 206 - if ($object instanceof PhabricatorSpacesInterface) { 207 - if (PhabricatorSpacesNamespaceQuery::getSpacesExist()) { 208 - $fields[] = id(new PhabricatorSearchSpacesField()) 209 - ->setKey('spacePHIDs') 210 - ->setAliases(array('space', 'spaces')) 211 - ->setLabel(pht('Spaces')); 218 + if ($object instanceof PhabricatorSpacesInterface) { 219 + if (PhabricatorSpacesNamespaceQuery::getSpacesExist()) { 220 + $fields[] = id(new PhabricatorSearchSpacesField()) 221 + ->setKey('spacePHIDs') 222 + ->setAliases(array('space', 'spaces')) 223 + ->setLabel(pht('Spaces')); 224 + } 212 225 } 213 226 } 214 227 215 228 foreach ($this->buildCustomFieldSearchFields() as $custom_field) { 216 229 $fields[] = $custom_field; 230 + } 231 + 232 + $query = $this->newQuery(); 233 + if ($query) { 234 + $orders = $query->getBuiltinOrders(); 235 + $orders = ipull($orders, 'name'); 236 + 237 + $fields[] = id(new PhabricatorSearchOrderField()) 238 + ->setLabel(pht('Order')) 239 + ->setKey('order') 240 + ->setOptions($orders); 217 241 } 218 242 219 243 $field_map = array(); ··· 890 914 891 915 $order = $saved->getParameter('order'); 892 916 $builtin = $query->getBuiltinOrders(); 917 + 893 918 if (strlen($order) && isset($builtin[$order])) { 894 919 $query->setOrder($order); 895 920 } else {
+30
src/applications/search/field/PhabricatorSearchOrderField.php
··· 1 + <?php 2 + 3 + final class PhabricatorSearchOrderField 4 + extends PhabricatorSearchField { 5 + 6 + private $options; 7 + 8 + public function setOptions(array $options) { 9 + $this->options = $options; 10 + return $this; 11 + } 12 + 13 + public function getOptions() { 14 + return $this->options; 15 + } 16 + 17 + protected function getDefaultValue() { 18 + return null; 19 + } 20 + 21 + protected function getValueFromRequest(AphrontRequest $request, $key) { 22 + return $request->getStr($key); 23 + } 24 + 25 + protected function newControl() { 26 + return id(new AphrontFormSelectControl()) 27 + ->setOptions($this->getOptions()); 28 + } 29 + 30 + }
+31 -1
src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
··· 200 200 return null; 201 201 } 202 202 203 - protected function newResultObject() { 203 + public function newResultObject() { 204 204 return null; 205 205 } 206 206 ··· 844 844 'column' => 'indexValue', 845 845 'type' => $index->getIndexValueType(), 846 846 'null' => 'tail', 847 + 'customfield' => true, 848 + 'customfield.index.table' => $index->getTableName(), 849 + 'customfield.index.key' => $digest, 847 850 ); 848 851 } 849 852 } ··· 1229 1232 } 1230 1233 } 1231 1234 1235 + // TODO: Get rid of this. 1232 1236 foreach ($this->applicationSearchOrders as $key => $order) { 1233 1237 $table = $order['table']; 1234 1238 $index = $order['index']; ··· 1245 1249 $phid_column, 1246 1250 $alias, 1247 1251 $index); 1252 + } 1253 + 1254 + $phid_column = $this->getApplicationSearchObjectPHIDColumn(); 1255 + $orderable = $this->getOrderableColumns(); 1256 + 1257 + $vector = $this->getOrderVector(); 1258 + foreach ($vector as $order) { 1259 + $spec = $orderable[$order->getOrderKey()]; 1260 + if (empty($spec['customfield'])) { 1261 + continue; 1262 + } 1263 + 1264 + $table = $spec['customfield.index.table']; 1265 + $alias = $spec['table']; 1266 + $key = $spec['customfield.index.key']; 1267 + 1268 + $joins[] = qsprintf( 1269 + $conn_r, 1270 + 'LEFT JOIN %T %T ON %T.objectPHID = %Q 1271 + AND %T.indexKey = %s', 1272 + $table, 1273 + $alias, 1274 + $alias, 1275 + $phid_column, 1276 + $alias, 1277 + $key); 1248 1278 } 1249 1279 1250 1280 return implode(' ', $joins);