1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-30 10:42:41 +01:00

Add contextual typeahead function documentation

Summary: Ref T4100. This integration into the "Browse" dialog is probably a little more heavy-handed than we should shoot for.

Test Plan:
{F377131}

{F377132}

Reviewers: btrahan, chad

Reviewed By: chad

Subscribers: epriestley

Maniphest Tasks: T4100

Differential Revision: https://secure.phabricator.com/D12482
This commit is contained in:
epriestley 2015-04-20 13:43:07 -07:00
parent 6193548b46
commit bae0a85dbc
12 changed files with 292 additions and 7 deletions

View file

@ -2639,6 +2639,7 @@ phutil_register_library_map(array(
'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',
'PhabricatorTypeaheadFunctionHelpController' => 'applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php',
'PhabricatorTypeaheadInvalidTokenException' => 'applications/typeahead/exception/PhabricatorTypeaheadInvalidTokenException.php', 'PhabricatorTypeaheadInvalidTokenException' => 'applications/typeahead/exception/PhabricatorTypeaheadInvalidTokenException.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',
@ -6054,6 +6055,7 @@ phutil_register_library_map(array(
'PhabricatorTypeaheadCompositeDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorTypeaheadCompositeDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorTypeaheadDatasource' => 'Phobject', 'PhabricatorTypeaheadDatasource' => 'Phobject',
'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController', 'PhabricatorTypeaheadDatasourceController' => 'PhabricatorController',
'PhabricatorTypeaheadFunctionHelpController' => 'PhabricatorTypeaheadDatasourceController',
'PhabricatorTypeaheadInvalidTokenException' => 'Exception', 'PhabricatorTypeaheadInvalidTokenException' => 'Exception',
'PhabricatorTypeaheadModularDatasourceController' => 'PhabricatorTypeaheadDatasourceController', 'PhabricatorTypeaheadModularDatasourceController' => 'PhabricatorTypeaheadDatasourceController',
'PhabricatorTypeaheadMonogramDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorTypeaheadMonogramDatasource' => 'PhabricatorTypeaheadDatasource',

View file

@ -20,7 +20,20 @@ final class ManiphestNoOwnerDatasource
public function getDatasourceFunctions() { public function getDatasourceFunctions() {
return array( return array(
'none' => array( 'none' => array(
'name' => pht('Find results which are not assigned.'), 'name' => pht('No Owner'),
'summary' => pht('Find results which are not assigned.'),
'description' => pht(
'This function includes results which have no owner. Use a query '.
'like this to find unassigned results:'.
"\n\n".
'> none()'.
"\n\n".
'If you combine this function with other functions, the query will '.
'return results which match the other selectors //or// have no '.
'owner. For example, this query will find results which are owned '.
'by `alincoln`, and will also find results which have no owner:'.
"\n\n".
'> alincoln, none()'),
), ),
); );
} }

View file

@ -24,10 +24,54 @@ final class PhabricatorProjectLogicalOrNotDatasource
public function getDatasourceFunctions() { public function getDatasourceFunctions() {
return array( return array(
'any' => array( 'any' => array(
'name' => pht('Find results in any of several projects.'), 'name' => pht('In Any: ...'),
'arguments' => pht('project'),
'summary' => pht('Find results in any of several projects.'),
'description' => pht(
'This function allows you to find results in one of several '.
'projects. Another way to think of this function is that it '.
'allows you to perform an "or" query.'.
"\n\n".
'By default, if you enter several projects, results are returned '.
'only if they belong to all of the projects you enter. That is, '.
'this query will only return results in //both// projects:'.
"\n\n".
'> ios, android'.
"\n\n".
'If you want to find results in any of several projects, you can '.
'use the `any()` function. For example, you can use this query to '.
'find results which are in //either// project:'.
"\n\n".
'> any(ios), any(android)'.
"\n\n".
'You can combine the `any()` function with normal project tokens '.
'to refine results. For example, use this query to find bugs in '.
'//either// iOS or Android:'.
"\n\n".
'> bug, any(ios), any(android)'),
), ),
'not' => array( 'not' => array(
'name' => pht('Find results not in specific projects.'), 'name' => pht('Not In: ...'),
'arguments' => pht('project'),
'summary' => pht('Find results not in specific projects.'),
'description' => pht(
'This function allows you to find results which are not in '.
'one or more projects. For example, use this query to find '.
'results which are not associated with a specific project:'.
"\n\n".
'> not(vanilla)'.
"\n\n".
'You can exclude multiple projects. This will cause the query '.
'to return only results which are not in any of the excluded '.
'projects:'.
"\n\n".
'> not(vanilla), not(chocolate)'.
"\n\n".
'You can combine this function with other functions to refine '.
'results. For example, use this query to find iOS results which '.
'are not bugs:'.
"\n\n".
'> ios, not(bug)'),
), ),
); );
} }

View file

@ -24,7 +24,17 @@ final class PhabricatorProjectLogicalUserDatasource
public function getDatasourceFunctions() { public function getDatasourceFunctions() {
return array( return array(
'projects' => array( 'projects' => array(
'name' => pht("Find results in any of a user's projects."), 'name' => pht('Projects: ...'),
'arguments' => pht('username'),
'summary' => pht("Find results in any of a user's projects."),
'description' => pht(
'This function allows you to find results associated with any '.
'of the projects a specified user is a member of. For example, '.
'this will find results associated with all of the projects '.
'`alincoln` is a member of:'.
"\n\n".
'> projects(alincoln)'.
"\n\n"),
), ),
); );
} }

View file

@ -18,7 +18,19 @@ final class PhabricatorProjectLogicalViewerDatasource
public function getDatasourceFunctions() { public function getDatasourceFunctions() {
return array( return array(
'viewerprojects' => array( 'viewerprojects' => array(
'name' => pht("Find results in any of the current viewer's projects."), 'name' => pht("Current Viewer's Projects"),
'summary' => pht(
"Find results in any of the current viewer's projects."),
'description' => pht(
"This function matches results in any of the current viewing ".
"user's projects:".
"\n\n".
"> viewerprojects()".
"\n\n".
"This normally means //your// projects, but if you save a query ".
"using this function and send it to someone else, it will mean ".
"//their// projects when they run it (they become the currnet ".
"viewer). This can be useful for building dashboard panels."),
), ),
); );
} }

View file

@ -24,7 +24,14 @@ final class PhabricatorProjectMembersDatasource
public function getDatasourceFunctions() { public function getDatasourceFunctions() {
return array( return array(
'members' => array( 'members' => array(
'name' => pht('Find results for members of a project.'), 'name' => pht('Members: ...'),
'arguments' => pht('project'),
'summary' => pht('Find results for members of a project.'),
'description' => pht(
'This function allows you to find results for any of the members '.
'of a project:'.
"\n\n".
'> members(frontend)'),
), ),
); );
} }

View file

@ -18,7 +18,15 @@ final class PhabricatorProjectNoProjectsDatasource
public function getDatasourceFunctions() { public function getDatasourceFunctions() {
return array( return array(
'null' => array( 'null' => array(
'name' => pht('Find results which are not in any projects.'), 'name' => pht('Not In Any Projects'),
'summary' => pht('Find results which are not in any projects.'),
'description' => pht(
'This function matches results which are not associated with any '.
'projects. It is usually most often used to find objects which '.
'might have slipped through the cracks and not been organized '.
'properly.'.
"\n\n".
"> null()"),
), ),
); );
} }

View file

@ -11,6 +11,8 @@ final class PhabricatorTypeaheadApplication extends PhabricatorApplication {
'/typeahead/' => array( '/typeahead/' => array(
'(?P<action>browse|class)/(?:(?P<class>\w+)/)?' '(?P<action>browse|class)/(?:(?P<class>\w+)/)?'
=> 'PhabricatorTypeaheadModularDatasourceController', => 'PhabricatorTypeaheadModularDatasourceController',
'help/(?P<class>\w+)/'
=> 'PhabricatorTypeaheadFunctionHelpController',
), ),
); );
} }

View file

@ -0,0 +1,150 @@
<?php
final class PhabricatorTypeaheadFunctionHelpController
extends PhabricatorTypeaheadDatasourceController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$class = $request->getURIData('class');
$sources = id(new PhutilSymbolLoader())
->setAncestorClass('PhabricatorTypeaheadDatasource')
->loadObjects();
if (!isset($sources[$class])) {
return new Aphront404Response();
}
$source = $sources[$class];
$application_class = $source->getDatasourceApplicationClass();
if ($application_class) {
$result = id(new PhabricatorApplicationQuery())
->setViewer($this->getViewer())
->withClasses(array($application_class))
->execute();
if (!$result) {
return new Aphront404Response();
}
}
$source->setViewer($viewer);
$title = pht('Typeahead Function Help');
$functions = $source->getAllDatasourceFunctions();
ksort($functions);
$content = array();
$content[] = '= '.pht('Overview');
$content[] = pht(
'Typeahead functions are an advanced feature which allow you to build '.
'more powerful queries. This document explains functions available '.
'for the selected control.'.
"\n\n".
'Note that different controls support //different// functions '.
'(depending on what the control is doing), so these specific functions '.
'may not work everywhere. You can always check the help for a control '.
'to review which functions are available for that control.');
$table = array();
$table_header = array(
pht('Function'),
pht('Token Name'),
pht('Summary'),
);
$table[] = '| '.implode(' | ', $table_header).' |';
$table[] = '|---|---|---|';
foreach ($functions as $function => $spec) {
$spec = $spec + array(
'summary' => null,
'arguments' => null,
);
if (idx($spec, 'arguments')) {
$signature = '**'.$function.'(**//'.$spec['arguments'].'//**)**';
} else {
$signature = '**'.$function.'()**';
}
$name = idx($spec, 'name', '');
$summary = idx($spec, 'summary', '');
$table[] = '| '.$signature.' | '.$name.' | '.$summary.' |';
}
$table = implode("\n", $table);
$content[] = '= '.pht('Function Quick Reference');
$content[] = pht(
'This table briefly describes available functions for this control. '.
'For details on a particular function, see the corresponding section '.
'below.');
$content[] = $table;
$content[] = '= '.pht('Using Typeahead Functions');
$content[] = pht(
'In addition to typing user and project names to build queries, you can '.
'also type the names of special functions which give you more options '.
'and the ability to express more complex queries.'.
"\n\n".
'Functions have an internal name (like `viewer()`) and a '.
'human-readable name, like `Current Viewer`. In general, you can type '.
'either one to select the function. You can also click the '.
'{nav icon=search} button on any typeahead control to browse available '.
'functions and find this documentation.'.
"\n\n".
'This documentation uses the internal names to make it clear where '.
'tokens begin and end. Specifically, you will find queries written '.
'out like this in the documentation: '.
"\n\n".
'> viewer(), alincoln'.
"\n\n".
'When this query is actually shown in the control, it will look more '.
'like this:'.
"\n\n".
'> {nav Current Viewer} {nav alincoln (Abraham Lincoln)}');
$middot = "\xC2\xB7";
foreach ($functions as $function => $spec) {
$arguments = idx($spec, 'arguments', '');
$name = idx($spec, 'name');
$content[] = '= '.$function.'('.$arguments.') '.$middot.' '.$name;
$content[] = $spec['description'];
}
$content = implode("\n\n", $content);
$content_box = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($content),
'default',
$viewer);
$header = id(new PHUIHeaderView())
->setHeader($title);
$document = id(new PHUIDocumentView())
->setHeader($header)
->setFontKit(PHUIDocumentView::FONT_SOURCE_SANS)
->appendChild($content_box);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Function Help'));
return $this->buildApplicationPage(
array(
$crumbs,
$document,
),
array(
'title' => $title,
));
}
}

View file

@ -222,11 +222,32 @@ final class PhabricatorTypeaheadModularDatasourceController
$frame, $frame,
); );
$function_help = null;
if ($source->getAllDatasourceFunctions()) {
$reference_uri = '/typeahead/help/'.get_class($source).'/';
$reference_link = phutil_tag(
'a',
array(
'href' => $reference_uri,
'target' => '_blank',
),
pht('Reference: Advanced Functions'));
$function_help = array(
id(new PHUIIconView())
->setIconFont('fa-book'),
' ',
$reference_link,
);
}
return $this->newDialog() return $this->newDialog()
->setWidth(AphrontDialogView::WIDTH_FORM) ->setWidth(AphrontDialogView::WIDTH_FORM)
->setRenderDialogAsDiv(true) ->setRenderDialogAsDiv(true)
->setTitle($source->getBrowseTitle()) ->setTitle($source->getBrowseTitle())
->appendChild($browser) ->appendChild($browser)
->addFooter($function_help)
->addCancelButton('/', pht('Close')); ->addCancelButton('/', pht('Close'));
} }

View file

@ -117,6 +117,14 @@ abstract class PhabricatorTypeaheadCompositeDatasource
return $this->usable; return $this->usable;
} }
public function getAllDatasourceFunctions() {
$results = parent::getAllDatasourceFunctions();
foreach ($this->getUsableDatasources() as $source) {
$results += $source->getAllDatasourceFunctions();
}
return $results;
}
protected function didEvaluateTokens(array $results) { protected function didEvaluateTokens(array $results) {
foreach ($this->getUsableDatasources() as $source) { foreach ($this->getUsableDatasources() as $source) {
$results = $source->didEvaluateTokens($results); $results = $source->didEvaluateTokens($results);

View file

@ -276,6 +276,14 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject {
} }
/**
* @task functions
*/
public function getAllDatasourceFunctions() {
return $this->getDatasourceFunctions();
}
/** /**
* @task functions * @task functions
*/ */