1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-29 17:00:59 +01:00

Modernize "owner" typeahead datasource

Summary: Ref T4420. This one is users plus "upforgrabs". I renamed that to "none" and gave it a special visual style to make it more discoverable. Future diffs will improve this.

Test Plan:
  - Used it in global search.
  - Used it in batch editor.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T4420

Differential Revision: https://secure.phabricator.com/D9891
This commit is contained in:
epriestley 2014-07-17 15:49:00 -07:00
parent 27daa116c2
commit 7f0fb63c44
8 changed files with 60 additions and 214 deletions

View file

@ -2333,12 +2333,13 @@ phutil_register_library_map(array(
'PhabricatorTranslationsConfigOptions' => 'applications/config/option/PhabricatorTranslationsConfigOptions.php', 'PhabricatorTranslationsConfigOptions' => 'applications/config/option/PhabricatorTranslationsConfigOptions.php',
'PhabricatorTrivialTestCase' => 'infrastructure/testing/__tests__/PhabricatorTrivialTestCase.php', 'PhabricatorTrivialTestCase' => 'infrastructure/testing/__tests__/PhabricatorTrivialTestCase.php',
'PhabricatorTwoColumnExample' => 'applications/uiexample/examples/PhabricatorTwoColumnExample.php', 'PhabricatorTwoColumnExample' => 'applications/uiexample/examples/PhabricatorTwoColumnExample.php',
'PhabricatorTypeaheadCommonDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadCommonDatasourceController.php',
'PhabricatorTypeaheadCompositeDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php', 'PhabricatorTypeaheadCompositeDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php',
'PhabricatorTypeaheadDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php', 'PhabricatorTypeaheadDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php',
'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadDatasourceController.php', 'PhabricatorTypeaheadDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadDatasourceController.php',
'PhabricatorTypeaheadModularDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php', 'PhabricatorTypeaheadModularDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php',
'PhabricatorTypeaheadMonogramDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadMonogramDatasource.php', 'PhabricatorTypeaheadMonogramDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadMonogramDatasource.php',
'PhabricatorTypeaheadNoOwnerDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadNoOwnerDatasource.php',
'PhabricatorTypeaheadOwnerDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadOwnerDatasource.php',
'PhabricatorTypeaheadResult' => 'applications/typeahead/storage/PhabricatorTypeaheadResult.php', 'PhabricatorTypeaheadResult' => 'applications/typeahead/storage/PhabricatorTypeaheadResult.php',
'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadRuntimeCompositeDatasource.php', 'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadRuntimeCompositeDatasource.php',
'PhabricatorUIConfigOptions' => 'applications/config/option/PhabricatorUIConfigOptions.php', 'PhabricatorUIConfigOptions' => 'applications/config/option/PhabricatorUIConfigOptions.php',
@ -5173,12 +5174,13 @@ phutil_register_library_map(array(
'PhabricatorTranslationsConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorTranslationsConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorTrivialTestCase' => 'PhabricatorTestCase', 'PhabricatorTrivialTestCase' => 'PhabricatorTestCase',
'PhabricatorTwoColumnExample' => 'PhabricatorUIExample', 'PhabricatorTwoColumnExample' => 'PhabricatorUIExample',
'PhabricatorTypeaheadCommonDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
'PhabricatorTypeaheadCompositeDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorTypeaheadCompositeDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorTypeaheadDatasource' => 'Phobject', 'PhabricatorTypeaheadDatasource' => 'Phobject',
'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController', 'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
'PhabricatorTypeaheadModularDatasourceController' => 'PhabricatorTypeaheadDatasourceController', 'PhabricatorTypeaheadModularDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
'PhabricatorTypeaheadMonogramDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorTypeaheadMonogramDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorTypeaheadNoOwnerDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorTypeaheadOwnerDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'PhabricatorUIConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorUIConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorUIExampleRenderController' => 'PhabricatorController', 'PhabricatorUIExampleRenderController' => 'PhabricatorController',

View file

@ -63,6 +63,7 @@ final class ManiphestBatchEditController extends ManiphestController {
$projects_source = new PhabricatorProjectDatasource(); $projects_source = new PhabricatorProjectDatasource();
$mailable_source = new PhabricatorMetaMTAMailableDatasource(); $mailable_source = new PhabricatorMetaMTAMailableDatasource();
$owner_source = new PhabricatorTypeaheadOwnerDatasource();
require_celerity_resource('maniphest-batch-editor'); require_celerity_resource('maniphest-batch-editor');
Javelin::initBehavior( Javelin::initBehavior(
@ -76,9 +77,8 @@ final class ManiphestBatchEditController extends ManiphestController {
'placeholder' => $projects_source->getPlaceholderText(), 'placeholder' => $projects_source->getPlaceholderText(),
), ),
'owner' => array( 'owner' => array(
'src' => '/typeahead/common/searchowner/', 'src' => $owner_source->getDatasourceURI(),
'placeholder' => pht( 'placeholder' => $owner_source->getPlaceholderText(),
'Type a user name or "upforgrabs" to unassign...'),
'limit' => 1, 'limit' => 1,
), ),
'cc' => array( 'cc' => array(

View file

@ -142,7 +142,7 @@ final class PhabricatorSearchApplicationSearchEngine
id(new AphrontFormTokenizerControl()) id(new AphrontFormTokenizerControl())
->setName('ownerPHIDs') ->setName('ownerPHIDs')
->setLabel('Owners') ->setLabel('Owners')
->setDatasource('/typeahead/common/searchowner/') ->setDatasource(new PhabricatorTypeaheadOwnerDatasource())
->setValue($owner_handles)) ->setValue($owner_handles))
->appendChild( ->appendChild(
id(new AphrontFormCheckboxControl()) id(new AphrontFormCheckboxControl())

View file

@ -5,8 +5,6 @@ final class PhabricatorApplicationTypeahead extends PhabricatorApplication {
public function getRoutes() { public function getRoutes() {
return array( return array(
'/typeahead/' => array( '/typeahead/' => array(
'common/(?P<type>\w+)/'
=> 'PhabricatorTypeaheadCommonDatasourceController',
'class/(?:(?P<class>\w+)/)?' 'class/(?:(?P<class>\w+)/)?'
=> 'PhabricatorTypeaheadModularDatasourceController', => 'PhabricatorTypeaheadModularDatasourceController',
), ),

View file

@ -1,179 +0,0 @@
<?php
final class PhabricatorTypeaheadCommonDatasourceController
extends PhabricatorTypeaheadDatasourceController {
private $type;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->type = $data['type'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$query = $request->getStr('q');
$raw_query = $request->getStr('raw');
$need_users = false;
$need_upforgrabs = false;
$need_rich_data = false;
switch ($this->type) {
case 'searchowner':
$need_users = true;
$need_upforgrabs = true;
break;
}
$results = array();
if ($need_upforgrabs) {
$results[] = id(new PhabricatorTypeaheadResult())
->setName('upforgrabs (Up For Grabs)')
->setPHID(ManiphestTaskOwner::OWNER_UP_FOR_GRABS);
}
if ($need_users) {
$columns = array(
'isSystemAgent',
'isAdmin',
'isDisabled',
'userName',
'realName',
'phid');
if ($query) {
// This is an arbitrary limit which is just larger than any limit we
// actually use in the application.
// TODO: The datasource should pass this in the query.
$limit = 15;
$user_table = new PhabricatorUser();
$conn_r = $user_table->establishConnection('r');
$ids = queryfx_all(
$conn_r,
'SELECT id FROM %T WHERE username LIKE %>
ORDER BY username ASC LIMIT %d',
$user_table->getTableName(),
$query,
$limit);
$ids = ipull($ids, 'id');
if (count($ids) < $limit) {
// If we didn't find enough username hits, look for real name hits.
// We need to pull the entire pagesize so that we end up with the
// right number of items if this query returns many duplicate IDs
// that we've already selected.
$realname_ids = queryfx_all(
$conn_r,
'SELECT DISTINCT userID FROM %T WHERE token LIKE %>
ORDER BY token ASC LIMIT %d',
PhabricatorUser::NAMETOKEN_TABLE,
$query,
$limit);
$realname_ids = ipull($realname_ids, 'userID');
$ids = array_merge($ids, $realname_ids);
$ids = array_unique($ids);
$ids = array_slice($ids, 0, $limit);
}
// Always add the logged-in user because some tokenizers autosort them
// first. They'll be filtered out on the client side if they don't
// match the query.
$ids[] = $request->getUser()->getID();
if ($ids) {
$users = id(new PhabricatorUser())->loadColumnsWhere(
$columns,
'id IN (%Ld)',
$ids);
} else {
$users = array();
}
} else {
$users = id(new PhabricatorUser())->loadColumns($columns);
}
if ($need_rich_data) {
$phids = mpull($users, 'getPHID');
$handles = $this->loadViewerHandles($phids);
}
foreach ($users as $user) {
$closed = null;
if ($user->getIsDisabled()) {
$closed = pht('Disabled');
} else if ($user->getIsSystemAgent()) {
$closed = pht('Bot/Script');
}
$result = id(new PhabricatorTypeaheadResult())
->setName($user->getFullName())
->setURI('/p/'.$user->getUsername())
->setPHID($user->getPHID())
->setPriorityString($user->getUsername())
->setIcon('fa-user bluegrey')
->setPriorityType('user')
->setClosed($closed);
if ($need_rich_data) {
$display_type = 'User';
if ($user->getIsAdmin()) {
$display_type = 'Administrator';
}
$result->setDisplayType($display_type);
$result->setImageURI($handles[$user->getPHID()]->getImageURI());
}
$results[] = $result;
}
}
$content = mpull($results, 'getWireFormat');
if ($request->isAjax()) {
return id(new AphrontAjaxResponse())->setContent($content);
}
// If there's a non-Ajax request to this endpoint, show results in a tabular
// format to make it easier to debug typeahead output.
$rows = array();
foreach ($results as $result) {
$wire = $result->getWireFormat();
$rows[] = $wire;
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Name',
'URI',
'PHID',
'Priority',
'Display Name',
'Display Type',
'Image URI',
'Priority Type',
'Sprite Class',
));
$panel = new AphrontPanelView();
$panel->setHeader('Typeahead Results');
$panel->appendChild($table);
return $this->buildStandardPageResponse(
$panel,
array(
'title' => pht('Typeahead Results'),
'device' => true
));
}
}

View file

@ -0,0 +1,28 @@
<?php
final class PhabricatorTypeaheadNoOwnerDatasource
extends PhabricatorTypeaheadDatasource {
public function getPlaceholderText() {
return pht('Type "none"...');
}
public function getDatasourceApplicationClass() {
return null;
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$results = array();
$results[] = id(new PhabricatorTypeaheadResult())
->setName(pht('None'))
->setIcon('fa-ban orange')
->setPHID(ManiphestTaskOwner::OWNER_UP_FOR_GRABS);
return $results;
}
}

View file

@ -0,0 +1,17 @@
<?php
final class PhabricatorTypeaheadOwnerDatasource
extends PhabricatorTypeaheadCompositeDatasource {
public function getPlaceholderText() {
return pht('Type a user name or "none"...');
}
public function getComponentDatasources() {
return array(
new PhabricatorPeopleDatasource(),
new PhabricatorTypeaheadNoOwnerDatasource(),
);
}
}

View file

@ -7,7 +7,7 @@ final class AphrontFormTokenizerControl extends AphrontFormControl {
private $limit; private $limit;
private $placeholder; private $placeholder;
public function setDatasource($datasource) { public function setDatasource(PhabricatorTypeaheadDatasource $datasource) {
$this->datasource = $datasource; $this->datasource = $datasource;
return $this; return $this;
} }
@ -43,8 +43,11 @@ final class AphrontFormTokenizerControl extends AphrontFormControl {
$id = celerity_generate_unique_node_id(); $id = celerity_generate_unique_node_id();
} }
$placeholder = null;
if (!strlen($this->placeholder)) { if (!strlen($this->placeholder)) {
$placeholder = $this->getDefaultPlaceholder(); if ($this->datasource) {
$placeholder = $this->datasource->getPlaceholderText();
}
} else { } else {
$placeholder = $this->placeholder; $placeholder = $this->placeholder;
} }
@ -59,10 +62,9 @@ final class AphrontFormTokenizerControl extends AphrontFormControl {
$username = $this->user->getUsername(); $username = $this->user->getUsername();
} }
if ($this->datasource instanceof PhabricatorTypeaheadDatasource) { $datasource_uri = null;
if ($this->datasource) {
$datasource_uri = $this->datasource->getDatasourceURI(); $datasource_uri = $this->datasource->getDatasourceURI();
} else {
$datasource_uri = $this->datasource;
} }
if (!$this->disableBehavior) { if (!$this->disableBehavior) {
@ -80,26 +82,4 @@ final class AphrontFormTokenizerControl extends AphrontFormControl {
return $template->render(); return $template->render();
} }
private function getDefaultPlaceholder() {
$datasource = $this->datasource;
if ($datasource instanceof PhabricatorTypeaheadDatasource) {
return $datasource->getPlaceholderText();
}
$matches = null;
if (!preg_match('@^/typeahead/common/(.*)/$@', $datasource, $matches)) {
return null;
}
$request = $matches[1];
$map = array(
'searchowner' => pht('Type a user name...'),
);
return idx($map, $request);
}
} }