diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 9ebf482748..1af2166ce1 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2520,6 +2520,7 @@ phutil_register_library_map(array( 'PhabricatorSearchManagementInitWorkflow' => 'applications/search/management/PhabricatorSearchManagementInitWorkflow.php', 'PhabricatorSearchManagementWorkflow' => 'applications/search/management/PhabricatorSearchManagementWorkflow.php', 'PhabricatorSearchOrderController' => 'applications/search/controller/PhabricatorSearchOrderController.php', + 'PhabricatorSearchOrderField' => 'applications/search/field/PhabricatorSearchOrderField.php', 'PhabricatorSearchOwnersField' => 'applications/search/field/PhabricatorSearchOwnersField.php', 'PhabricatorSearchPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorSearchPreferencesSettingsPanel.php', 'PhabricatorSearchProjectsField' => 'applications/search/field/PhabricatorSearchProjectsField.php', @@ -6024,6 +6025,7 @@ phutil_register_library_map(array( 'PhabricatorSearchManagementInitWorkflow' => 'PhabricatorSearchManagementWorkflow', 'PhabricatorSearchManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorSearchOrderController' => 'PhabricatorSearchBaseController', + 'PhabricatorSearchOrderField' => 'PhabricatorSearchField', 'PhabricatorSearchOwnersField' => 'PhabricatorSearchTokenizerField', 'PhabricatorSearchPreferencesSettingsPanel' => 'PhabricatorSettingsPanel', 'PhabricatorSearchProjectsField' => 'PhabricatorSearchTokenizerField', diff --git a/src/applications/almanac/query/AlmanacServiceQuery.php b/src/applications/almanac/query/AlmanacServiceQuery.php index 85217d1247..34879d5ca5 100644 --- a/src/applications/almanac/query/AlmanacServiceQuery.php +++ b/src/applications/almanac/query/AlmanacServiceQuery.php @@ -60,47 +60,35 @@ final class AlmanacServiceQuery } protected function loadPage() { - $table = new AlmanacService(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT service.* FROM %T service %Q %Q %Q %Q', - $table->getTableName(), - $this->buildJoinClause($conn_r), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($data); + return $this->loadStandardPage(new AlmanacService()); } - protected function buildJoinClause(AphrontDatabaseConnection $conn_r) { - $joins = array(); + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { + $joins = parent::buildJoinClauseParts($conn); if ($this->devicePHIDs !== null) { $joins[] = qsprintf( - $conn_r, + $conn, 'JOIN %T binding ON service.phid = binding.servicePHID', id(new AlmanacBinding())->getTableName()); } - return implode(' ', $joins); + return $joins; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - $where = array(); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'service.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'service.phid IN (%Ls)', $this->phids); } @@ -112,49 +100,47 @@ final class AlmanacServiceQuery } $where[] = qsprintf( - $conn_r, + $conn, 'service.nameIndex IN (%Ls)', $hashes); } if ($this->serviceClasses !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'service.serviceClass IN (%Ls)', $this->serviceClasses); } if ($this->devicePHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'binding.devicePHID IN (%Ls)', $this->devicePHIDs); } if ($this->locked !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'service.isLocked = %d', (int)$this->locked); } if ($this->namePrefix !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'service.name LIKE %>', $this->namePrefix); } if ($this->nameSuffix !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'service.name LIKE %<', $this->nameSuffix); } - $where[] = $this->buildPagingClause($conn_r); - - return $this->formatWhereClause($where); + return $where; } protected function willFilterPage(array $services) { @@ -192,10 +178,14 @@ final class AlmanacServiceQuery return parent::didFilterPage($services); } + public function getPrimaryTableAlias() { + return 'service'; + } + public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'name' => array( - 'table' => 'service', + 'table' => $this->getPrimaryTableAlias(), 'column' => 'name', 'type' => 'string', 'unique' => true, diff --git a/src/applications/almanac/query/AlmanacServiceSearchEngine.php b/src/applications/almanac/query/AlmanacServiceSearchEngine.php index 8610f86572..27672e8bf9 100644 --- a/src/applications/almanac/query/AlmanacServiceSearchEngine.php +++ b/src/applications/almanac/query/AlmanacServiceSearchEngine.php @@ -11,30 +11,26 @@ final class AlmanacServiceSearchEngine return 'PhabricatorAlmanacApplication'; } - public function buildSavedQueryFromRequest(AphrontRequest $request) { - $saved = new PhabricatorSavedQuery(); - - $this->saveQueryOrder($saved, $request); - - return $saved; + public function newQuery() { + return new AlmanacServiceQuery(); } - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { - $query = id(new AlmanacServiceQuery()); + public function newResultObject() { + // NOTE: We need to attach a service type in order to generate custom + // field definitions. + return AlmanacService::initializeNewService() + ->attachServiceType(new AlmanacCustomServiceType()); + } - $this->setQueryOrder($query, $saved); + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); return $query; } - public function buildSearchForm( - AphrontFormView $form, - PhabricatorSavedQuery $saved) { - $this->appendOrderFieldsToForm( - $form, - $saved, - new AlmanacServiceQuery()); + protected function buildCustomSearchFields() { + return array(); } protected function getURI($path) { @@ -62,12 +58,6 @@ final class AlmanacServiceSearchEngine return parent::buildSavedQueryFromBuiltin($query_key); } - protected function getRequiredHandlePHIDsForResultList( - array $services, - PhabricatorSavedQuery $query) { - return array(); - } - protected function renderResultList( array $services, PhabricatorSavedQuery $query, diff --git a/src/applications/files/query/PhabricatorFileQuery.php b/src/applications/files/query/PhabricatorFileQuery.php index 95a7f4fa1b..ad13908d7d 100644 --- a/src/applications/files/query/PhabricatorFileQuery.php +++ b/src/applications/files/query/PhabricatorFileQuery.php @@ -117,6 +117,10 @@ final class PhabricatorFileQuery return $this; } + public function newResultObject() { + return new PhabricatorFile(); + } + protected function loadPage() { $files = $this->loadStandardPage(new PhabricatorFile()); diff --git a/src/applications/files/query/PhabricatorFileSearchEngine.php b/src/applications/files/query/PhabricatorFileSearchEngine.php index e3db58b311..69eeb0e98a 100644 --- a/src/applications/files/query/PhabricatorFileSearchEngine.php +++ b/src/applications/files/query/PhabricatorFileSearchEngine.php @@ -11,8 +11,8 @@ final class PhabricatorFileSearchEngine return 'PhabricatorFilesApplication'; } - public function newResultObject() { - return new PhabricatorFile(); + public function newQuery() { + return new PhabricatorFileQuery(); } protected function buildCustomSearchFields() { @@ -46,7 +46,7 @@ final class PhabricatorFileSearchEngine } public function buildQueryFromParameters(array $map) { - $query = id(new PhabricatorFileQuery()); + $query = $this->newQuery(); if ($map['authorPHIDs']) { $query->withAuthorPHIDs($map['authorPHIDs']); diff --git a/src/applications/maniphest/query/ManiphestTaskQuery.php b/src/applications/maniphest/query/ManiphestTaskQuery.php index 09472be2ab..636fea218d 100644 --- a/src/applications/maniphest/query/ManiphestTaskQuery.php +++ b/src/applications/maniphest/query/ManiphestTaskQuery.php @@ -193,7 +193,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery { return $this; } - protected function newResultObject() { + public function newResultObject() { return new ManiphestTask(); } diff --git a/src/applications/paste/query/PhabricatorPasteQuery.php b/src/applications/paste/query/PhabricatorPasteQuery.php index 75bc4d7084..5c31b65931 100644 --- a/src/applications/paste/query/PhabricatorPasteQuery.php +++ b/src/applications/paste/query/PhabricatorPasteQuery.php @@ -67,7 +67,7 @@ final class PhabricatorPasteQuery return $this; } - protected function newResultObject() { + public function newResultObject() { return new PhabricatorPaste(); } diff --git a/src/applications/paste/query/PhabricatorPasteSearchEngine.php b/src/applications/paste/query/PhabricatorPasteSearchEngine.php index a443472b55..b6dcfce104 100644 --- a/src/applications/paste/query/PhabricatorPasteSearchEngine.php +++ b/src/applications/paste/query/PhabricatorPasteSearchEngine.php @@ -11,13 +11,13 @@ final class PhabricatorPasteSearchEngine return 'PhabricatorPasteApplication'; } - public function newResultObject() { - return new PhabricatorPaste(); + public function newQuery() { + return id(new PhabricatorPasteQuery()) + ->needContent(true); } protected function buildQueryFromParameters(array $map) { - $query = id(new PhabricatorPasteQuery()) - ->needContent(true); + $query = $this->newQuery(); if ($map['authorPHIDs']) { $query->withAuthorPHIDs($map['authorPHIDs']); diff --git a/src/applications/people/query/PhabricatorPeopleQuery.php b/src/applications/people/query/PhabricatorPeopleQuery.php index 09dfd51f6d..b8a0851252 100644 --- a/src/applications/people/query/PhabricatorPeopleQuery.php +++ b/src/applications/people/query/PhabricatorPeopleQuery.php @@ -113,19 +113,13 @@ final class PhabricatorPeopleQuery return $this; } - protected function loadPage() { - $table = new PhabricatorUser(); - $conn_r = $table->establishConnection('r'); + public function newResultObject() { + return new PhabricatorUser(); + } - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T user %Q %Q %Q %Q %Q', - $table->getTableName(), - $this->buildJoinsClause($conn_r), - $this->buildWhereClause($conn_r), - $this->buildGroupClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); + protected function loadPage() { + $table = new PhabricatorUser(); + $data = $this->loadStandardPageRows($table); if ($this->needPrimaryEmail) { $table->putInSet(new LiskDAOSet()); @@ -225,23 +219,21 @@ final class PhabricatorPeopleQuery return $users; } - protected function buildGroupClause(AphrontDatabaseConnection $conn) { + protected function shouldGroupQueryResultRows() { if ($this->nameTokens) { - return qsprintf( - $conn, - 'GROUP BY user.id'); - } else { - return $this->buildApplicationSearchGroupClause($conn); + return true; } + + return parent::shouldGroupQueryResultRows(); } - private function buildJoinsClause($conn_r) { - $joins = array(); + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { + $joins = parent::buildJoinClauseParts($conn); if ($this->emails) { $email_table = new PhabricatorUserEmail(); $joins[] = qsprintf( - $conn_r, + $conn, 'JOIN %T email ON email.userPHID = user.PHID', $email_table->getTableName()); } @@ -250,7 +242,7 @@ final class PhabricatorPeopleQuery foreach ($this->nameTokens as $key => $token) { $token_table = 'token_'.$key; $joins[] = qsprintf( - $conn_r, + $conn, 'JOIN %T %T ON %T.userID = user.id AND %T.token LIKE %>', PhabricatorUser::NAMETOKEN_TABLE, $token_table, @@ -260,110 +252,105 @@ final class PhabricatorPeopleQuery } } - $joins[] = $this->buildApplicationSearchJoinClause($conn_r); - - $joins = implode(' ', $joins); return $joins; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - $where = array(); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); if ($this->usernames !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'user.userName IN (%Ls)', $this->usernames); } if ($this->emails !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'email.address IN (%Ls)', $this->emails); } if ($this->realnames !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'user.realName IN (%Ls)', $this->realnames); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'user.phid IN (%Ls)', $this->phids); } if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'user.id IN (%Ld)', $this->ids); } if ($this->dateCreatedAfter) { $where[] = qsprintf( - $conn_r, + $conn, 'user.dateCreated >= %d', $this->dateCreatedAfter); } if ($this->dateCreatedBefore) { $where[] = qsprintf( - $conn_r, + $conn, 'user.dateCreated <= %d', $this->dateCreatedBefore); } if ($this->isAdmin !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'user.isAdmin = %d', (int)$this->isAdmin); } if ($this->isDisabled !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'user.isDisabled = %d', (int)$this->isDisabled); } if ($this->isApproved !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'user.isApproved = %d', (int)$this->isApproved); } if ($this->isSystemAgent !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'user.isSystemAgent = %d', (int)$this->isSystemAgent); } if ($this->isMailingList !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'user.isMailingList = %d', (int)$this->isMailingList); } if (strlen($this->nameLike)) { $where[] = qsprintf( - $conn_r, + $conn, 'user.username LIKE %~ OR user.realname LIKE %~', $this->nameLike, $this->nameLike); } - $where[] = $this->buildPagingClause($conn_r); - - return $this->formatWhereClause($where); + return $where; } protected function getPrimaryTableAlias() { diff --git a/src/applications/people/query/PhabricatorPeopleSearchEngine.php b/src/applications/people/query/PhabricatorPeopleSearchEngine.php index f141b256c6..64b9f9db62 100644 --- a/src/applications/people/query/PhabricatorPeopleSearchEngine.php +++ b/src/applications/people/query/PhabricatorPeopleSearchEngine.php @@ -11,8 +11,10 @@ final class PhabricatorPeopleSearchEngine return 'PhabricatorPeopleApplication'; } - public function newResultObject() { - return new PhabricatorUser(); + public function newQuery() { + return id(new PhabricatorPeopleQuery()) + ->needPrimaryEmail(true) + ->needProfileImage(true); } protected function buildCustomSearchFields() { @@ -77,9 +79,7 @@ final class PhabricatorPeopleSearchEngine } public function buildQueryFromParameters(array $map) { - $query = id(new PhabricatorPeopleQuery()) - ->needPrimaryEmail(true) - ->needProfileImage(true); + $query = $this->newQuery(); $viewer = $this->requireViewer(); diff --git a/src/applications/pholio/query/PholioMockQuery.php b/src/applications/pholio/query/PholioMockQuery.php index 3f0ca2c66b..5f1711def6 100644 --- a/src/applications/pholio/query/PholioMockQuery.php +++ b/src/applications/pholio/query/PholioMockQuery.php @@ -53,6 +53,10 @@ final class PholioMockQuery return $this; } + public function newResultObject() { + return new PholioMock(); + } + protected function loadPage() { $mocks = $this->loadStandardPage(new PholioMock()); diff --git a/src/applications/pholio/query/PholioMockSearchEngine.php b/src/applications/pholio/query/PholioMockSearchEngine.php index e410d6ce39..8b0f290c25 100644 --- a/src/applications/pholio/query/PholioMockSearchEngine.php +++ b/src/applications/pholio/query/PholioMockSearchEngine.php @@ -10,11 +10,14 @@ final class PholioMockSearchEngine extends PhabricatorApplicationSearchEngine { return 'PhabricatorPholioApplication'; } - public function newResultObject() { - return new PholioMock(); + public function newQuery() { + return id(new PholioMockQuery()) + ->needCoverFiles(true) + ->needImages(true) + ->needTokenCounts(true); } - public function buildCustomSearchFields() { + protected function buildCustomSearchFields() { return array( id(new PhabricatorSearchUsersField()) ->setKey('authorPHIDs') @@ -30,10 +33,7 @@ final class PholioMockSearchEngine extends PhabricatorApplicationSearchEngine { } public function buildQueryFromParameters(array $map) { - $query = id(new PholioMockQuery()) - ->needCoverFiles(true) - ->needImages(true) - ->needTokenCounts(true); + $query = $this->newQuery(); if ($map['authorPHIDs']) { $query->withAuthorPHIDs($map['authorPHIDs']); diff --git a/src/applications/project/query/PhabricatorProjectQuery.php b/src/applications/project/query/PhabricatorProjectQuery.php index 711df75c06..bbb0857315 100644 --- a/src/applications/project/query/PhabricatorProjectQuery.php +++ b/src/applications/project/query/PhabricatorProjectQuery.php @@ -95,12 +95,25 @@ final class PhabricatorProjectQuery return $this; } + public function newResultObject() { + return new PhabricatorProject(); + } + protected function getDefaultOrderVector() { return array('name'); } - public function getOrderableColumns() { + public function getBuiltinOrders() { return array( + 'name' => array( + 'vector' => array('name'), + 'name' => pht('Name'), + ), + ) + parent::getBuiltinOrders(); + } + + public function getOrderableColumns() { + return parent::getOrderableColumns() + array( 'name' => array( 'table' => $this->getPrimaryTableAlias(), 'column' => 'name', diff --git a/src/applications/project/query/PhabricatorProjectSearchEngine.php b/src/applications/project/query/PhabricatorProjectSearchEngine.php index 50e7d17340..4a384b492a 100644 --- a/src/applications/project/query/PhabricatorProjectSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectSearchEngine.php @@ -11,8 +11,9 @@ final class PhabricatorProjectSearchEngine return 'PhabricatorProjectApplication'; } - public function newResultObject() { - return new PhabricatorProject(); + public function newQuery() { + return id(new PhabricatorProjectQuery()) + ->needImages(true); } protected function buildCustomSearchFields() { @@ -41,8 +42,7 @@ final class PhabricatorProjectSearchEngine public function buildQueryFromParameters(array $map) { - $query = id(new PhabricatorProjectQuery()) - ->needImages(true); + $query = $this->newQuery(); if (strlen($map['name'])) { $tokens = PhabricatorTypeaheadDatasource::tokenizeString($map['name']); diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php index 89e2d05c46..11e562b43f 100644 --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -27,6 +27,19 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { const CONTEXT_PANEL = 'panel'; public function newResultObject() { + // We may be able to get this automatically if newQuery() is implemented. + $query = $this->newQuery(); + if ($query) { + $object = $query->newResultObject(); + if ($object) { + return $object; + } + } + + return null; + } + + public function newQuery() { return null; } @@ -98,15 +111,15 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { $fields = $this->buildSearchFields(); $viewer = $this->requireViewer(); - $parameters = array(); + $map = array(); foreach ($fields as $field) { $field->setViewer($viewer); $field->readValueFromSavedQuery($saved); $value = $field->getValueForQuery($field->getValue()); - $parameters[$field->getKey()] = $value; + $map[$field->getKey()] = $value; } - $query = $this->buildQueryFromParameters($parameters); + $query = $this->buildQueryFromParameters($map); $object = $this->newResultObject(); if (!$object) { @@ -114,25 +127,25 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { } if ($object instanceof PhabricatorSubscribableInterface) { - if (!empty($parameters['subscriberPHIDs'])) { + if (!empty($map['subscriberPHIDs'])) { $query->withEdgeLogicPHIDs( PhabricatorObjectHasSubscriberEdgeType::EDGECONST, PhabricatorQueryConstraint::OPERATOR_OR, - $parameters['subscriberPHIDs']); + $map['subscriberPHIDs']); } } if ($object instanceof PhabricatorProjectInterface) { - if (!empty($parameters['projectPHIDs'])) { + if (!empty($map['projectPHIDs'])) { $query->withEdgeLogicConstraints( PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, - $parameters['projectPHIDs']); + $map['projectPHIDs']); } } if ($object instanceof PhabricatorSpacesInterface) { - if (!empty($parameters['spacePHIDs'])) { - $query->withSpacePHIDs($parameters['spacePHIDs']); + if (!empty($map['spacePHIDs'])) { + $query->withSpacePHIDs($map['spacePHIDs']); } } @@ -140,6 +153,8 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { $this->applyCustomFieldsToQuery($query, $saved); } + $this->setQueryOrder($query, $saved); + return $query; } @@ -185,30 +200,28 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { } $object = $this->newResultObject(); - if (!$object) { - return $fields; - } + if ($object) { + if ($object instanceof PhabricatorSubscribableInterface) { + $fields[] = id(new PhabricatorSearchSubscribersField()) + ->setLabel(pht('Subscribers')) + ->setKey('subscriberPHIDs') + ->setAliases(array('subscriber', 'subscribers')); + } - if ($object instanceof PhabricatorSubscribableInterface) { - $fields[] = id(new PhabricatorSearchSubscribersField()) - ->setLabel(pht('Subscribers')) - ->setKey('subscriberPHIDs') - ->setAliases(array('subscriber', 'subscribers')); - } + if ($object instanceof PhabricatorProjectInterface) { + $fields[] = id(new PhabricatorSearchProjectsField()) + ->setKey('projectPHIDs') + ->setAliases(array('project', 'projects')) + ->setLabel(pht('Projects')); + } - if ($object instanceof PhabricatorProjectInterface) { - $fields[] = id(new PhabricatorSearchProjectsField()) - ->setKey('projectPHIDs') - ->setAliases(array('project', 'projects')) - ->setLabel(pht('Projects')); - } - - if ($object instanceof PhabricatorSpacesInterface) { - if (PhabricatorSpacesNamespaceQuery::getSpacesExist()) { - $fields[] = id(new PhabricatorSearchSpacesField()) - ->setKey('spacePHIDs') - ->setAliases(array('space', 'spaces')) - ->setLabel(pht('Spaces')); + if ($object instanceof PhabricatorSpacesInterface) { + if (PhabricatorSpacesNamespaceQuery::getSpacesExist()) { + $fields[] = id(new PhabricatorSearchSpacesField()) + ->setKey('spacePHIDs') + ->setAliases(array('space', 'spaces')) + ->setLabel(pht('Spaces')); + } } } @@ -216,6 +229,17 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { $fields[] = $custom_field; } + $query = $this->newQuery(); + if ($query) { + $orders = $query->getBuiltinOrders(); + $orders = ipull($orders, 'name'); + + $fields[] = id(new PhabricatorSearchOrderField()) + ->setLabel(pht('Order')) + ->setKey('order') + ->setOptions($orders); + } + $field_map = array(); foreach ($fields as $field) { $key = $field->getKey(); @@ -890,6 +914,7 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { $order = $saved->getParameter('order'); $builtin = $query->getBuiltinOrders(); + if (strlen($order) && isset($builtin[$order])) { $query->setOrder($order); } else { diff --git a/src/applications/search/field/PhabricatorSearchOrderField.php b/src/applications/search/field/PhabricatorSearchOrderField.php new file mode 100644 index 0000000000..98e3b2beb3 --- /dev/null +++ b/src/applications/search/field/PhabricatorSearchOrderField.php @@ -0,0 +1,30 @@ +options = $options; + return $this; + } + + public function getOptions() { + return $this->options; + } + + protected function getDefaultValue() { + return null; + } + + protected function getValueFromRequest(AphrontRequest $request, $key) { + return $request->getStr($key); + } + + protected function newControl() { + return id(new AphrontFormSelectControl()) + ->setOptions($this->getOptions()); + } + +} diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php index 18686b84ad..aace1c728c 100644 --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -200,7 +200,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery return null; } - protected function newResultObject() { + public function newResultObject() { return null; } @@ -844,6 +844,9 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery 'column' => 'indexValue', 'type' => $index->getIndexValueType(), 'null' => 'tail', + 'customfield' => true, + 'customfield.index.table' => $index->getTableName(), + 'customfield.index.key' => $digest, ); } } @@ -1229,6 +1232,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery } } + // TODO: Get rid of this. foreach ($this->applicationSearchOrders as $key => $order) { $table = $order['table']; $index = $order['index']; @@ -1247,6 +1251,32 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery $index); } + $phid_column = $this->getApplicationSearchObjectPHIDColumn(); + $orderable = $this->getOrderableColumns(); + + $vector = $this->getOrderVector(); + foreach ($vector as $order) { + $spec = $orderable[$order->getOrderKey()]; + if (empty($spec['customfield'])) { + continue; + } + + $table = $spec['customfield.index.table']; + $alias = $spec['table']; + $key = $spec['customfield.index.key']; + + $joins[] = qsprintf( + $conn_r, + 'LEFT JOIN %T %T ON %T.objectPHID = %Q + AND %T.indexKey = %s', + $table, + $alias, + $alias, + $phid_column, + $alias, + $key); + } + return implode(' ', $joins); }