From f82e4b0c70a0ecf0bfb69b0ef2c5edfab6d01a5a Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 1 Jul 2013 12:36:34 -0700 Subject: [PATCH] Modernize most Conduit console interfaces Summary: Ref T603. Ref T2625. Long chain of "doing the right thing" here: I want to clean this up, so I can clean up the Conduit logs, so I can add a setup issue for deprecated method calls, so I can remove deprecated methods, so I can get rid of `DifferentialRevisionListData`, so I can make Differntial policy-aware. Adds modern infrastructure and UI to all of the Conduit interfaces (except only partially for the logs, that will be the next diff). Test Plan: {F48201} {F48202} {F48203} {F48204} {F48206} This will get further updates in the next diff: {F48205} Reviewers: btrahan, chad Reviewed By: chad CC: aran Maniphest Tasks: T603, T2625 Differential Revision: https://secure.phabricator.com/D6331 --- src/__phutil_library_map__.php | 15 +- .../PhabricatorApplicationConduit.php | 2 +- .../PhabricatorConduitAPIController.php | 31 +++- .../PhabricatorConduitConsoleController.php | 53 ++++--- .../PhabricatorConduitController.php | 121 ++-------------- .../PhabricatorConduitListController.php | 130 ++++++++--------- .../PhabricatorConduitLogController.php | 12 +- .../PhabricatorConduitTokenController.php | 46 ++++-- .../conduit/method/ConduitAPIMethod.php | 56 ++++++- .../query/PhabricatorConduitMethodQuery.php | 124 ++++++++++++++++ .../query/PhabricatorConduitSearchEngine.php | 137 ++++++++++++++++++ ...PhabricatorApplicationSearchController.php | 3 +- .../PhabricatorApplicationSearchEngine.php | 10 ++ 13 files changed, 513 insertions(+), 227 deletions(-) create mode 100644 src/applications/conduit/query/PhabricatorConduitMethodQuery.php create mode 100644 src/applications/conduit/query/PhabricatorConduitSearchEngine.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 9628a57784..0f60bc2ddd 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -921,6 +921,8 @@ phutil_register_library_map(array( 'PhabricatorConduitListController' => 'applications/conduit/controller/PhabricatorConduitListController.php', 'PhabricatorConduitLogController' => 'applications/conduit/controller/PhabricatorConduitLogController.php', 'PhabricatorConduitMethodCallLog' => 'applications/conduit/storage/PhabricatorConduitMethodCallLog.php', + 'PhabricatorConduitMethodQuery' => 'applications/conduit/query/PhabricatorConduitMethodQuery.php', + 'PhabricatorConduitSearchEngine' => 'applications/conduit/query/PhabricatorConduitSearchEngine.php', 'PhabricatorConduitTokenController' => 'applications/conduit/controller/PhabricatorConduitTokenController.php', 'PhabricatorConfigAllController' => 'applications/config/controller/PhabricatorConfigAllController.php', 'PhabricatorConfigController' => 'applications/config/controller/PhabricatorConfigController.php', @@ -2009,6 +2011,11 @@ phutil_register_library_map(array( 'CelerityResourceController' => 'PhabricatorController', 'CelerityResourceGraph' => 'AbstractDirectedGraph', 'CelerityResourceTransformerTestCase' => 'PhabricatorTestCase', + 'ConduitAPIMethod' => + array( + 0 => 'Phobject', + 1 => 'PhabricatorPolicyInterface', + ), 'ConduitAPI_arcanist_Method' => 'ConduitAPIMethod', 'ConduitAPI_arcanist_projectinfo_Method' => 'ConduitAPI_arcanist_Method', 'ConduitAPI_audit_Method' => 'ConduitAPIMethod', @@ -2807,9 +2814,15 @@ phutil_register_library_map(array( 'PhabricatorConduitConsoleController' => 'PhabricatorConduitController', 'PhabricatorConduitController' => 'PhabricatorController', 'PhabricatorConduitDAO' => 'PhabricatorLiskDAO', - 'PhabricatorConduitListController' => 'PhabricatorConduitController', + 'PhabricatorConduitListController' => + array( + 0 => 'PhabricatorConduitController', + 1 => 'PhabricatorApplicationSearchResultsControllerInterface', + ), 'PhabricatorConduitLogController' => 'PhabricatorConduitController', 'PhabricatorConduitMethodCallLog' => 'PhabricatorConduitDAO', + 'PhabricatorConduitMethodQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorConduitSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorConduitTokenController' => 'PhabricatorConduitController', 'PhabricatorConfigAllController' => 'PhabricatorConfigController', 'PhabricatorConfigController' => 'PhabricatorController', diff --git a/src/applications/conduit/application/PhabricatorApplicationConduit.php b/src/applications/conduit/application/PhabricatorApplicationConduit.php index 0c32612433..c4d595625c 100644 --- a/src/applications/conduit/application/PhabricatorApplicationConduit.php +++ b/src/applications/conduit/application/PhabricatorApplicationConduit.php @@ -38,7 +38,7 @@ final class PhabricatorApplicationConduit extends PhabricatorApplication { public function getRoutes() { return array( '/conduit/' => array( - '' => 'PhabricatorConduitListController', + '(?:query/(?P[^/]+)/)?' => 'PhabricatorConduitListController', 'method/(?P[^/]+)/' => 'PhabricatorConduitConsoleController', 'log/' => 'PhabricatorConduitLogController', 'log/view/(?P[^/]+)/' => 'PhabricatorConduitLogController', diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php index 290a356627..afa1823dc4 100644 --- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php +++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php @@ -354,6 +354,7 @@ final class PhabricatorConduitAPIController } $param_table = new AphrontTableView($param_rows); + $param_table->setDeviceReadyTable(true); $param_table->setColumnClasses( array( 'header', @@ -369,6 +370,7 @@ final class PhabricatorConduitAPIController } $result_table = new AphrontTableView($result_rows); + $result_table->setDeviceReadyTable(true); $result_table->setColumnClasses( array( 'header', @@ -383,13 +385,36 @@ final class PhabricatorConduitAPIController $result_panel->setHeader('Method Result'); $result_panel->appendChild($result_table); - return $this->buildStandardPageResponse( + $param_head = id(new PhabricatorHeaderView()) + ->setHeader(pht('Method Parameters')); + + $result_head = id(new PhabricatorHeaderView()) + ->setHeader(pht('Method Result')); + + $method_uri = $this->getApplicationURI('method/'.$method.'/'); + + $crumbs = $this->buildApplicationCrumbs(); + $crumbs + ->addCrumb( + id(new PhabricatorCrumbView()) + ->setName($method) + ->setHref($method_uri)) + ->addCrumb( + id(new PhabricatorCrumbView()) + ->setName(pht('Call'))); + + return $this->buildApplicationPage( array( - $param_panel, - $result_panel, + $crumbs, + $param_head, + $param_table, + $result_head, + $result_table, ), array( 'title' => 'Method Call Result', + 'device' => true, + 'dust' => true, )); } diff --git a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php index 68f4fb4935..7d2ac5b3a3 100644 --- a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php +++ b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php @@ -15,18 +15,19 @@ final class PhabricatorConduitConsoleController public function processRequest() { $request = $this->getRequest(); + $viewer = $request->getUser(); - $methods = $this->getAllMethods(); - if (empty($methods[$this->method])) { + $method = id(new PhabricatorConduitMethodQuery()) + ->setViewer($viewer) + ->withMethods(array($this->method)) + ->executeOne(); + + if (!$method) { return new Aphront404Response(); } - $this->setFilter('method/'.$this->method); - $method_class = $methods[$this->method]; - $method_object = newv($method_class, array()); - - $status = $method_object->getMethodStatus(); - $reason = $method_object->getMethodStatusDescription(); + $status = $method->getMethodStatus(); + $reason = $method->getMethodStatusDescription(); $status_view = null; if ($status != ConduitAPIMethod::METHOD_STATUS_STABLE) { @@ -49,7 +50,7 @@ final class PhabricatorConduitConsoleController } } - $error_types = $method_object->defineErrorTypes(); + $error_types = $method->defineErrorTypes(); if ($error_types) { $error_description = array(); foreach ($error_types as $error => $meaning) { @@ -68,14 +69,15 @@ final class PhabricatorConduitConsoleController ->setUser($request->getUser()) ->setAction('/api/'.$this->method) ->addHiddenInput('allowEmptyParams', 1) + ->setFlexible(true) ->appendChild( id(new AphrontFormStaticControl()) ->setLabel('Description') - ->setValue($method_object->getMethodDescription())) + ->setValue($method->getMethodDescription())) ->appendChild( id(new AphrontFormStaticControl()) ->setLabel('Returns') - ->setValue($method_object->defineReturnType())) + ->setValue($method->defineReturnType())) ->appendChild( id(new AphrontFormMarkupControl()) ->setLabel('Errors') @@ -85,7 +87,7 @@ final class PhabricatorConduitConsoleController 'JSON. For instance, to enter a list, type: '. '["apple", "banana", "cherry"]')); - $params = $method_object->defineParamTypes(); + $params = $method->defineParamTypes(); foreach ($params as $param => $desc) { $form->appendChild( id(new AphrontFormTextControl()) @@ -106,30 +108,25 @@ final class PhabricatorConduitConsoleController ))) ->appendChild( id(new AphrontFormSubmitControl()) + ->addCancelButton($this->getApplicationURI()) ->setValue('Call Method')); - $panel = new AphrontPanelView(); - $panel->setHeader('Conduit API: '.$this->method); - $panel->appendChild($form); - $panel->setWidth(AphrontPanelView::WIDTH_FULL); + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addCrumb( + id(new PhabricatorCrumbView()) + ->setName($method->getAPIMethodName())); - return $this->buildStandardPageResponse( + return $this->buildApplicationPage( array( + $crumbs, $status_view, - $panel, + $form, ), array( - 'title' => 'Conduit Console - '.$this->method, + 'title' => $method->getAPIMethodName(), + 'device' => true, + 'dust' => true, )); } - private function getAllMethods() { - $classes = $this->getAllMethodImplementationClasses(); - $methods = array(); - foreach ($classes as $class) { - $name = ConduitAPIMethod::getAPIMethodNameFromClassName($class); - $methods[$name] = $class; - } - return $methods; - } } diff --git a/src/applications/conduit/controller/PhabricatorConduitController.php b/src/applications/conduit/controller/PhabricatorConduitController.php index a4d61e05bb..91cf834b55 100644 --- a/src/applications/conduit/controller/PhabricatorConduitController.php +++ b/src/applications/conduit/controller/PhabricatorConduitController.php @@ -5,122 +5,27 @@ */ abstract class PhabricatorConduitController extends PhabricatorController { - private $filter; - protected $showSideNav; + protected function buildSideNavView() { + $viewer = $this->getRequest()->getUser(); - public function buildStandardPageResponse($view, array $data) { - $page = $this->buildStandardPageView(); + $nav = new AphrontSideNavFilterView(); + $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); - $page->setApplicationName('Conduit'); - $page->setBaseURI('/conduit/'); - $page->setTitle(idx($data, 'title')); - $page->setGlyph("\xE2\x87\xB5"); + id(new PhabricatorConduitSearchEngine()) + ->setViewer($viewer) + ->addNavigationItems($nav->getMenu()); - if ($this->showSideNav()) { + $nav->addLabel('Logs'); + $nav->addFilter('log', pht('Call Logs')); - $nav = new AphrontSideNavFilterView(); - $nav->setBaseURI(new PhutilURI('/conduit/')); - $method_filters = $this->getMethodFilters(); - foreach ($method_filters as $group => $methods) { - $nav->addLabel($group); - foreach ($methods as $method) { - $method_name = $method['full_name']; + $nav->selectFilter(null); - $display_name = $method_name; - switch ($method['status']) { - case ConduitAPIMethod::METHOD_STATUS_DEPRECATED: - $display_name = '('.$display_name.')'; - break; - } - - $nav->addFilter('method/'.$method_name, - $display_name); - } - } - $nav->selectFilter($this->getFilter()); - $nav->appendChild($view); - $body = $nav; - } else { - $body = $view; - } - $page->appendChild($body); - - $response = new AphrontWebpageResponse(); - return $response->setContent($page->render()); + return $nav; } - private function getFilter() { - return $this->filter; + protected function buildApplicationMenu() { + return $this->buildSideNavView()->getMenu(); } - protected function setFilter($filter) { - $this->filter = $filter; - return $this; - } - - private function showSideNav() { - return $this->showSideNav !== false; - } - - protected function setShowSideNav($show_side_nav) { - $this->showSideNav = $show_side_nav; - return $this; - } - - protected function getAllMethodImplementationClasses() { - $classes = id(new PhutilSymbolLoader()) - ->setAncestorClass('ConduitAPIMethod') - ->setType('class') - ->setConcreteOnly(true) - ->selectSymbolsWithoutLoading(); - - return array_values(ipull($classes, 'name')); - } - - protected function getMethodFilters() { - $classes = $this->getAllMethodImplementationClasses(); - $method_names = array(); - foreach ($classes as $method_class) { - $method_name = ConduitAPIMethod::getAPIMethodNameFromClassName( - $method_class); - $group_name = head(explode('.', $method_name)); - - $method_object = newv($method_class, array()); - - $application = $method_object->getApplication(); - if ($application && !$application->isInstalled()) { - continue; - } - - $status = $method_object->getMethodStatus(); - - $key = sprintf( - '%02d %s %s', - $this->getOrderForMethodStatus($status), - $group_name, - $method_name); - - $method_names[$key] = array( - 'full_name' => $method_name, - 'group_name' => $group_name, - 'status' => $status, - 'description' => $method_object->getMethodDescription(), - ); - } - ksort($method_names); - $method_names = igroup($method_names, 'group_name'); - ksort($method_names); - - return $method_names; - } - - private function getOrderForMethodStatus($status) { - $map = array( - ConduitAPIMethod::METHOD_STATUS_STABLE => 0, - ConduitAPIMethod::METHOD_STATUS_UNSTABLE => 1, - ConduitAPIMethod::METHOD_STATUS_DEPRECATED => 2, - ); - return idx($map, $status, 0); - } } diff --git a/src/applications/conduit/controller/PhabricatorConduitListController.php b/src/applications/conduit/controller/PhabricatorConduitListController.php index e80a6fcba6..b94b36a490 100644 --- a/src/applications/conduit/controller/PhabricatorConduitListController.php +++ b/src/applications/conduit/controller/PhabricatorConduitListController.php @@ -4,78 +4,78 @@ * @group conduit */ final class PhabricatorConduitListController - extends PhabricatorConduitController { + extends PhabricatorConduitController + implements PhabricatorApplicationSearchResultsControllerInterface { + + private $queryKey; + + public function willProcessRequest(array $data) { + $this->queryKey = idx($data, 'queryKey'); + } public function processRequest() { - $method_groups = $this->getMethodFilters(); - $rows = array(); - foreach ($method_groups as $group => $methods) { - foreach ($methods as $info) { - switch ($info['status']) { - case ConduitAPIMethod::METHOD_STATUS_DEPRECATED: - $status = 'Deprecated'; - break; - case ConduitAPIMethod::METHOD_STATUS_UNSTABLE: - $status = 'Unstable'; - break; - default: - $status = null; - break; - } + $request = $this->getRequest(); + $controller = id(new PhabricatorApplicationSearchController($request)) + ->setQueryKey($this->queryKey) + ->setSearchEngine(new PhabricatorConduitSearchEngine()) + ->setNavigation($this->buildSideNavView()); + return $this->delegateToController($controller); + } - $rows[] = array( - $group, - phutil_tag( - 'a', - array( - 'href' => '/conduit/method/'.$info['full_name'], - ), - $info['full_name']), - $info['description'], - $status, - ); - $group = null; + public function renderResultsList(array $methods) { + assert_instances_of($methods, 'ConduitAPIMethod'); + + $viewer = $this->getRequest()->getUser(); + + $out = array(); + + $last = null; + $list = null; + foreach ($methods as $method) { + $app = $method->getApplicationName(); + if ($app !== $last) { + $last = $app; + if ($list) { + $out[] = $list; + } + $list = id(new PhabricatorObjectItemListView()); + + $app_object = $method->getApplication(); + if ($app_object) { + $app_name = $app_object->getName(); + } else { + $app_name = $app; + } } + + $method_name = $method->getAPIMethodName(); + + $item = id(new PhabricatorObjectItemView()) + ->setHeader($method_name) + ->setHref($this->getApplicationURI('method/'.$method_name.'/')) + ->addAttribute($method->getMethodDescription()); + + switch ($method->getMethodStatus()) { + case ConduitAPIMethod::METHOD_STATUS_STABLE: + break; + case ConduitAPIMethod::METHOD_STATUS_UNSTABLE: + $item->addIcon('warning-grey', pht('Unstable')); + $item->setBarColor('yellow'); + break; + case ConduitAPIMethod::METHOD_STATUS_DEPRECATED: + $item->addIcon('warning', pht('Deprecated')); + $item->setBarColor('red'); + break; + } + + $list->addItem($item); } - $table = new AphrontTableView($rows); - $table->setHeaders(array( - 'Group', - 'Name', - 'Description', - 'Status', - )); - $table->setColumnClasses(array( - 'pri', - 'pri', - 'wide', - null, - )); + if ($list) { + $out[] = $list; + } - $panel = new AphrontPanelView(); - $panel->setHeader('Conduit Methods'); - $panel->appendChild($table); - $panel->setWidth(AphrontPanelView::WIDTH_FULL); - - $utils = new AphrontPanelView(); - $utils->setHeader('Utilities'); - $utils->appendChild(hsprintf( - '
    '. - '
  • Log - Conduit Method Calls
  • '. - '
  • Token - Certificate Install
  • '. - '
')); - $utils->setWidth(AphrontPanelView::WIDTH_FULL); - - $this->setShowSideNav(false); - - return $this->buildStandardPageResponse( - array( - $panel, - $utils, - ), - array( - 'title' => 'Conduit Console', - )); + return $out; } } diff --git a/src/applications/conduit/controller/PhabricatorConduitLogController.php b/src/applications/conduit/controller/PhabricatorConduitLogController.php index c752714dda..dd6b67db87 100644 --- a/src/applications/conduit/controller/PhabricatorConduitLogController.php +++ b/src/applications/conduit/controller/PhabricatorConduitLogController.php @@ -41,10 +41,16 @@ final class PhabricatorConduitLogController $panel->appendChild($table); $panel->appendChild($pager); - $this->setShowSideNav(false); + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addCrumb( + id(new PhabricatorCrumbView()) + ->setName(pht('Call Logs'))); - return $this->buildStandardPageResponse( - $panel, + return $this->buildApplicationPage( + array( + $crumbs, + $panel, + ), array( 'title' => 'Conduit Logs', )); diff --git a/src/applications/conduit/controller/PhabricatorConduitTokenController.php b/src/applications/conduit/controller/PhabricatorConduitTokenController.php index 50f87cbd6d..fb9887ceee 100644 --- a/src/applications/conduit/controller/PhabricatorConduitTokenController.php +++ b/src/applications/conduit/controller/PhabricatorConduitTokenController.php @@ -28,26 +28,40 @@ final class PhabricatorConduitTokenController ->setToken(Filesystem::readRandomCharacters(40)) ->save(); - $panel = new AphrontPanelView(); - $panel->setHeader('Certificate Install Token'); - $panel->setWidth(AphrontPanelView::WIDTH_FORM); + unset($unguarded); - $panel->appendChild(hsprintf( - '

Copy and paste this token into '. - 'the prompt given to you by "arc install-certificate":

'. - '

'. - '%s'. - '

'. - '

arc will then complete the '. - 'install process for you.

', - $token->getToken())); + $pre_instructions = pht( + 'Copy and paste this token into the prompt given to you by '. + '`arc install-certificate`'); - $this->setShowSideNav(false); + $post_instructions = pht( + 'After you copy and paste this token, `arc` will complete '. + 'the certificate install process for you.'); - return $this->buildStandardPageResponse( - $panel, + $form = id(new AphrontFormView()) + ->setUser($user) + ->appendRemarkupInstructions($pre_instructions) + ->appendChild( + id(new AphrontFormTextAreaControl()) + ->setLabel(pht('Token')) + ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT) + ->setValue($token->getToken())) + ->appendRemarkupInstructions($post_instructions); + + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addCrumb( + id(new PhabricatorCrumbView()) + ->setName(pht('Install Certificate'))); + + return $this->buildApplicationPage( array( - 'title' => 'Certificate Install Token', + $crumbs, + $form, + ), + array( + 'title' => pht('Certificate Install Token'), + 'device' => true, + 'dust' => true, )); } } diff --git a/src/applications/conduit/method/ConduitAPIMethod.php b/src/applications/conduit/method/ConduitAPIMethod.php index 452717d34d..da5c6d7b01 100644 --- a/src/applications/conduit/method/ConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitAPIMethod.php @@ -5,7 +5,9 @@ * @task status Method Status * @group conduit */ -abstract class ConduitAPIMethod { +abstract class ConduitAPIMethod + extends Phobject + implements PhabricatorPolicyInterface { const METHOD_STATUS_STABLE = 'stable'; const METHOD_STATUS_UNSTABLE = 'unstable'; @@ -21,6 +23,14 @@ abstract class ConduitAPIMethod { } + /** + * This is mostly for compatibility with + * @{class:AphrontCursorPagedPolicyAwareQuery}. + */ + public function getID() { + return $this->getAPIMethodName(); + } + /** * Get the status for this method (e.g., stable, unstable or deprecated). * Should return a METHOD_STATUS_* constant. By default, methods are @@ -62,6 +72,29 @@ abstract class ConduitAPIMethod { return self::getAPIMethodNameFromClassName(get_class($this)); } + /** + * Return a key which sorts methods by application name, then method status, + * then method name. + */ + public function getSortOrder() { + $name = $this->getAPIMethodName(); + + $map = array( + ConduitAPIMethod::METHOD_STATUS_STABLE => 0, + ConduitAPIMethod::METHOD_STATUS_UNSTABLE => 1, + ConduitAPIMethod::METHOD_STATUS_DEPRECATED => 2, + ); + $ord = idx($map, $this->getMethodStatus(), 0); + + list($head, $tail) = explode('.', $name, 2); + + return "{$head}.{$ord}.{$tail}"; + } + + public function getApplicationName() { + return head(explode('.', $this->getAPIMethodName(), 2)); + } + public static function getClassNameFromAPIMethodName($method_name) { $method_fragment = str_replace('.', '_', $method_name); return 'ConduitAPI_'.$method_fragment.'_Method'; @@ -129,4 +162,25 @@ abstract class ConduitAPIMethod { } } + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + ); + } + + public function getPolicy($capability) { + return PhabricatorPolicies::POLICY_USER; + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + // The policy interface on Conduit calls is currently just to let us hook + // into ApplicationSearch. Calls are always visible (even to logged out + // users). + return true; + } + } diff --git a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php new file mode 100644 index 0000000000..02236d85b3 --- /dev/null +++ b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php @@ -0,0 +1,124 @@ +methods = $methods; + return $this; + } + + public function withNameContains($name_contains) { + $this->nameContains = $name_contains; + return $this; + } + + public function withApplicationNames(array $application_names) { + $this->applicationNames = $application_names; + return $this; + } + + public function withIsStable($is_stable) { + $this->isStable = $is_stable; + return $this; + } + + public function withIsUnstable($is_unstable) { + $this->isUnstable = $is_unstable; + return $this; + } + + public function withIsDeprecated($is_deprecated) { + $this->isDeprecated = $is_deprecated; + return $this; + } + + public function loadPage() { + $methods = $this->getAllMethods(); + + $methods = $this->filterMethods($methods); + + return $methods; + } + + private function getAllMethods() { + static $methods; + if ($methods === null) { + $methods = id(new PhutilSymbolLoader()) + ->setAncestorClass('ConduitAPIMethod') + ->loadObjects(); + $methods = msort($methods, 'getSortOrder'); + } + return $methods; + } + + private function filterMethods(array $methods) { + foreach ($methods as $key => $method) { + $application = $method->getApplication(); + if (!$application) { + continue; + } + if (!$application->isInstalled()) { + unset($methods[$key]); + } + } + + $status = array( + ConduitAPIMethod::METHOD_STATUS_STABLE => $this->isStable, + ConduitAPIMethod::METHOD_STATUS_DEPRECATED => $this->isDeprecated, + ConduitAPIMethod::METHOD_STATUS_UNSTABLE => $this->isUnstable, + ); + + // Only apply status filters if any of them are set. + if (array_filter($status)) { + foreach ($methods as $key => $method) { + $keep = idx($status, $method->getMethodStatus()); + if (!$keep) { + unset($methods[$key]); + } + } + } + + if ($this->applicationNames) { + $map = array_fuse($this->applicationNames); + foreach ($methods as $key => $method) { + $needle = $method->getApplicationName(); + $needle = phutil_utf8_strtolower($needle); + if (empty($map[$needle])) { + unset($methods[$key]); + } + } + } + + if ($this->nameContains) { + $needle = phutil_utf8_strtolower($this->nameContains); + foreach ($methods as $key => $method) { + $haystack = $method->getAPIMethodName(); + $haystack = phutil_utf8_strtolower($haystack); + if (strpos($haystack, $needle) === false) { + unset($methods[$key]); + } + } + } + + if ($this->methods) { + $map = array_fuse($this->methods); + foreach ($methods as $key => $method) { + $needle = $method->getAPIMethodName(); + if (empty($map[$needle])) { + unset($methods[$key]); + } + } + } + + return $methods; + } + +} diff --git a/src/applications/conduit/query/PhabricatorConduitSearchEngine.php b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php new file mode 100644 index 0000000000..33b61ba007 --- /dev/null +++ b/src/applications/conduit/query/PhabricatorConduitSearchEngine.php @@ -0,0 +1,137 @@ +setParameter('isStable', $request->getStr('isStable')); + $saved->setParameter('isUnstable', $request->getStr('isUnstable')); + $saved->setParameter('isDeprecated', $request->getStr('isDeprecated')); + + $saved->setParameter( + 'applicationNames', + $request->getStrList('applicationNames')); + + $saved->setParameter('nameContains', $request->getStr('nameContains')); + + return $saved; + } + + public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { + $query = id(new PhabricatorConduitMethodQuery()); + + $query->withIsStable($saved->getParameter('isStable')); + $query->withIsUnstable($saved->getParameter('isUnstable')); + $query->withIsDeprecated($saved->getParameter('isDeprecated')); + + $names = $saved->getParameter('applicationNames', array()); + if ($names) { + $query->withApplicationNames($names); + } + + $contains = $saved->getParameter('nameContains'); + if (strlen($contains)) { + $query->withNameContains($contains); + } + + return $query; + } + + public function buildSearchForm( + AphrontFormView $form, + PhabricatorSavedQuery $saved) { + + $form + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel('Name Contains') + ->setName('nameContains') + ->setValue($saved->getParameter('nameContains'))); + + $names = $saved->getParameter('applicationNames', array()); + $form + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel('Applications') + ->setName('applicationNames') + ->setValue(implode(', ', $names)) + ->setCaption( + pht('Example: %s', hsprintf('differential, paste')))); + + $is_stable = $saved->getParameter('isStable'); + $is_unstable = $saved->getParameter('isUnstable'); + $is_deprecated = $saved->getParameter('isDeprecated'); + $form + ->appendChild( + id(new AphrontFormCheckboxControl()) + ->setLabel('Stability') + ->addCheckbox( + 'isStable', + 1, + hsprintf( + '%s: %s', + pht('Stable Methods'), + pht('Show established API methods with stable interfaces.')), + $is_stable) + ->addCheckbox( + 'isUnstable', + 1, + hsprintf( + '%s: %s', + pht('Unstable Methods'), + pht('Show new methods which are subject to change.')), + $is_unstable) + ->addCheckbox( + 'isDeprecated', + 1, + hsprintf( + '%s: %s', + pht('Deprecated Methods'), + pht( + 'Show old methods which will be deleted in a future '. + 'version of Phabricator.')), + $is_deprecated)); + + + } + + protected function getURI($path) { + return '/conduit/'.$path; + } + + public function getBuiltinQueryNames() { + $names = array( + 'modern' => pht('Modern Methods'), + 'all' => pht('All Methods'), + ); + + return $names; + } + + public function buildSavedQueryFromBuiltin($query_key) { + + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'modern': + return $query + ->setParameter('isStable', true) + ->setParameter('isUnstable', true); + case 'all': + return $query + ->setParameter('isStable', true) + ->setParameter('isUnstable', true) + ->setParameter('isDeprecated', true); + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + +} diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php index 40ad447857..c554ae5536 100644 --- a/src/applications/search/controller/PhabricatorApplicationSearchController.php +++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php @@ -173,16 +173,17 @@ final class PhabricatorApplicationSearchController $pager = new AphrontCursorPagerView(); $pager->readFromRequest($request); + $pager->setPageSize($engine->getPageSize($saved_query)); $objects = $query->setViewer($request->getUser()) ->executeWithCursorPager($pager); $list = $parent->renderResultsList($objects); - $list->setNoDataString(pht("No results found for this query.")); $nav->appendChild($list); // TODO: This is a bit hacky. if ($list instanceof PhabricatorObjectItemListView) { + $list->setNoDataString(pht("No results found for this query.")); $list->setPager($pager); } else { $nav->appendChild($pager); diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php index 6cd75dc419..e02eaa7470 100644 --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -305,4 +305,14 @@ abstract class PhabricatorApplicationSearchEngine { ->setLabel($end_name) ->setValue($end_str)); } + + +/* -( Pagination )--------------------------------------------------------- */ + + + public function getPageSize(PhabricatorSavedQuery $saved) { + return $saved->getParameter('limit', 100); + } + + }