From f75c13b987c73d0faf3d6cea311200f887795c81 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 2 Oct 2013 13:13:07 -0700 Subject: [PATCH] Use ApplicationSearch in Applications application Summary: Ref T603. OMG SO META Test Plan: See screenshot. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T603 Differential Revision: https://secure.phabricator.com/D7197 --- src/__phutil_library_map__.php | 7 +- .../base/PhabricatorApplication.php | 53 ++++++- .../PhabricatorApplicationApplications.php | 3 +- .../PhabricatorApplicationsController.php | 12 +- .../PhabricatorApplicationsListController.php | 44 +++--- .../meta/query/PhabricatorAppSearchEngine.php | 134 ++++++++++++++++++ .../query/PhabricatorApplicationQuery.php | 84 +++++++++++ .../interface/PhabricatorPolicyInterface.php | 28 ++++ 8 files changed, 329 insertions(+), 36 deletions(-) create mode 100644 src/applications/meta/query/PhabricatorAppSearchEngine.php create mode 100644 src/applications/meta/query/PhabricatorApplicationQuery.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 95be390d6a..99b39faa49 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -807,6 +807,7 @@ phutil_register_library_map(array( 'PhabricatorAnchorView' => 'view/layout/PhabricatorAnchorView.php', 'PhabricatorAphrontBarExample' => 'applications/uiexample/examples/PhabricatorAphrontBarExample.php', 'PhabricatorAphrontViewTestCase' => 'view/__tests__/PhabricatorAphrontViewTestCase.php', + 'PhabricatorAppSearchEngine' => 'applications/meta/query/PhabricatorAppSearchEngine.php', 'PhabricatorApplication' => 'applications/base/PhabricatorApplication.php', 'PhabricatorApplicationApplications' => 'applications/meta/application/PhabricatorApplicationApplications.php', 'PhabricatorApplicationAudit' => 'applications/audit/application/PhabricatorApplicationAudit.php', @@ -849,6 +850,7 @@ phutil_register_library_map(array( 'PhabricatorApplicationPolicy' => 'applications/policy/application/PhabricatorApplicationPolicy.php', 'PhabricatorApplicationPonder' => 'applications/ponder/application/PhabricatorApplicationPonder.php', 'PhabricatorApplicationProject' => 'applications/project/application/PhabricatorApplicationProject.php', + 'PhabricatorApplicationQuery' => 'applications/meta/query/PhabricatorApplicationQuery.php', 'PhabricatorApplicationReleeph' => 'applications/releeph/application/PhabricatorApplicationReleeph.php', 'PhabricatorApplicationReleephConfigOptions' => 'applications/releeph/config/PhabricatorApplicationReleephConfigOptions.php', 'PhabricatorApplicationRepositories' => 'applications/repository/application/PhabricatorApplicationRepositories.php', @@ -2912,6 +2914,8 @@ phutil_register_library_map(array( 'PhabricatorAnchorView' => 'AphrontView', 'PhabricatorAphrontBarExample' => 'PhabricatorUIExample', 'PhabricatorAphrontViewTestCase' => 'PhabricatorTestCase', + 'PhabricatorAppSearchEngine' => 'PhabricatorApplicationSearchEngine', + 'PhabricatorApplication' => 'PhabricatorPolicyInterface', 'PhabricatorApplicationApplications' => 'PhabricatorApplication', 'PhabricatorApplicationAudit' => 'PhabricatorApplication', 'PhabricatorApplicationAuth' => 'PhabricatorApplication', @@ -2953,6 +2957,7 @@ phutil_register_library_map(array( 'PhabricatorApplicationPolicy' => 'PhabricatorApplication', 'PhabricatorApplicationPonder' => 'PhabricatorApplication', 'PhabricatorApplicationProject' => 'PhabricatorApplication', + 'PhabricatorApplicationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorApplicationReleeph' => 'PhabricatorApplication', 'PhabricatorApplicationReleephConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorApplicationRepositories' => 'PhabricatorApplication', @@ -3669,7 +3674,7 @@ phutil_register_library_map(array( 'PhabricatorRefreshCSRFController' => 'PhabricatorAuthController', 'PhabricatorRegistrationProfile' => 'Phobject', 'PhabricatorRemarkupControl' => 'AphrontFormTextAreaControl', - 'PhabricatorRemarkupRuleEmbedFile' => 'PhutilRemarkupRule', + 'PhabricatorRemarkupRuleEmbedFile' => 'PhabricatorRemarkupRuleObject', 'PhabricatorRemarkupRuleImageMacro' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRuleMeme' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRuleMention' => 'PhutilRemarkupRule', diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php index a26e1f40a6..8949893018 100644 --- a/src/applications/base/PhabricatorApplication.php +++ b/src/applications/base/PhabricatorApplication.php @@ -8,7 +8,8 @@ * @task meta Application Management * @group apps */ -abstract class PhabricatorApplication { +abstract class PhabricatorApplication + implements PhabricatorPolicyInterface { const GROUP_CORE = 'core'; const GROUP_COMMUNICATION = 'communication'; @@ -49,7 +50,6 @@ abstract class PhabricatorApplication { /* -( Application Information )-------------------------------------------- */ - public function getName() { return substr(get_class($this), strlen('PhabricatorApplication')); } @@ -82,6 +82,25 @@ abstract class PhabricatorApplication { return false; } + /** + * Returns true if an application is first-party (developed by Phacility) + * and false otherwise. + */ + final public function isFirstParty() { + $where = id(new ReflectionClass($this))->getFileName(); + $root = phutil_get_library_root('phabricator'); + + if (!Filesystem::isDescendant($where, $root)) { + return false; + } + + if (Filesystem::isDescendant($where, $root.'/extensions')) { + return false; + } + + return true; + } + public function canUninstall() { return true; } @@ -297,5 +316,33 @@ abstract class PhabricatorApplication { return $apps; } -} +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + ); + } + + public function getPolicy($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return PhabricatorPolicies::POLICY_USER; + case PhabricatorPolicyCapability::CAN_EDIT: + return PhabricatorPolicies::POLICY_ADMIN; + } + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return false; + } + + public function describeAutomaticCapability($capability) { + return null; + } + + +} diff --git a/src/applications/meta/application/PhabricatorApplicationApplications.php b/src/applications/meta/application/PhabricatorApplicationApplications.php index 0be0576379..314f878d13 100644 --- a/src/applications/meta/application/PhabricatorApplicationApplications.php +++ b/src/applications/meta/application/PhabricatorApplicationApplications.php @@ -29,7 +29,8 @@ final class PhabricatorApplicationApplications extends PhabricatorApplication { public function getRoutes() { return array( '/applications/' => array( - '' => 'PhabricatorApplicationsListController', + '(?:query/(?P[^/]+)/)?' => + 'PhabricatorApplicationsListController', 'view/(?P\w+)/' => 'PhabricatorApplicationDetailViewController', '(?P\w+)/(?Pinstall|uninstall)/' => diff --git a/src/applications/meta/controller/PhabricatorApplicationsController.php b/src/applications/meta/controller/PhabricatorApplicationsController.php index c64700bae8..69f5bd03dd 100644 --- a/src/applications/meta/controller/PhabricatorApplicationsController.php +++ b/src/applications/meta/controller/PhabricatorApplicationsController.php @@ -6,19 +6,23 @@ abstract class PhabricatorApplicationsController extends PhabricatorController { return true; } - public function buildSideNavView($filter = null, $for_app = false) { + public function buildSideNavView($for_app = false) { $user = $this->getRequest()->getUser(); $nav = new AphrontSideNavFilterView(); $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); - $nav->addLabel(pht('Installed Applications')); - $nav->addFilter('/', pht('Applications')); + + id(new PhabricatorAppSearchEngine()) + ->setViewer($user) + ->addNavigationItems($nav->getMenu()); + + $nav->selectFilter(null); return $nav; } public function buildApplicationMenu() { - return $this->buildSideNavView(null, true)->getMenu(); + return $this->buildSideNavView(true)->getMenu(); } } diff --git a/src/applications/meta/controller/PhabricatorApplicationsListController.php b/src/applications/meta/controller/PhabricatorApplicationsListController.php index 55d22f6431..a458e866a9 100644 --- a/src/applications/meta/controller/PhabricatorApplicationsListController.php +++ b/src/applications/meta/controller/PhabricatorApplicationsListController.php @@ -1,40 +1,30 @@ queryKey = idx($data, 'queryKey'); + } public function processRequest() { $request = $this->getRequest(); - $user = $request->getUser(); + $controller = id(new PhabricatorApplicationSearchController($request)) + ->setQueryKey($this->queryKey) + ->setSearchEngine(new PhabricatorAppSearchEngine()) + ->setNavigation($this->buildSideNavView()); - $nav = $this->buildSideNavView(); - $nav->selectFilter('/'); - - $applications = PhabricatorApplication::getAllApplications(); - - $list = $this->buildInstalledApplicationsList($applications); - $title = pht('Installed Applications'); - $nav->appendChild($list); - - $crumbs = $this - ->buildApplicationCrumbs() - ->addCrumb( - id(new PhabricatorCrumbView()) - ->setName(pht('Applications')) - ->setHref($this->getApplicationURI())); - - $nav->setCrumbs($crumbs); - - return $this->buildApplicationPage( - $nav, - array( - 'title' => $title, - 'device' => true, - )); + return $this->delegateToController($controller); } + public function renderResultsList( + array $applications, + PhabricatorSavedQuery $query) { + assert_instances_of($applications, 'PhabricatorApplication'); - private function buildInstalledApplicationsList(array $applications) { $list = new PHUIObjectItemListView(); $applications = msort($applications, 'getName'); diff --git a/src/applications/meta/query/PhabricatorAppSearchEngine.php b/src/applications/meta/query/PhabricatorAppSearchEngine.php new file mode 100644 index 0000000000..edafda93e2 --- /dev/null +++ b/src/applications/meta/query/PhabricatorAppSearchEngine.php @@ -0,0 +1,134 @@ +setParameter('name', $request->getStr('name')); + + $saved->setParameter('installed', $this->readBool($request, 'installed')); + $saved->setParameter('beta', $this->readBool($request, 'beta')); + $saved->setParameter('firstParty', $this->readBool($request, 'firstParty')); + + return $saved; + } + + public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { + $query = id(new PhabricatorApplicationQuery()); + + $name = $saved->getParameter('name'); + if (strlen($name)) { + $query->withNameContains($name); + } + + $installed = $saved->getParameter('installed'); + if ($installed !== null) { + $query->withInstalled($installed); + } + + $beta = $saved->getParameter('beta'); + if ($beta !== null) { + $query->withBeta($beta); + } + + $first_party = $saved->getParameter('firstParty'); + if ($first_party !== null) { + $query->withFirstParty($first_party); + } + + return $query; + } + + public function buildSearchForm( + AphrontFormView $form, + PhabricatorSavedQuery $saved) { + + $form + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel(pht('Name Contains')) + ->setName('name') + ->setValue($saved->getParameter('name'))) + ->appendChild( + id(new AphrontFormSelectControl()) + ->setLabel(pht('Installed')) + ->setName('installed') + ->setValue($this->getBool($saved, 'installed')) + ->setOptions( + array( + '' => pht('Show All Applications'), + 'true' => pht('Show Installed Applications'), + 'false' => pht('Show Uninstalled Applications'), + ))) + ->appendChild( + id(new AphrontFormSelectControl()) + ->setLabel(pht('Beta')) + ->setName('beta') + ->setValue($this->getBool($saved, 'beta')) + ->setOptions( + array( + '' => pht('Show All Applications'), + 'true' => pht('Show Beta Applications'), + 'false' => pht('Show Released Applications'), + ))) + ->appendChild( + id(new AphrontFormSelectControl()) + ->setLabel(pht('Provenance')) + ->setName('firstParty') + ->setValue($this->getBool($saved, 'firstParty')) + ->setOptions( + array( + '' => pht('Show All Applications'), + 'true' => pht('Show First-Party Applications'), + 'false' => pht('Show Third-Party Applications'), + ))); + + } + + protected function getURI($path) { + return '/applications/'.$path; + } + + public function getBuiltinQueryNames() { + $names = array( + 'all' => pht('All Applications'), + ); + + return $names; + } + + public function buildSavedQueryFromBuiltin($query_key) { + + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'all': + return $query; + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + + private function readBool(AphrontRequest $request, $key) { + if (!strlen($request->getStr($key))) { + return null; + } + return $request->getBool($key); + } + + private function getBool(PhabricatorSavedQuery $query, $key) { + $value = $query->getParameter($key); + if ($value === null) { + return $value; + } + return $value ? 'true' : 'false'; + } + +} diff --git a/src/applications/meta/query/PhabricatorApplicationQuery.php b/src/applications/meta/query/PhabricatorApplicationQuery.php new file mode 100644 index 0000000000..bfa0361325 --- /dev/null +++ b/src/applications/meta/query/PhabricatorApplicationQuery.php @@ -0,0 +1,84 @@ +nameContains = $name_contains; + return $this; + } + + public function withInstalled($installed) { + $this->installed = $installed; + return $this; + } + + public function withBeta($beta) { + $this->beta = $beta; + return $this; + } + + public function withFirstParty($first_party) { + $this->firstParty = $first_party; + return $this; + } + + public function withClasses(array $classes) { + $this->classes = $classes; + return $this; + } + + public function loadPage() { + $apps = PhabricatorApplication::getAllApplications(); + + if ($this->classes) { + $classes = array_fuse($this->classes); + foreach ($apps as $key => $app) { + if (empty($classes[get_class($app)])) { + unset($apps[$key]); + } + } + } + + if (strlen($this->nameContains)) { + foreach ($apps as $key => $app) { + if (stripos($app->getName(), $this->nameContains) === false) { + unset($apps[$key]); + } + } + } + + if ($this->installed !== null) { + foreach ($apps as $key => $app) { + if ($app->isInstalled() != $this->installed) { + unset($apps[$key]); + } + } + } + + if ($this->beta !== null) { + foreach ($apps as $key => $app) { + if ($app->isBeta() != $this->beta) { + unset($apps[$key]); + } + } + } + + if ($this->firstParty !== null) { + foreach ($apps as $key => $app) { + if ($app->isFirstParty() != $this->firstParty) { + unset($apps[$key]); + } + } + } + + return msort($apps, 'getName'); + } + +} diff --git a/src/applications/policy/interface/PhabricatorPolicyInterface.php b/src/applications/policy/interface/PhabricatorPolicyInterface.php index fb64180815..73905c7f63 100644 --- a/src/applications/policy/interface/PhabricatorPolicyInterface.php +++ b/src/applications/policy/interface/PhabricatorPolicyInterface.php @@ -35,3 +35,31 @@ interface PhabricatorPolicyInterface { public function describeAutomaticCapability($capability); } + +// TEMPLATE IMPLEMENTATION ///////////////////////////////////////////////////// + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ +/* + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + ); + } + + public function getPolicy($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return PhabricatorPolicies::POLICY_USER; + } + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return false; + } + + public function describeAutomaticCapability($capability) { + return null; + } + +*/