diff --git a/src/applications/audit/query/PhabricatorCommitSearchEngine.php b/src/applications/audit/query/PhabricatorCommitSearchEngine.php index 993626e1e3..e1b294a9d6 100644 --- a/src/applications/audit/query/PhabricatorCommitSearchEngine.php +++ b/src/applications/audit/query/PhabricatorCommitSearchEngine.php @@ -1,239 +1,252 @@ needAuditRequests(true) ->needCommitData(true) ->needDrafts(true); } protected function newResultBuckets() { return DiffusionCommitResultBucket::getAllResultBuckets(); } protected function buildQueryFromParameters(array $map) { $query = $this->newQuery(); if ($map['responsiblePHIDs']) { $query->withResponsiblePHIDs($map['responsiblePHIDs']); } if ($map['auditorPHIDs']) { $query->withAuditorPHIDs($map['auditorPHIDs']); } if ($map['authorPHIDs']) { $query->withAuthorPHIDs($map['authorPHIDs']); } if ($map['statuses']) { $query->withStatuses($map['statuses']); } if ($map['repositoryPHIDs']) { $query->withRepositoryPHIDs($map['repositoryPHIDs']); } if ($map['packagePHIDs']) { $query->withPackagePHIDs($map['packagePHIDs']); } if ($map['unreachable'] !== null) { $query->withUnreachable($map['unreachable']); } if ($map['ancestorsOf']) { $query->withAncestorsOf($map['ancestorsOf']); } return $query; } protected function buildCustomSearchFields() { return array( id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Responsible Users')) ->setKey('responsiblePHIDs') ->setConduitKey('responsible') ->setAliases(array('responsible', 'responsibles', 'responsiblePHID')) - ->setDatasource(new DifferentialResponsibleDatasource()), + ->setDatasource(new DifferentialResponsibleDatasource()) + ->setDescription( + pht( + 'Find commits where given users, projects, or packages are '. + 'responsible for the next steps in the audit workflow.')), id(new PhabricatorUsersSearchField()) ->setLabel(pht('Authors')) ->setKey('authorPHIDs') ->setConduitKey('authors') - ->setAliases(array('author', 'authors', 'authorPHID')), + ->setAliases(array('author', 'authors', 'authorPHID')) + ->setDescription(pht('Find commits authored by particular users.')), id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Auditors')) ->setKey('auditorPHIDs') ->setConduitKey('auditors') ->setAliases(array('auditor', 'auditors', 'auditorPHID')) - ->setDatasource(new DiffusionAuditorFunctionDatasource()), + ->setDatasource(new DiffusionAuditorFunctionDatasource()) + ->setDescription( + pht( + 'Find commits where given users, projects, or packages are '. + 'auditors.')), id(new PhabricatorSearchCheckboxesField()) ->setLabel(pht('Audit Status')) ->setKey('statuses') ->setAliases(array('status')) - ->setOptions(PhabricatorAuditCommitStatusConstants::getStatusNameMap()), + ->setOptions(PhabricatorAuditCommitStatusConstants::getStatusNameMap()) + ->setDescription(pht('Find commits with given audit statuses.')), id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Repositories')) ->setKey('repositoryPHIDs') ->setConduitKey('repositories') ->setAliases(array('repository', 'repositories', 'repositoryPHID')) - ->setDatasource(new DiffusionRepositoryFunctionDatasource()), + ->setDatasource(new DiffusionRepositoryFunctionDatasource()) + ->setDescription(pht('Find commits in particular repositories.')), id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Packages')) ->setKey('packagePHIDs') ->setConduitKey('packages') ->setAliases(array('package', 'packages', 'packagePHID')) - ->setDatasource(new PhabricatorOwnersPackageDatasource()), + ->setDatasource(new PhabricatorOwnersPackageDatasource()) + ->setDescription( + pht('Find commits which affect given packages.')), id(new PhabricatorSearchThreeStateField()) ->setLabel(pht('Unreachable')) ->setKey('unreachable') ->setOptions( pht('(Show All)'), pht('Show Only Unreachable Commits'), pht('Hide Unreachable Commits')) ->setDescription( pht( 'Find or exclude unreachable commits which are not ancestors of '. 'any branch, tag, or ref.')), id(new PhabricatorSearchStringListField()) ->setLabel(pht('Ancestors Of')) ->setKey('ancestorsOf') ->setDescription( pht( 'Find commits which are ancestors of a particular ref, '. 'like "master".')), ); } protected function getURI($path) { return '/diffusion/commit/'.$path; } protected function getBuiltinQueryNames() { $names = array(); if ($this->requireViewer()->isLoggedIn()) { $names['active'] = pht('Active Audits'); $names['authored'] = pht('Authored'); $names['audited'] = pht('Audited'); } $names['all'] = pht('All Commits'); return $names; } public function buildSavedQueryFromBuiltin($query_key) { $query = $this->newSavedQuery(); $query->setQueryKey($query_key); $viewer = $this->requireViewer(); $viewer_phid = $viewer->getPHID(); switch ($query_key) { case 'all': return $query; case 'active': $bucket_key = DiffusionCommitRequiredActionResultBucket::BUCKETKEY; $open = PhabricatorAuditCommitStatusConstants::getOpenStatusConstants(); $query ->setParameter('responsiblePHIDs', array($viewer_phid)) ->setParameter('statuses', $open) ->setParameter('bucket', $bucket_key) ->setParameter('unreachable', false); return $query; case 'authored': $query ->setParameter('authorPHIDs', array($viewer_phid)); return $query; case 'audited': $query ->setParameter('auditorPHIDs', array($viewer_phid)); return $query; } return parent::buildSavedQueryFromBuiltin($query_key); } protected function renderResultList( array $commits, PhabricatorSavedQuery $query, array $handles) { assert_instances_of($commits, 'PhabricatorRepositoryCommit'); $viewer = $this->requireViewer(); $bucket = $this->getResultBucket($query); $template = id(new PhabricatorAuditListView()) ->setViewer($viewer) ->setShowDrafts(true); $views = array(); if ($bucket) { $bucket->setViewer($viewer); try { $groups = $bucket->newResultGroups($query, $commits); foreach ($groups as $group) { // Don't show groups in Dashboard Panels if ($group->getObjects() || !$this->isPanelContext()) { $views[] = id(clone $template) ->setHeader($group->getName()) ->setNoDataString($group->getNoDataString()) ->setCommits($group->getObjects()); } } } catch (Exception $ex) { $this->addError($ex->getMessage()); } } else { $views[] = id(clone $template) ->setCommits($commits) ->setNoDataString(pht('No commits found.')); } if (!$views) { $views[] = id(new PhabricatorAuditListView()) ->setViewer($viewer) ->setNoDataString(pht('No commits found.')); } if (count($views) == 1) { $list = head($views)->buildList(); } else { $list = $views; } $result = new PhabricatorApplicationSearchResultView(); $result->setContent($list); return $result; } protected function getNewUserBody() { $view = id(new PHUIBigInfoView()) ->setIcon('fa-check-circle-o') ->setTitle(pht('Welcome to Audit')) ->setDescription( pht('Post-commit code review and auditing. Audits you are assigned '. 'to will appear here.')); return $view; } } diff --git a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php index 3d13f2c3d9..4a8fccb876 100644 --- a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php +++ b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php @@ -1,599 +1,601 @@ getCustomQueryMaps($query); // Make sure we emit empty maps as objects, not lists. foreach ($maps as $key => $map) { if (!$map) { $maps[$key] = (object)$map; } } if (!$maps) { $maps = (object)$maps; } return $maps; } protected function getCustomQueryMaps($query) { return array(); } public function getApplication() { $engine = $this->newSearchEngine(); $class = $engine->getApplicationClassName(); return PhabricatorApplication::getByClass($class); } final protected function defineParamTypes() { return array( 'queryKey' => 'optional string', 'constraints' => 'optional map', 'attachments' => 'optional map', 'order' => 'optional order', ) + $this->getPagerParamTypes(); } final protected function defineReturnType() { return 'map'; } final protected function execute(ConduitAPIRequest $request) { $engine = $this->newSearchEngine() ->setViewer($request->getUser()); return $engine->buildConduitResponse($request, $this); } final public function getMethodDescription() { return pht( 'This is a standard **ApplicationSearch** method which will let you '. 'list, query, or search for objects. For documentation on these '. 'endpoints, see **[[ %s | Conduit API: Using Search Endpoints ]]**.', PhabricatorEnv::getDoclink('Conduit API: Using Search Endpoints')); } final public function getMethodDocumentation() { $viewer = $this->getViewer(); $engine = $this->newSearchEngine() ->setViewer($viewer); $query = $engine->newQuery(); $out = array(); $out[] = $this->buildQueriesBox($engine); $out[] = $this->buildConstraintsBox($engine); $out[] = $this->buildOrderBox($engine, $query); $out[] = $this->buildFieldsBox($engine); $out[] = $this->buildAttachmentsBox($engine); $out[] = $this->buildPagingBox($engine); return $out; } private function buildQueriesBox( PhabricatorApplicationSearchEngine $engine) { $viewer = $this->getViewer(); $info = pht(<<loadAllNamedQueries(); $rows = array(); foreach ($named_queries as $named_query) { $builtin = $named_query->getIsBuiltin() ? pht('Builtin') : pht('Custom'); $rows[] = array( $named_query->getQueryKey(), $named_query->getQueryName(), $builtin, ); } $table = id(new AphrontTableView($rows)) ->setHeaders( array( pht('Query Key'), pht('Name'), pht('Builtin'), )) ->setColumnClasses( array( 'prewrap', 'pri wide', null, )); return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Builtin and Saved Queries')) ->setCollapsed(true) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($this->buildRemarkup($info)) ->appendChild($table); } private function buildConstraintsBox( PhabricatorApplicationSearchEngine $engine) { $info = pht(<<getSearchFieldsForConduit(); // As a convenience, put these fields at the very top, even if the engine // specifies and alternate display order for the web UI. These fields are // very important in the API and nearly useless in the web UI. $fields = array_select_keys( $fields, array('ids', 'phids')) + $fields; $rows = array(); foreach ($fields as $field) { $key = $field->getConduitKey(); $label = $field->getLabel(); $type_object = $field->getConduitParameterType(); if ($type_object) { $type = $type_object->getTypeName(); $description = $field->getDescription(); } else { $type = null; $description = phutil_tag('em', array(), pht('Not supported.')); } $rows[] = array( $key, $label, $type, $description, ); } $table = id(new AphrontTableView($rows)) ->setHeaders( array( pht('Key'), pht('Label'), pht('Type'), pht('Description'), )) ->setColumnClasses( array( 'prewrap', 'pri', 'prewrap', 'wide', )); return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Custom Query Constraints')) ->setCollapsed(true) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($this->buildRemarkup($info)) ->appendChild($table); } private function buildOrderBox( PhabricatorApplicationSearchEngine $engine, $query) { $orders_info = pht(<<getBuiltinOrders(); $rows = array(); foreach ($orders as $key => $order) { $rows[] = array( $key, $order['name'], implode(', ', $order['vector']), ); } $orders_table = id(new AphrontTableView($rows)) ->setHeaders( array( pht('Key'), pht('Description'), pht('Columns'), )) ->setColumnClasses( array( 'pri', '', 'wide', )); $columns_info = pht(<<getOrderableColumns(); $rows = array(); foreach ($columns as $key => $column) { $rows[] = array( $key, idx($column, 'unique') ? pht('Yes') : pht('No'), ); } $columns_table = id(new AphrontTableView($rows)) ->setHeaders( array( pht('Key'), pht('Unique'), )) ->setColumnClasses( array( 'pri', 'wide', )); return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Result Ordering')) ->setCollapsed(true) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($this->buildRemarkup($orders_info)) ->appendChild($orders_table) ->appendChild($this->buildRemarkup($columns_info)) ->appendChild($columns_table); } private function buildFieldsBox( PhabricatorApplicationSearchEngine $engine) { $info = pht(<<getAllConduitFieldSpecifications(); $rows = array(); foreach ($specs as $key => $spec) { $type = $spec->getType(); $description = $spec->getDescription(); $rows[] = array( $key, $type, $description, ); } $table = id(new AphrontTableView($rows)) ->setHeaders( array( pht('Key'), pht('Type'), pht('Description'), )) ->setColumnClasses( array( 'pri', 'mono', 'wide', )); return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Object Fields')) ->setCollapsed(true) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($this->buildRemarkup($info)) ->appendChild($table); } private function buildAttachmentsBox( PhabricatorApplicationSearchEngine $engine) { $info = pht(<<getConduitSearchAttachments(); $rows = array(); foreach ($attachments as $key => $attachment) { $rows[] = array( $key, $attachment->getAttachmentName(), $attachment->getAttachmentDescription(), ); } $table = id(new AphrontTableView($rows)) ->setNoDataString(pht('This call does not support any attachments.')) ->setHeaders( array( pht('Key'), pht('Name'), pht('Description'), )) ->setColumnClasses( array( 'prewrap', 'pri', 'wide', )); return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Attachments')) ->setCollapsed(true) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($this->buildRemarkup($info)) ->appendChild($table); } private function buildPagingBox( PhabricatorApplicationSearchEngine $engine) { $info = pht(<<setHeaderText(pht('Paging and Limits')) ->setCollapsed(true) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->appendChild($this->buildRemarkup($info)); } private function buildRemarkup($remarkup) { $viewer = $this->getViewer(); $view = new PHUIRemarkupView($viewer, $remarkup); $view->setRemarkupOptions( array( PHUIRemarkupView::OPTION_PRESERVE_LINEBREAKS => false, )); return id(new PHUIBoxView()) ->appendChild($view) ->addPadding(PHUI::PADDING_LARGE); } }