Add a basic search typeahead
Summary: This needs a bunch of refinement but pretty much works. Currently shows only users and applications. Plans: - Show actual search results too. - Clean up the datasource endpoint so it's less of a mess. - Make other typeaheads look more like this one. - Improve sorting. - Make object names hit the named objects as the first match. Test Plan: Will attach screenshots. Reviewers: btrahan, vrana, chad Reviewed By: vrana CC: aran Maniphest Tasks: T1569 Differential Revision: https://secure.phabricator.com/D3110
This commit is contained in:
@@ -1439,6 +1439,21 @@ celerity_register_resource_map(array(
|
||||
),
|
||||
'disk' => '/rsrc/js/application/core/behavior-oncopy.js',
|
||||
),
|
||||
'javelin-behavior-phabricator-search-typeahead' =>
|
||||
array(
|
||||
'uri' => '/res/9ceffb09/rsrc/js/application/core/behavior-search-typeahead.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-typeahead-ondemand-source',
|
||||
2 => 'javelin-typeahead',
|
||||
3 => 'javelin-dom',
|
||||
4 => 'javelin-uri',
|
||||
5 => 'javelin-stratcom',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/core/behavior-search-typeahead.js',
|
||||
),
|
||||
'javelin-behavior-phabricator-tooltips' =>
|
||||
array(
|
||||
'uri' => '/res/49f92a92/rsrc/js/application/core/behavior-tooltip.js',
|
||||
@@ -2263,7 +2278,7 @@ celerity_register_resource_map(array(
|
||||
),
|
||||
'phabricator-main-menu-view' =>
|
||||
array(
|
||||
'uri' => '/res/795788ca/rsrc/css/application/base/main-menu-view.css',
|
||||
'uri' => '/res/5bae3234/rsrc/css/application/base/main-menu-view.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
|
||||
@@ -30,15 +30,35 @@ abstract class PhabricatorApplication {
|
||||
|
||||
|
||||
public function getName() {
|
||||
return substr(__CLASS__, strlen('PhabricatorApplication'));
|
||||
return substr(get_class($this), strlen('PhabricatorApplication'));
|
||||
}
|
||||
|
||||
public function getShortDescription() {
|
||||
return $this->getName().' Application';
|
||||
}
|
||||
|
||||
public function isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getPHID() {
|
||||
return 'PHID-APPS-'.get_class($this);
|
||||
}
|
||||
|
||||
/* -( Application Information )-------------------------------------------- */
|
||||
public function getTypeaheadURI() {
|
||||
return $this->getBaseURI();
|
||||
}
|
||||
|
||||
public function getBaseURI() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getIconURI() {
|
||||
return PhabricatorUser::getDefaultProfileImageURI();
|
||||
}
|
||||
|
||||
|
||||
/* -( URI Routing )-------------------------------------------------------- */
|
||||
|
||||
|
||||
public function getRoutes() {
|
||||
|
||||
@@ -24,5 +24,13 @@ final class PhabricatorApplicationDifferential extends PhabricatorApplication {
|
||||
);
|
||||
}
|
||||
|
||||
public function getBaseURI() {
|
||||
return '/differential/';
|
||||
}
|
||||
|
||||
public function getShortDescription() {
|
||||
return 'Code Review Application';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,10 @@ final class PhabricatorTypeaheadCommonDatasourceController
|
||||
$request = $this->getRequest();
|
||||
$query = $request->getStr('q');
|
||||
|
||||
$need_rich_data = false;
|
||||
|
||||
$need_users = false;
|
||||
$need_applications = false;
|
||||
$need_all_users = false;
|
||||
$need_lists = false;
|
||||
$need_projs = false;
|
||||
@@ -38,6 +41,11 @@ final class PhabricatorTypeaheadCommonDatasourceController
|
||||
$need_arcanist_projects = false;
|
||||
$need_noproject = false;
|
||||
switch ($this->type) {
|
||||
case 'mainsearch':
|
||||
$need_users = true;
|
||||
$need_applications = true;
|
||||
$need_rich_data = true;
|
||||
break;
|
||||
case 'searchowner':
|
||||
$need_users = true;
|
||||
$need_upforgrabs = true;
|
||||
@@ -78,9 +86,20 @@ final class PhabricatorTypeaheadCommonDatasourceController
|
||||
case 'arcanistprojects':
|
||||
$need_arcanist_projects = true;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// TODO: We transfer these fields without keys as an opitimization, but this
|
||||
// function is hard to read as a result. Until we can sort it out, here's
|
||||
// what the position arguments mean:
|
||||
//
|
||||
// 0: (required) name to match against what the user types
|
||||
// 1: (optional) URI
|
||||
// 2: (required) PHID
|
||||
// 3: (optional) priority matching string
|
||||
// 4: (optional) display name [overrides position 0]
|
||||
// 5: (optional) display type
|
||||
// 6: (optional) image URI
|
||||
|
||||
$data = array();
|
||||
|
||||
if ($need_upforgrabs) {
|
||||
@@ -106,11 +125,16 @@ final class PhabricatorTypeaheadCommonDatasourceController
|
||||
'userName',
|
||||
'realName',
|
||||
'phid');
|
||||
|
||||
if ($need_rich_data) {
|
||||
$columns[] = 'profileImagePHID';
|
||||
}
|
||||
|
||||
if ($query) {
|
||||
$conn_r = id(new PhabricatorUser())->establishConnection('r');
|
||||
$ids = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT DISTINCT userID FROM %T WHERE token LIKE %>',
|
||||
'SELECT DISTINCT userID FROM %T WHERE token LIKE %> OR 1 = 1',
|
||||
PhabricatorUser::NAMETOKEN_TABLE,
|
||||
$query);
|
||||
$ids = ipull($ids, 'userID');
|
||||
@@ -125,6 +149,12 @@ final class PhabricatorTypeaheadCommonDatasourceController
|
||||
} else {
|
||||
$users = id(new PhabricatorUser())->loadColumns($columns);
|
||||
}
|
||||
|
||||
if ($need_rich_data) {
|
||||
$phids = mpull($users, 'getPHID');
|
||||
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
|
||||
}
|
||||
|
||||
foreach ($users as $user) {
|
||||
if (!$need_all_users) {
|
||||
if ($user->getIsSystemAgent()) {
|
||||
@@ -134,12 +164,18 @@ final class PhabricatorTypeaheadCommonDatasourceController
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$data[] = array(
|
||||
$spec = array(
|
||||
$user->getUsername().' ('.$user->getRealName().')',
|
||||
'/p/'.$user->getUsername(),
|
||||
$user->getPHID(),
|
||||
$user->getUsername(),
|
||||
null,
|
||||
'User',
|
||||
);
|
||||
if ($need_rich_data) {
|
||||
$spec[] = $handles[$user->getPHID()]->getImageURI();
|
||||
}
|
||||
$data[] = $spec;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +237,33 @@ final class PhabricatorTypeaheadCommonDatasourceController
|
||||
}
|
||||
}
|
||||
|
||||
if ($need_applications) {
|
||||
$applications = PhabricatorApplication::getAllInstalledApplications();
|
||||
foreach ($applications as $application) {
|
||||
$uri = $application->getTypeaheadURI();
|
||||
if (!$uri) {
|
||||
continue;
|
||||
}
|
||||
$data[] = array(
|
||||
$application->getName().' '.$application->getShortDescription(),
|
||||
$uri,
|
||||
$application->getPHID(),
|
||||
$application->getName(),
|
||||
$application->getName(),
|
||||
$application->getShortDescription(),
|
||||
$application->getIconURI(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$need_rich_data) {
|
||||
foreach ($data as $key => $info) {
|
||||
unset($data[$key][4]);
|
||||
unset($data[$key][5]);
|
||||
unset($data[$key][6]);
|
||||
}
|
||||
}
|
||||
|
||||
return id(new AphrontAjaxResponse())
|
||||
->setContent($data);
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ final class PhabricatorMainMenuSearchView extends AphrontView {
|
||||
public function render() {
|
||||
$user = $this->user;
|
||||
|
||||
$target_id = celerity_generate_unique_node_id();
|
||||
$search_id = $this->getID();
|
||||
|
||||
$input = phutil_render_tag(
|
||||
@@ -49,16 +50,28 @@ final class PhabricatorMainMenuSearchView extends AphrontView {
|
||||
array(
|
||||
'type' => 'text',
|
||||
'name' => 'query',
|
||||
'id' => $search_id,
|
||||
'id' => $search_id,
|
||||
'autocomplete' => 'off',
|
||||
));
|
||||
|
||||
$scope = $this->scope;
|
||||
|
||||
Javelin::initBehavior(
|
||||
'placeholder',
|
||||
$target = javelin_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'id' => $search_id,
|
||||
'text' => PhabricatorSearchScope::getScopePlaceholder($scope),
|
||||
'id' => $target_id,
|
||||
'class' => 'phabricator-main-menu-search-target',
|
||||
),
|
||||
'');
|
||||
|
||||
Javelin::initBehavior(
|
||||
'phabricator-search-typeahead',
|
||||
array(
|
||||
'id' => $target_id,
|
||||
'input' => $search_id,
|
||||
'src' => '/typeahead/common/mainsearch/',
|
||||
'limit' => 10,
|
||||
'placeholder' => PhabricatorSearchScope::getScopePlaceholder($scope),
|
||||
));
|
||||
|
||||
$scope_input = phutil_render_tag(
|
||||
@@ -79,6 +92,7 @@ final class PhabricatorMainMenuSearchView extends AphrontView {
|
||||
$input.
|
||||
'<button>Search</button>'.
|
||||
$scope_input.
|
||||
$target.
|
||||
'</div>');
|
||||
|
||||
$group = new PhabricatorMainMenuGroupView();
|
||||
|
||||
Reference in New Issue
Block a user