diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -28,7 +28,7 @@ 'rsrc/css/aphront/table-view.css' => '9258e19f', 'rsrc/css/aphront/tokenizer.css' => '056da01b', 'rsrc/css/aphront/tooltip.css' => '1a07aea8', - 'rsrc/css/aphront/typeahead-browse.css' => 'd8581d2c', + 'rsrc/css/aphront/typeahead-browse.css' => '8904346a', 'rsrc/css/aphront/typeahead.css' => 'd4f16145', 'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af', 'rsrc/css/application/auth/auth.css' => '0877ed6e', @@ -889,7 +889,7 @@ 'syntax-default-css' => '9923583c', 'syntax-highlighting-css' => '9fc496d5', 'tokens-css' => '3d0f239e', - 'typeahead-browse-css' => 'd8581d2c', + 'typeahead-browse-css' => '8904346a', 'unhandled-exception-css' => '4c96257a', ), 'requires' => array( diff --git a/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php b/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php --- a/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionRepositoryDatasource.php @@ -31,24 +31,50 @@ "phabricator-search-icon phui-font-fa phui-icon-view {$type_icon}"; $results = array(); - foreach ($repos as $repo) { - $display_name = $repo->getMonogram().' '.$repo->getName(); + foreach ($repos as $repository) { + $monogram = $repository->getMonogram(); + $name = $repository->getName(); - $name = $display_name; - $slug = $repo->getRepositorySlug(); + $display_name = "{$monogram} {$name}"; + + $parts = array(); + $parts[] = $name; + + $slug = $repository->getRepositorySlug(); if (strlen($slug)) { - $name = "{$name} {$slug}"; + $parts[] = $slug; + } + + $callsign = $repository->getCallsign(); + if ($callsign) { + $parts[] = $callsign; + } + + foreach ($repository->getAllMonograms() as $monogram) { + $parts[] = $monogram; } - $results[] = id(new PhabricatorTypeaheadResult()) + $name = implode(' ', $parts); + + $vcs = $repository->getVersionControlSystem(); + $vcs_type = PhabricatorRepositoryType::getNameForRepositoryType($vcs); + + $result = id(new PhabricatorTypeaheadResult()) ->setName($name) ->setDisplayName($display_name) - ->setURI($repo->getURI()) - ->setPHID($repo->getPHID()) - ->setPriorityString($repo->getMonogram()) + ->setURI($repository->getURI()) + ->setPHID($repository->getPHID()) + ->setPriorityString($repository->getMonogram()) ->setPriorityType('repo') ->setImageSprite($image_sprite) - ->setDisplayType(pht('Repository')); + ->setDisplayType(pht('Repository')) + ->addAttribute($vcs_type); + + if (!$repository->isTracked()) { + $result->setClosed(pht('Inactive')); + } + + $results[] = $result; } return $results; diff --git a/src/applications/diffusion/typeahead/DiffusionTaggedRepositoriesFunctionDatasource.php b/src/applications/diffusion/typeahead/DiffusionTaggedRepositoriesFunctionDatasource.php --- a/src/applications/diffusion/typeahead/DiffusionTaggedRepositoriesFunctionDatasource.php +++ b/src/applications/diffusion/typeahead/DiffusionTaggedRepositoriesFunctionDatasource.php @@ -43,7 +43,10 @@ ->setColor(null) ->setPHID('tagged('.$result->getPHID().')') ->setDisplayName(pht('Tagged: %s', $result->getDisplayName())) - ->setName('tagged '.$result->getName()); + ->setName('tagged '.$result->getName()) + ->resetAttributes() + ->addAttribute(pht('Function')) + ->addAttribute(pht('Select repositories tagged with this project.')); } return $results; diff --git a/src/applications/drydock/typeahead/DrydockBlueprintDatasource.php b/src/applications/drydock/typeahead/DrydockBlueprintDatasource.php --- a/src/applications/drydock/typeahead/DrydockBlueprintDatasource.php +++ b/src/applications/drydock/typeahead/DrydockBlueprintDatasource.php @@ -7,6 +7,10 @@ return pht('Type a blueprint name...'); } + public function getBrowseTitle() { + return pht('Browse Blueprints'); + } + public function getDatasourceApplicationClass() { return 'PhabricatorDrydockApplication'; } @@ -37,6 +41,9 @@ $result->setClosed(pht('Disabled')); } + $result->addAttribute( + $blueprint->getImplementation()->getBlueprintName()); + $results[] = $result; } diff --git a/src/applications/maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php --- a/src/applications/maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestTaskClosedStatusDatasource.php @@ -67,7 +67,8 @@ ->setName($name.' closed') ->setDisplayName($name) ->setPHID(self::FUNCTION_TOKEN) - ->setUnique(true); + ->setUnique(true) + ->addAttribute(pht('Select any closed status.')); } } diff --git a/src/applications/maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php --- a/src/applications/maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestTaskOpenStatusDatasource.php @@ -67,7 +67,8 @@ ->setName($name.' open') ->setDisplayName($name) ->setPHID(self::FUNCTION_TOKEN) - ->setUnique(true); + ->setUnique(true) + ->addAttribute(pht('Select any open status.')); } } diff --git a/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php --- a/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestTaskPriorityDatasource.php @@ -32,7 +32,8 @@ $result = id(new PhabricatorTypeaheadResult()) ->setIcon(ManiphestTaskPriority::getTaskPriorityIcon($value)) ->setPHID($value) - ->setName($name); + ->setName($name) + ->addAttribute(pht('Priority')); if (ManiphestTaskPriority::isDisabledPriority($value)) { $result->setClosed(pht('Disabled')); diff --git a/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php --- a/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php @@ -35,6 +35,12 @@ ->setPHID($value) ->setName($name); + if (ManiphestTaskStatus::isOpenStatus($value)) { + $result->addAttribute(pht('Open Status')); + } else { + $result->addAttribute(pht('Closed Status')); + } + if (ManiphestTaskStatus::isDisabledStatus($value)) { $result->setClosed(pht('Disabled')); } diff --git a/src/applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php b/src/applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php --- a/src/applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php +++ b/src/applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php @@ -62,7 +62,8 @@ ->setDisplayName($name) ->setIcon('fa-certificate') ->setPHID(self::FUNCTION_TOKEN) - ->setUnique(true); + ->setUnique(true) + ->addAttribute(pht('Select results with any owner.')); } } diff --git a/src/applications/people/typeahead/PhabricatorPeopleDatasource.php b/src/applications/people/typeahead/PhabricatorPeopleDatasource.php --- a/src/applications/people/typeahead/PhabricatorPeopleDatasource.php +++ b/src/applications/people/typeahead/PhabricatorPeopleDatasource.php @@ -3,18 +3,6 @@ final class PhabricatorPeopleDatasource extends PhabricatorTypeaheadDatasource { - private $enrichResults; - - /** - * Controls enriched rendering, for global search. This is a bit hacky and - * should probably be handled in a more general way, but is fairly reasonable - * for now. - */ - public function setEnrichResults($enrich) { - $this->enrichResults = $enrich; - return $this; - } - public function getBrowseTitle() { return pht('Browse Users'); } @@ -40,7 +28,9 @@ $users = $this->executeQuery($query); - if ($this->enrichResults && $users) { + $is_browse = $this->getIsBrowse(); + + if ($is_browse && $users) { $phids = mpull($users, 'getPHID'); $handles = id(new PhabricatorHandleQuery()) ->setViewer($viewer) @@ -50,6 +40,8 @@ $results = array(); foreach ($users as $user) { + $phid = $user->getPHID(); + $closed = null; if ($user->getIsDisabled()) { $closed = pht('Disabled'); @@ -64,7 +56,7 @@ $result = id(new PhabricatorTypeaheadResult()) ->setName($user->getFullName()) ->setURI('/p/'.$username.'/') - ->setPHID($user->getPHID()) + ->setPHID($phid) ->setPriorityString($username) ->setPriorityType('user') ->setAutocomplete('@'.$username) @@ -74,13 +66,29 @@ $result->setIcon('fa-envelope-o'); } - if ($this->enrichResults) { - $display_type = pht('User'); + if ($is_browse) { + $handle = $handles[$phid]; + + $result + ->setIcon($handle->getIcon()) + ->setImageURI($handle->getImageURI()) + ->addAttribute($handle->getSubtitle()); + + if ($user->getIsAdmin()) { + $result->addAttribute( + array( + id(new PHUIIconView())->setIcon('fa-star'), + ' ', + pht('Administrator'), + )); + } + if ($user->getIsAdmin()) { $display_type = pht('Administrator'); + } else { + $display_type = pht('User'); } $result->setDisplayType($display_type); - $result->setImageURI($handles[$user->getPHID()]->getImageURI()); } $results[] = $result; diff --git a/src/applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php b/src/applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php --- a/src/applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php +++ b/src/applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php @@ -69,7 +69,8 @@ ->setDisplayName($name) ->setIcon('fa-ban') ->setPHID('none()') - ->setUnique(true); + ->setUnique(true) + ->addAttribute(pht('Select results with no owner.')); } } diff --git a/src/applications/people/typeahead/PhabricatorViewerDatasource.php b/src/applications/people/typeahead/PhabricatorViewerDatasource.php --- a/src/applications/people/typeahead/PhabricatorViewerDatasource.php +++ b/src/applications/people/typeahead/PhabricatorViewerDatasource.php @@ -74,7 +74,8 @@ ->setName(pht('Current Viewer')) ->setPHID('viewer()') ->setIcon('fa-user') - ->setUnique(true); + ->setUnique(true) + ->addAttribute(pht('Select current viewer.')); } } diff --git a/src/applications/project/typeahead/PhabricatorProjectDatasource.php b/src/applications/project/typeahead/PhabricatorProjectDatasource.php --- a/src/applications/project/typeahead/PhabricatorProjectDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectDatasource.php @@ -55,9 +55,25 @@ $has_cols = array_fill_keys(array_keys($projs), true); } + $is_browse = $this->getIsBrowse(); + if ($is_browse && $projs) { + // TODO: This is a little ad-hoc, but we don't currently have + // infrastructure for bulk querying custom fields efficiently. + $table = new PhabricatorProjectCustomFieldStorage(); + $descriptions = $table->loadAllWhere( + 'objectPHID IN (%Ls) AND fieldIndex = %s', + array_keys($projs), + PhabricatorHash::digestForIndex('std:project:internal:description')); + $descriptions = mpull($descriptions, 'getFieldValue', 'getObjectPHID'); + } else { + $descriptions = array(); + } + $results = array(); foreach ($projs as $proj) { - if (!isset($has_cols[$proj->getPHID()])) { + $phid = $proj->getPHID(); + + if (!isset($has_cols[$phid])) { continue; } @@ -99,7 +115,7 @@ ->setDisplayName($proj->getDisplayName()) ->setDisplayType($proj->getDisplayIconName()) ->setURI($proj->getURI()) - ->setPHID($proj->getPHID()) + ->setPHID($phid) ->setIcon($proj->getDisplayIconIcon()) ->setColor($proj->getColor()) ->setPriorityType('proj') @@ -111,6 +127,16 @@ $proj_result->setImageURI($proj->getProfileImageURI()); + if ($is_browse) { + $proj_result->addAttribute($proj->getDisplayIconName()); + + $description = idx($descriptions, $phid); + if (strlen($description)) { + $summary = PhabricatorMarkupEngine::summarize($description); + $proj_result->addAttribute($summary); + } + } + $results[] = $proj_result; } diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php --- a/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php @@ -86,20 +86,24 @@ $result ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION) ->setIcon('fa-asterisk') - ->setColor(null); + ->setColor(null) + ->resetAttributes() + ->addAttribute(pht('Function')); if ($return_any) { $return[] = id(clone $result) ->setPHID('any('.$result->getPHID().')') ->setDisplayName(pht('In Any: %s', $result->getDisplayName())) - ->setName('any '.$result->getName()); + ->setName('any '.$result->getName()) + ->addAttribute(pht('Include results tagged with this project.')); } if ($return_not) { $return[] = id(clone $result) ->setPHID('not('.$result->getPHID().')') ->setDisplayName(pht('Not In: %s', $result->getDisplayName())) - ->setName('not '.$result->getName()); + ->setName('not '.$result->getName()) + ->addAttribute(pht('Exclude results tagged with this project.')); } } diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php --- a/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php @@ -96,7 +96,8 @@ ->setName(pht('Current Viewer\'s Projects')) ->setPHID('viewerprojects()') ->setIcon('fa-asterisk') - ->setUnique(true); + ->setUnique(true) + ->addAttribute(pht('Select projects current viewer is a member of.')); } } diff --git a/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php b/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php --- a/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectMembersDatasource.php @@ -44,7 +44,10 @@ ->setColor(null) ->setPHID('members('.$result->getPHID().')') ->setDisplayName(pht('Members: %s', $result->getDisplayName())) - ->setName($result->getName().' members'); + ->setName($result->getName().' members') + ->resetAttributes() + ->addAttribute(pht('Function')) + ->addAttribute(pht('Select project members.')); } return $results; diff --git a/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php b/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php --- a/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectNoProjectsDatasource.php @@ -68,7 +68,8 @@ ->setPHID('null()') ->setIcon('fa-ban') ->setName('null '.$name) - ->setDisplayName($name); + ->setDisplayName($name) + ->addAttribute(pht('Select results with no tags.')); } } diff --git a/src/applications/search/typeahead/PhabricatorSearchDatasource.php b/src/applications/search/typeahead/PhabricatorSearchDatasource.php --- a/src/applications/search/typeahead/PhabricatorSearchDatasource.php +++ b/src/applications/search/typeahead/PhabricatorSearchDatasource.php @@ -16,14 +16,22 @@ } public function getComponentDatasources() { - return array( - id(new PhabricatorPeopleDatasource())->setEnrichResults(true), + $sources = array( + new PhabricatorPeopleDatasource(), new PhabricatorProjectDatasource(), new PhabricatorApplicationDatasource(), new PhabricatorTypeaheadMonogramDatasource(), new DiffusionRepositoryDatasource(), new DiffusionSymbolDatasource(), ); + + // These results are always rendered in the full browse display mode, so + // set the browse flag on all component sources. + foreach ($sources as $source) { + $source->setIsBrowse(true); + } + + return $sources; } } diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php --- a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php +++ b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php @@ -65,7 +65,8 @@ } $composite - ->setOffset($offset); + ->setOffset($offset) + ->setIsBrowse(true); } $results = $composite->loadResults(); @@ -142,8 +143,7 @@ $items = array(); foreach ($results as $result) { - $token = PhabricatorTypeaheadTokenView::newFromTypeaheadResult( - $result); + $information = $this->renderBrowseResult($result); // Disable already-selected tokens. $disabled = isset($exclude[$result->getPHID()]); @@ -167,8 +167,8 @@ 'class' => 'typeahead-browse-item grouped', ), array( - $token, $button, + $information, )); } @@ -350,4 +350,56 @@ ->appendChild($view); } + private function renderBrowseResult(PhabricatorTypeaheadResult $result) { + $class = array(); + $style = array(); + $separator = " \xC2\xB7 "; + + $class[] = 'phabricator-main-search-typeahead-result'; + + $name = phutil_tag( + 'div', + array( + 'class' => 'result-name', + ), + $result->getDisplayName()); + + $icon = $result->getIcon(); + $icon = id(new PHUIIconView())->setIcon($icon); + + $attributes = $result->getAttributes(); + $attributes = phutil_implode_html($separator, $attributes); + $attributes = array($icon, ' ', $attributes); + + $closed = $result->getClosed(); + if ($closed) { + $class[] = 'result-closed'; + $attributes = array($closed, $separator, $attributes); + } + + $attributes = phutil_tag( + 'div', + array( + 'class' => 'result-type', + ), + $attributes); + + $image = $result->getImageURI(); + if ($image) { + $style[] = 'background-image: url('.$image.');'; + $class[] = 'has-image'; + } + + return phutil_tag( + 'div', + array( + 'class' => implode(' ', $class), + 'style' => implode(' ', $style), + ), + array( + $name, + $attributes, + )); + } + } diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php @@ -37,6 +37,7 @@ } $stack = $this->getFunctionStack(); + $is_browse = $this->getIsBrowse(); $results = array(); foreach ($this->getUsableDatasources() as $source) { @@ -70,6 +71,10 @@ $source->setLimit($offset + $limit); } + if ($is_browse) { + $source->setIsBrowse(true); + } + $source_results = $source->loadResults(); $source_results = $source->didLoadResults($source_results); $results[] = $source_results; diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php @@ -12,6 +12,7 @@ private $limit; private $parameters = array(); private $functionStack = array(); + private $isBrowse; public function setLimit($limit) { $this->limit = $limit; @@ -71,6 +72,15 @@ return idx($this->parameters, $name, $default); } + public function setIsBrowse($is_browse) { + $this->isBrowse = $is_browse; + return $this; + } + + public function getIsBrowse() { + return $this->isBrowse; + } + public function getDatasourceURI() { $uri = new PhutilURI('/typeahead/class/'.get_class($this).'/'); $uri->setQueryParams($this->parameters); @@ -199,7 +209,8 @@ protected function newFunctionResult() { return id(new PhabricatorTypeaheadResult()) ->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION) - ->setIcon('fa-asterisk'); + ->setIcon('fa-asterisk') + ->addAttribute(pht('Function')); } public function newInvalidToken($name) { diff --git a/src/applications/typeahead/storage/PhabricatorTypeaheadResult.php b/src/applications/typeahead/storage/PhabricatorTypeaheadResult.php --- a/src/applications/typeahead/storage/PhabricatorTypeaheadResult.php +++ b/src/applications/typeahead/storage/PhabricatorTypeaheadResult.php @@ -17,6 +17,7 @@ private $tokenType; private $unique; private $autocomplete; + private $attributes = array(); public function setIcon($icon) { $this->icon = $icon; @@ -188,4 +189,26 @@ return null; } + public function getImageURI() { + return $this->imageURI; + } + + public function getClosed() { + return $this->closed; + } + + public function resetAttributes() { + $this->attributes = array(); + return $this; + } + + public function getAttributes() { + return $this->attributes; + } + + public function addAttribute($attribute) { + $this->attributes[] = $attribute; + return $this; + } + } diff --git a/webroot/rsrc/css/aphront/typeahead-browse.css b/webroot/rsrc/css/aphront/typeahead-browse.css --- a/webroot/rsrc/css/aphront/typeahead-browse.css +++ b/webroot/rsrc/css/aphront/typeahead-browse.css @@ -57,10 +57,26 @@ .typeahead-browse-item button { float: right; - margin: 2px 6px; + margin: 8px 6px 0; } .typeahead-browse-item a.jx-tokenizer-token { margin-top: 1px; margin-left: 6px; } + +.typeahead-browse-item .phabricator-main-search-typeahead-result { + margin: 2px 0; + padding: 0 8px; +} + +.typeahead-browse-item .phabricator-main-search-typeahead-result.has-image { + padding-left: 48px; +} + +.typeahead-browse-item + .phabricator-main-search-typeahead-result.result-closed + .result-name { + text-decoration: line-through; + color: {$lightgreytext}; +}