From be2b8f4bcb62deb953049debacfbc7dca2e6edef Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 31 Oct 2019 12:43:40 -0700 Subject: [PATCH] Support querying projects by "Root Projects" in the UI, and "min/max depth" in the API Summary: Fixes T13441. Internally, projects can be queried by depth, but this is not exposed in the UI. Add a "Is root project?" contraint in the UI, and "minDepth" / "maxDepth" constraints to the API. Test Plan: - Used the UI to query root projects, got only root projects back. - Used "project.search" in the API to query combinations of root projects and projects at particular depths, got matching results. Maniphest Tasks: T13441 Differential Revision: https://secure.phabricator.com/D20886 --- src/__phutil_library_map__.php | 2 + .../query/PhabricatorProjectSearchEngine.php | 65 +++++++++++++++++++ .../field/PhabricatorSearchIntField.php | 22 +++++++ 3 files changed, 89 insertions(+) create mode 100644 src/applications/search/field/PhabricatorSearchIntField.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 83954637ce..2db41f0a69 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -4652,6 +4652,7 @@ phutil_register_library_map(array( 'PhabricatorSearchHovercardController' => 'applications/search/controller/PhabricatorSearchHovercardController.php', 'PhabricatorSearchIndexVersion' => 'applications/search/storage/PhabricatorSearchIndexVersion.php', 'PhabricatorSearchIndexVersionDestructionEngineExtension' => 'applications/search/engineextension/PhabricatorSearchIndexVersionDestructionEngineExtension.php', + 'PhabricatorSearchIntField' => 'applications/search/field/PhabricatorSearchIntField.php', 'PhabricatorSearchManagementIndexWorkflow' => 'applications/search/management/PhabricatorSearchManagementIndexWorkflow.php', 'PhabricatorSearchManagementInitWorkflow' => 'applications/search/management/PhabricatorSearchManagementInitWorkflow.php', 'PhabricatorSearchManagementNgramsWorkflow' => 'applications/search/management/PhabricatorSearchManagementNgramsWorkflow.php', @@ -11273,6 +11274,7 @@ phutil_register_library_map(array( 'PhabricatorSearchHovercardController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchIndexVersion' => 'PhabricatorSearchDAO', 'PhabricatorSearchIndexVersionDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension', + 'PhabricatorSearchIntField' => 'PhabricatorSearchField', 'PhabricatorSearchManagementIndexWorkflow' => 'PhabricatorSearchManagementWorkflow', 'PhabricatorSearchManagementInitWorkflow' => 'PhabricatorSearchManagementWorkflow', 'PhabricatorSearchManagementNgramsWorkflow' => 'PhabricatorSearchManagementWorkflow', diff --git a/src/applications/project/query/PhabricatorProjectSearchEngine.php b/src/applications/project/query/PhabricatorProjectSearchEngine.php index 88a35676cc..cb179c995f 100644 --- a/src/applications/project/query/PhabricatorProjectSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectSearchEngine.php @@ -65,6 +65,35 @@ final class PhabricatorProjectSearchEngine pht( 'Pass true to find only milestones, or false to omit '. 'milestones.')), + id(new PhabricatorSearchThreeStateField()) + ->setLabel(pht('Root Projects')) + ->setKey('isRoot') + ->setOptions( + pht('(Show All)'), + pht('Show Only Root Projects'), + pht('Hide Root Projects')) + ->setDescription( + pht( + 'Pass true to find only root projects, or false to omit '. + 'root projects.')), + id(new PhabricatorSearchIntField()) + ->setLabel(pht('Minimum Depth')) + ->setKey('minDepth') + ->setIsHidden(true) + ->setDescription( + pht( + 'Find projects with a given minimum depth. Root projects '. + 'have depth 0, their immediate children have depth 1, and '. + 'so on.')), + id(new PhabricatorSearchIntField()) + ->setLabel(pht('Maximum Depth')) + ->setKey('maxDepth') + ->setIsHidden(true) + ->setDescription( + pht( + 'Find projects with a given maximum depth. Root projects '. + 'have depth 0, their immediate children have depth 1, and '. + 'so on.')), id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Subtypes')) ->setKey('subtypes') @@ -137,6 +166,42 @@ final class PhabricatorProjectSearchEngine $query->withIsMilestone($map['isMilestone']); } + $min_depth = $map['minDepth']; + $max_depth = $map['maxDepth']; + + if ($min_depth !== null || $max_depth !== null) { + if ($min_depth !== null && $max_depth !== null) { + if ($min_depth > $max_depth) { + throw new Exception( + pht( + 'Search constraint "minDepth" must be no larger than '. + 'search constraint "maxDepth".')); + } + } + } + + if ($map['isRoot'] !== null) { + if ($map['isRoot']) { + if ($max_depth === null) { + $max_depth = 0; + } else { + $max_depth = min(0, $max_depth); + } + + $query->withDepthBetween(null, 0); + } else { + if ($min_depth === null) { + $min_depth = 1; + } else { + $min_depth = max($min_depth, 1); + } + } + } + + if ($min_depth !== null || $max_depth !== null) { + $query->withDepthBetween($min_depth, $max_depth); + } + if ($map['parentPHIDs']) { $query->withParentProjectPHIDs($map['parentPHIDs']); } diff --git a/src/applications/search/field/PhabricatorSearchIntField.php b/src/applications/search/field/PhabricatorSearchIntField.php new file mode 100644 index 0000000000..70af934470 --- /dev/null +++ b/src/applications/search/field/PhabricatorSearchIntField.php @@ -0,0 +1,22 @@ +getInt($key); + } + + protected function newControl() { + return new AphrontFormTextControl(); + } + + protected function newConduitParameterType() { + return new ConduitIntParameterType(); + } + +}