diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2500,6 +2500,7 @@ 'PhabricatorSearchApplicationStorageEnginePanel' => 'applications/search/applicationpanel/PhabricatorSearchApplicationStorageEnginePanel.php', 'PhabricatorSearchAttachController' => 'applications/search/controller/PhabricatorSearchAttachController.php', 'PhabricatorSearchBaseController' => 'applications/search/controller/PhabricatorSearchBaseController.php', + 'PhabricatorSearchCheckboxesField' => 'applications/search/field/PhabricatorSearchCheckboxesField.php', 'PhabricatorSearchConfigOptions' => 'applications/search/config/PhabricatorSearchConfigOptions.php', 'PhabricatorSearchController' => 'applications/search/controller/PhabricatorSearchController.php', 'PhabricatorSearchDAO' => 'applications/search/storage/PhabricatorSearchDAO.php', @@ -2523,6 +2524,7 @@ 'PhabricatorSearchManagementWorkflow' => 'applications/search/management/PhabricatorSearchManagementWorkflow.php', 'PhabricatorSearchOrderController' => 'applications/search/controller/PhabricatorSearchOrderController.php', 'PhabricatorSearchPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorSearchPreferencesSettingsPanel.php', + 'PhabricatorSearchProjectsField' => 'applications/search/field/PhabricatorSearchProjectsField.php', 'PhabricatorSearchRelationship' => 'applications/search/constants/PhabricatorSearchRelationship.php', 'PhabricatorSearchResultView' => 'applications/search/view/PhabricatorSearchResultView.php', 'PhabricatorSearchSelectController' => 'applications/search/controller/PhabricatorSearchSelectController.php', @@ -6002,6 +6004,7 @@ 'PhabricatorSearchApplicationStorageEnginePanel' => 'PhabricatorApplicationConfigurationPanel', 'PhabricatorSearchAttachController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchBaseController' => 'PhabricatorController', + 'PhabricatorSearchCheckboxesField' => 'PhabricatorSearchField', 'PhabricatorSearchConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorSearchController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchDAO' => 'PhabricatorLiskDAO', @@ -6023,6 +6026,7 @@ 'PhabricatorSearchManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorSearchOrderController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchPreferencesSettingsPanel' => 'PhabricatorSettingsPanel', + 'PhabricatorSearchProjectsField' => 'PhabricatorSearchTokenizerField', 'PhabricatorSearchResultView' => 'AphrontView', 'PhabricatorSearchSelectController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchSpacesField' => 'PhabricatorSearchTokenizerField', diff --git a/src/applications/paste/query/PhabricatorPasteQuery.php b/src/applications/paste/query/PhabricatorPasteQuery.php --- a/src/applications/paste/query/PhabricatorPasteQuery.php +++ b/src/applications/paste/query/PhabricatorPasteQuery.php @@ -72,20 +72,7 @@ } protected function loadPage() { - $table = new PhabricatorPaste(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT paste.* FROM %T paste %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - $pastes = $table->loadAllFromArray($data); - - return $pastes; + return $this->loadStandardPage(new PhabricatorPaste()); } protected function didFilterPage(array $pastes) { @@ -103,49 +90,49 @@ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); - if ($this->ids) { + if ($this->ids !== null) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } - if ($this->phids) { + if ($this->phids !== null) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } - if ($this->authorPHIDs) { + if ($this->authorPHIDs !== null) { $where[] = qsprintf( $conn, 'authorPHID IN (%Ls)', $this->authorPHIDs); } - if ($this->parentPHIDs) { + if ($this->parentPHIDs !== null) { $where[] = qsprintf( $conn, 'parentPHID IN (%Ls)', $this->parentPHIDs); } - if ($this->languages) { + if ($this->languages !== null) { $where[] = qsprintf( $conn, 'language IN (%Ls)', $this->languages); } - if ($this->dateCreatedAfter) { + if ($this->dateCreatedAfter !== null) { $where[] = qsprintf( $conn, 'dateCreated >= %d', $this->dateCreatedAfter); } - if ($this->dateCreatedBefore) { + if ($this->dateCreatedBefore !== null) { $where[] = qsprintf( $conn, 'dateCreated <= %d', diff --git a/src/applications/pholio/query/PholioMockQuery.php b/src/applications/pholio/query/PholioMockQuery.php --- a/src/applications/pholio/query/PholioMockQuery.php +++ b/src/applications/pholio/query/PholioMockQuery.php @@ -54,22 +54,7 @@ } protected function loadPage() { - $table = new PholioMock(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - '%Q FROM %T mock %Q %Q %Q %Q %Q %Q', - $this->buildSelectClause($conn_r), - $table->getTableName(), - $this->buildJoinClause($conn_r), - $this->buildWhereClause($conn_r), - $this->buildGroupClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildHavingClause($conn_r), - $this->buildLimitClause($conn_r)); - - $mocks = $table->loadAllFromArray($data); + $mocks = $this->loadStandardPage(new PholioMock()); if ($mocks && $this->needImages) { self::loadImages($this->getViewer(), $mocks, $this->needInlineComments); @@ -86,40 +71,38 @@ return $mocks; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - $where = array(); - - $where[] = $this->buildWhereClauseParts($conn_r); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'mock.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'mock.phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'mock.authorPHID in (%Ls)', $this->authorPHIDs); } if ($this->statuses !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'mock.status IN (%Ls)', $this->statuses); } - return $this->formatWhereClause($where); + return $where; } public static function loadImages( diff --git a/src/applications/pholio/query/PholioMockSearchEngine.php b/src/applications/pholio/query/PholioMockSearchEngine.php --- a/src/applications/pholio/query/PholioMockSearchEngine.php +++ b/src/applications/pholio/query/PholioMockSearchEngine.php @@ -10,86 +10,42 @@ return 'PhabricatorPholioApplication'; } - public function buildSavedQueryFromRequest(AphrontRequest $request) { - $saved = new PhabricatorSavedQuery(); - - $saved->setParameter( - 'authorPHIDs', - $this->readUsersFromRequest($request, 'authors')); - - $saved->setParameter( - 'projects', - $this->readProjectsFromRequest($request, 'projects')); - - $saved->setParameter( - 'statuses', - $request->getStrList('status')); + public function newResultObject() { + return new PholioMock(); + } - return $saved; + public function buildCustomSearchFields() { + return array( + id(new PhabricatorSearchUsersField()) + ->setKey('authorPHIDs') + ->setAliases(array('authors')) + ->setLabel(pht('Authors')), + id(new PhabricatorSearchCheckboxesField()) + ->setKey('statuses') + ->setLabel(pht('Status')) + ->setOptions( + id(new PholioMock()) + ->getStatuses()), + ); } - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { + public function buildQueryFromParameters(array $map) { $query = id(new PholioMockQuery()) ->needCoverFiles(true) ->needImages(true) ->needTokenCounts(true); - $datasource = id(new PhabricatorPeopleUserFunctionDatasource()) - ->setViewer($this->requireViewer()); - - $author_phids = $saved->getParameter('authorPHIDs', array()); - $author_phids = $datasource->evaluateTokens($author_phids); - if ($author_phids) { - $query->withAuthorPHIDs($author_phids); + if ($map['authorPHIDs']) { + $query->withAuthorPHIDs($map['authorPHIDs']); } - $statuses = $saved->getParameter('statuses', array()); - if ($statuses) { - $query->withStatuses($statuses); + if ($map['statuses']) { + $query->withStatuses($map['statuses']); } - $this->setQueryProjects($query, $saved); - return $query; } - public function buildSearchForm( - AphrontFormView $form, - PhabricatorSavedQuery $saved_query) { - - $author_phids = $saved_query->getParameter('authorPHIDs', array()); - $projects = $saved_query->getParameter('projects', array()); - - $statuses = array( - '' => pht('Any Status'), - 'closed' => pht('Closed'), - 'open' => pht('Open'), - ); - - $status = $saved_query->getParameter('statuses', array()); - $status = head($status); - - $form - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setDatasource(new PhabricatorPeopleUserFunctionDatasource()) - ->setName('authors') - ->setLabel(pht('Authors')) - ->setValue($author_phids)) - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setDatasource(new PhabricatorProjectLogicalDatasource()) - ->setName('projects') - ->setLabel(pht('Projects')) - ->setValue($projects)) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Status')) - ->setName('status') - ->setOptions($statuses) - ->setValue($status)); - } - protected function getURI($path) { return '/pholio/'.$path; } diff --git a/src/applications/pholio/storage/PholioMock.php b/src/applications/pholio/storage/PholioMock.php --- a/src/applications/pholio/storage/PholioMock.php +++ b/src/applications/pholio/storage/PholioMock.php @@ -13,6 +13,9 @@ const MARKUP_FIELD_DESCRIPTION = 'markup:description'; + const STATUS_OPEN = 'open'; + const STATUS_CLOSED = 'closed'; + protected $authorPHID; protected $viewPolicy; protected $editPolicy; @@ -41,7 +44,7 @@ return id(new PholioMock()) ->setAuthorPHID($actor->getPHID()) ->attachImages(array()) - ->setStatus('open') + ->setStatus(self::STATUS_OPEN) ->setViewPolicy($view_policy) ->setEditPolicy($edit_policy); } @@ -159,8 +162,8 @@ public function getStatuses() { $options = array(); - $options['open'] = pht('Open'); - $options['closed'] = pht('Closed'); + $options[self::STATUS_OPEN] = pht('Open'); + $options[self::STATUS_CLOSED] = pht('Closed'); return $options; } diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -113,6 +113,14 @@ return $query; } + if ($object instanceof PhabricatorProjectInterface) { + if (!empty($parameters['projectPHIDs'])) { + $query->withEdgeLogicConstraints( + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, + $parameters['projectPHIDs']); + } + } + if ($object instanceof PhabricatorSpacesInterface) { if (!empty($parameters['spacePHIDs'])) { $query->withSpacePHIDs($parameters['spacePHIDs']); @@ -168,6 +176,13 @@ return $fields; } + 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()) diff --git a/src/applications/search/field/PhabricatorSearchCheckboxesField.php b/src/applications/search/field/PhabricatorSearchCheckboxesField.php new file mode 100644 --- /dev/null +++ b/src/applications/search/field/PhabricatorSearchCheckboxesField.php @@ -0,0 +1,40 @@ +options = $options; + return $this; + } + + public function getOptions() { + return $this->options; + } + + protected function getDefaultValue() { + return array(); + } + + protected function getValueFromRequest(AphrontRequest $request, $key) { + return $this->getListFromRequest($request, $key); + } + + protected function newControl() { + $value = array_fuse($this->getValue()); + + $control = new AphrontFormCheckboxControl(); + foreach ($this->getOptions() as $key => $option) { + $control->addCheckbox( + $this->getKey().'[]', + $key, + $option, + isset($value[$key])); + } + + return $control; + } + +} diff --git a/src/applications/search/field/PhabricatorSearchProjectsField.php b/src/applications/search/field/PhabricatorSearchProjectsField.php new file mode 100644 --- /dev/null +++ b/src/applications/search/field/PhabricatorSearchProjectsField.php @@ -0,0 +1,50 @@ +getListFromRequest($request, $key); + + $phids = array(); + $slugs = array(); + $project_type = PhabricatorProjectProjectPHIDType::TYPECONST; + foreach ($list as $item) { + $type = phid_get_type($item); + if ($type == $project_type) { + $phids[] = $item; + } else { + if (PhabricatorTypeaheadDatasource::isFunctionToken($item)) { + // If this is a function, pass it through unchanged; we'll evaluate + // it later. + $phids[] = $item; + } else { + $slugs[] = $item; + } + } + } + + if ($slugs) { + $projects = id(new PhabricatorProjectQuery()) + ->setViewer($this->requireViewer()) + ->withSlugs($slugs) + ->execute(); + foreach ($projects as $project) { + $phids[] = $project->getPHID(); + } + $phids = array_unique($phids); + } + + return $phids; + + } + +} diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -76,6 +76,24 @@ return $this->beforeID; } + public function loadStandardPage(PhabricatorLiskDAO $table) { + $conn = $table->establishConnection('r'); + + $rows = queryfx_all( + $conn, + '%Q FROM %T %Q %Q %Q %Q %Q %Q %Q', + $this->buildSelectClause($conn), + $table->getTableName(), + (string)$this->getPrimaryTableAlias(), + $this->buildJoinClause($conn), + $this->buildWhereClause($conn), + $this->buildGroupClause($conn), + $this->buildHavingClause($conn), + $this->buildOrderClause($conn), + $this->buildLimitClause($conn)); + + return $table->loadAllFromArray($rows); + } /** * Get the viewer for making cursor paging queries.