@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Collapse "Show unassigned tasks" in Maniphest into a parameterized owners typeahead

Summary: Ref T4100. Support viewer(), members(), and add a new none().

Test Plan:
- Used all new functions.
- Batch edited tasks with unassign action.
- Saved a query from master, upgrade it to this patch, checkbox migrated cleanly into a "no one" token.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T4100

Differential Revision: https://secure.phabricator.com/D12470

+145 -84
+6 -4
src/__phutil_library_map__.php
··· 998 998 'MacroCreateMemeConduitAPIMethod' => 'applications/macro/conduit/MacroCreateMemeConduitAPIMethod.php', 999 999 'MacroQueryConduitAPIMethod' => 'applications/macro/conduit/MacroQueryConduitAPIMethod.php', 1000 1000 'ManiphestAssignEmailCommand' => 'applications/maniphest/command/ManiphestAssignEmailCommand.php', 1001 + 'ManiphestAssigneeDatasource' => 'applications/maniphest/typeahead/ManiphestAssigneeDatasource.php', 1001 1002 'ManiphestBatchEditController' => 'applications/maniphest/controller/ManiphestBatchEditController.php', 1002 1003 'ManiphestBulkEditCapability' => 'applications/maniphest/capability/ManiphestBulkEditCapability.php', 1003 1004 'ManiphestClaimEmailCommand' => 'applications/maniphest/command/ManiphestClaimEmailCommand.php', ··· 1031 1032 'ManiphestInfoConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php', 1032 1033 'ManiphestNameIndex' => 'applications/maniphest/storage/ManiphestNameIndex.php', 1033 1034 'ManiphestNameIndexEventListener' => 'applications/maniphest/event/ManiphestNameIndexEventListener.php', 1035 + 'ManiphestNoOwnerDatasource' => 'applications/maniphest/typeahead/ManiphestNoOwnerDatasource.php', 1036 + 'ManiphestOwnerDatasource' => 'applications/maniphest/typeahead/ManiphestOwnerDatasource.php', 1034 1037 'ManiphestPriorityEmailCommand' => 'applications/maniphest/command/ManiphestPriorityEmailCommand.php', 1035 1038 'ManiphestQueryConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php', 1036 1039 'ManiphestQueryStatusesConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestQueryStatusesConduitAPIMethod.php', ··· 2639 2642 'PhabricatorTypeaheadInvalidTokenException' => 'applications/typeahead/exception/PhabricatorTypeaheadInvalidTokenException.php', 2640 2643 'PhabricatorTypeaheadModularDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php', 2641 2644 'PhabricatorTypeaheadMonogramDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadMonogramDatasource.php', 2642 - 'PhabricatorTypeaheadNoOwnerDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadNoOwnerDatasource.php', 2643 - 'PhabricatorTypeaheadOwnerDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadOwnerDatasource.php', 2644 2645 'PhabricatorTypeaheadResult' => 'applications/typeahead/storage/PhabricatorTypeaheadResult.php', 2645 2646 'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadRuntimeCompositeDatasource.php', 2646 2647 'PhabricatorTypeaheadTokenView' => 'applications/typeahead/view/PhabricatorTypeaheadTokenView.php', ··· 4270 4271 'MacroCreateMemeConduitAPIMethod' => 'MacroConduitAPIMethod', 4271 4272 'MacroQueryConduitAPIMethod' => 'MacroConduitAPIMethod', 4272 4273 'ManiphestAssignEmailCommand' => 'ManiphestEmailCommand', 4274 + 'ManiphestAssigneeDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 4273 4275 'ManiphestBatchEditController' => 'ManiphestController', 4274 4276 'ManiphestBulkEditCapability' => 'PhabricatorPolicyCapability', 4275 4277 'ManiphestClaimEmailCommand' => 'ManiphestEmailCommand', ··· 4304 4306 'ManiphestInfoConduitAPIMethod' => 'ManiphestConduitAPIMethod', 4305 4307 'ManiphestNameIndex' => 'ManiphestDAO', 4306 4308 'ManiphestNameIndexEventListener' => 'PhabricatorEventListener', 4309 + 'ManiphestNoOwnerDatasource' => 'PhabricatorTypeaheadDatasource', 4310 + 'ManiphestOwnerDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 4307 4311 'ManiphestPriorityEmailCommand' => 'ManiphestEmailCommand', 4308 4312 'ManiphestQueryConduitAPIMethod' => 'ManiphestConduitAPIMethod', 4309 4313 'ManiphestQueryStatusesConduitAPIMethod' => 'ManiphestConduitAPIMethod', ··· 6053 6057 'PhabricatorTypeaheadInvalidTokenException' => 'Exception', 6054 6058 'PhabricatorTypeaheadModularDatasourceController' => 'PhabricatorTypeaheadDatasourceController', 6055 6059 'PhabricatorTypeaheadMonogramDatasource' => 'PhabricatorTypeaheadDatasource', 6056 - 'PhabricatorTypeaheadNoOwnerDatasource' => 'PhabricatorTypeaheadDatasource', 6057 - 'PhabricatorTypeaheadOwnerDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 6058 6060 'PhabricatorTypeaheadRuntimeCompositeDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 6059 6061 'PhabricatorTypeaheadTokenView' => 'AphrontTagView', 6060 6062 'PhabricatorTypeaheadUserParameterizedDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
-1
src/applications/maniphest/constants/ManiphestTaskOwner.php
··· 2 2 3 3 final class ManiphestTaskOwner extends ManiphestConstants { 4 4 5 - const OWNER_UP_FOR_GRABS = 'PHID-!!!!-UP-FOR-GRABS'; 6 5 const PROJECT_NO_PROJECT = 'PHID-!!!!-NO-PROJECT'; 7 6 8 7 }
+3 -2
src/applications/maniphest/controller/ManiphestBatchEditController.php
··· 86 86 $projects_source = new PhabricatorProjectDatasource(); 87 87 $mailable_source = new PhabricatorMetaMTAMailableDatasource(); 88 88 $mailable_source->setViewer($viewer); 89 - $owner_source = new PhabricatorTypeaheadOwnerDatasource(); 89 + $owner_source = new ManiphestAssigneeDatasource(); 90 90 $owner_source->setViewer($viewer); 91 91 92 92 require_celerity_resource('maniphest-batch-editor'); ··· 265 265 continue 2; 266 266 } 267 267 $value = head($value); 268 - if ($value === ManiphestTaskOwner::OWNER_UP_FOR_GRABS) { 268 + $no_owner = ManiphestNoOwnerDatasource::FUNCTION_TOKEN; 269 + if ($value === ManiphestNoOwnerDatasource::FUNCTION_TOKEN) { 269 270 $value = null; 270 271 } 271 272 break;
+3 -1
src/applications/maniphest/query/ManiphestTaskQuery.php
··· 73 73 } 74 74 75 75 public function withOwners(array $owners) { 76 + $no_owner = ManiphestNoOwnerDatasource::FUNCTION_TOKEN; 77 + 76 78 $this->includeUnowned = false; 77 79 foreach ($owners as $k => $phid) { 78 - if ($phid == ManiphestTaskOwner::OWNER_UP_FOR_GRABS || $phid === null) { 80 + if ($phid === $no_owner || $phid === null) { 79 81 $this->includeUnowned = true; 80 82 unset($owners[$k]); 81 83 break;
+23 -20
src/applications/maniphest/query/ManiphestTaskSearchEngine.php
··· 49 49 'assignedPHIDs', 50 50 $this->readUsersFromRequest($request, 'assigned')); 51 51 52 - $saved->setParameter('withUnassigned', $request->getBool('withUnassigned')); 53 - 54 52 $saved->setParameter( 55 53 'authorPHIDs', 56 54 $this->readUsersFromRequest($request, 'authors')); ··· 139 137 $query->withSubscribers($subscriber_phids); 140 138 } 141 139 142 - $with_unassigned = $saved->getParameter('withUnassigned'); 143 - if ($with_unassigned) { 144 - $query->withOwners(array(null)); 145 - } else { 146 - $assigned_phids = $saved->getParameter('assignedPHIDs', array()); 147 - if ($assigned_phids) { 148 - $query->withOwners($assigned_phids); 149 - } 140 + $datasource = id(new ManiphestOwnerDatasource()) 141 + ->setViewer($this->requireViewer()); 142 + 143 + $assigned_phids = $this->readAssignedPHIDs($saved); 144 + $assigned_phids = $datasource->evaluateTokens($assigned_phids); 145 + if ($assigned_phids) { 146 + $query->withOwners($assigned_phids); 150 147 } 151 148 152 149 $statuses = $saved->getParameter('statuses'); ··· 242 239 AphrontFormView $form, 243 240 PhabricatorSavedQuery $saved) { 244 241 245 - $assigned_phids = $saved->getParameter('assignedPHIDs', array()); 242 + $assigned_phids = $this->readAssignedPHIDs($saved); 243 + 246 244 $author_phids = $saved->getParameter('authorPHIDs', array()); 247 245 $all_project_phids = $saved->getParameter( 248 246 'allProjectPHIDs', ··· 258 256 array()); 259 257 $subscriber_phids = $saved->getParameter('subscriberPHIDs', array()); 260 258 261 - $with_unassigned = $saved->getParameter('withUnassigned'); 262 259 $with_no_projects = $saved->getParameter('withNoProject'); 263 260 264 261 $statuses = $saved->getParameter('statuses', array()); ··· 314 311 $form 315 312 ->appendControl( 316 313 id(new AphrontFormTokenizerControl()) 317 - ->setDatasource(new PhabricatorPeopleDatasource()) 314 + ->setDatasource(new ManiphestOwnerDatasource()) 318 315 ->setName('assigned') 319 316 ->setLabel(pht('Assigned To')) 320 317 ->setValue($assigned_phids)) 321 - ->appendChild( 322 - id(new AphrontFormCheckboxControl()) 323 - ->addCheckbox( 324 - 'withUnassigned', 325 - 1, 326 - pht('Show only unassigned tasks.'), 327 - $with_unassigned)) 328 318 ->appendControl( 329 319 id(new AphrontFormTokenizerControl()) 330 320 ->setDatasource(new PhabricatorProjectDatasource()) ··· 561 551 ->setCanEditPriority($can_edit_priority) 562 552 ->setCanBatchEdit($can_bulk_edit) 563 553 ->setShowBatchControls($this->showBatchControls); 554 + } 555 + 556 + private function readAssignedPHIDs(PhabricatorSavedQuery $saved) { 557 + $assigned_phids = $saved->getParameter('assignedPHIDs', array()); 558 + 559 + // This may be present in old saved queries from before parameterized 560 + // typeaheads, and is retained for compatibility. We could remove it by 561 + // migrating old saved queries. 562 + if ($saved->getParameter('withUnassigned')) { 563 + $assigned_phids[] = ManiphestNoOwnerDatasource::FUNCTION_TOKEN; 564 + } 565 + 566 + return $assigned_phids; 564 567 } 565 568 566 569 }
+21
src/applications/maniphest/typeahead/ManiphestAssigneeDatasource.php
··· 1 + <?php 2 + 3 + final class ManiphestAssigneeDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse Assignees'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type a username or "none"...'); 12 + } 13 + 14 + public function getComponentDatasources() { 15 + return array( 16 + new PhabricatorPeopleDatasource(), 17 + new ManiphestNoOwnerDatasource(), 18 + ); 19 + } 20 + 21 + }
+64
src/applications/maniphest/typeahead/ManiphestNoOwnerDatasource.php
··· 1 + <?php 2 + 3 + final class ManiphestNoOwnerDatasource 4 + extends PhabricatorTypeaheadDatasource { 5 + 6 + const FUNCTION_TOKEN = 'none()'; 7 + 8 + public function getBrowseTitle() { 9 + return pht('Browse No Owner'); 10 + } 11 + 12 + public function getPlaceholderText() { 13 + return pht('Type "none"...'); 14 + } 15 + 16 + public function getDatasourceApplicationClass() { 17 + return 'PhabricatorManiphestApplication'; 18 + } 19 + 20 + public function getDatasourceFunctions() { 21 + return array( 22 + 'none' => array( 23 + 'name' => pht('Find results which are not assigned.'), 24 + ), 25 + ); 26 + } 27 + 28 + public function loadResults() { 29 + $results = array( 30 + $this->buildNoOwnerResult(), 31 + ); 32 + return $this->filterResultsAgainstTokens($results); 33 + } 34 + 35 + protected function evaluateFunction($function, array $argv_list) { 36 + $results = array(); 37 + 38 + foreach ($argv_list as $argv) { 39 + $results[] = self::FUNCTION_TOKEN; 40 + } 41 + 42 + return $results; 43 + } 44 + 45 + public function renderFunctionTokens($function, array $argv_list) { 46 + $results = array(); 47 + foreach ($argv_list as $argv) { 48 + $results[] = PhabricatorTypeaheadTokenView::newFromTypeaheadResult( 49 + $this->buildNoOwnerResult()); 50 + } 51 + return $results; 52 + } 53 + 54 + private function buildNoOwnerResult() { 55 + $name = pht('No Owner'); 56 + return $this->newFunctionResult() 57 + ->setName($name.' none') 58 + ->setDisplayName($name) 59 + ->setIcon('fa-ban') 60 + ->setPHID('none()') 61 + ->setUnique(true); 62 + } 63 + 64 + }
+23
src/applications/maniphest/typeahead/ManiphestOwnerDatasource.php
··· 1 + <?php 2 + 3 + final class ManiphestOwnerDatasource 4 + extends PhabricatorTypeaheadCompositeDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse Owners'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type a username or function...'); 12 + } 13 + 14 + public function getComponentDatasources() { 15 + return array( 16 + new PhabricatorViewerDatasource(), 17 + new ManiphestNoOwnerDatasource(), 18 + new PhabricatorPeopleDatasource(), 19 + new PhabricatorProjectMembersDatasource(), 20 + ); 21 + } 22 + 23 + }
+1 -1
src/applications/search/query/PhabricatorSearchApplicationSearchEngine.php
··· 126 126 id(new AphrontFormTokenizerControl()) 127 127 ->setName('ownerPHIDs') 128 128 ->setLabel('Owners') 129 - ->setDatasource(new PhabricatorTypeaheadOwnerDatasource()) 129 + ->setDatasource(new ManiphestOwnerDatasource()) 130 130 ->setValue($owner_phids)) 131 131 ->appendChild( 132 132 id(new AphrontFormCheckboxControl())
+1 -2
src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php
··· 247 247 // format to make it easier to debug typeahead output. 248 248 249 249 foreach ($sources as $key => $source) { 250 - // This can happen with composite sources like user or project, as well 251 - // generic ones like NoOwner 250 + // This can happen with composite or generic sources. 252 251 if (!$source->getDatasourceApplicationClass()) { 253 252 continue; 254 253 }
-32
src/applications/typeahead/datasource/PhabricatorTypeaheadNoOwnerDatasource.php
··· 1 - <?php 2 - 3 - final class PhabricatorTypeaheadNoOwnerDatasource 4 - extends PhabricatorTypeaheadDatasource { 5 - 6 - public function getBrowseTitle() { 7 - return pht('Browse No Owner'); 8 - } 9 - 10 - public function getPlaceholderText() { 11 - return pht('Type "none"...'); 12 - } 13 - 14 - public function getDatasourceApplicationClass() { 15 - return null; 16 - } 17 - 18 - public function loadResults() { 19 - $viewer = $this->getViewer(); 20 - $raw_query = $this->getRawQuery(); 21 - 22 - $results = array(); 23 - 24 - $results[] = id(new PhabricatorTypeaheadResult()) 25 - ->setName(pht('None')) 26 - ->setIcon('fa-ban orange') 27 - ->setPHID(ManiphestTaskOwner::OWNER_UP_FOR_GRABS); 28 - 29 - return $results; 30 - } 31 - 32 - }
-21
src/applications/typeahead/datasource/PhabricatorTypeaheadOwnerDatasource.php
··· 1 - <?php 2 - 3 - final class PhabricatorTypeaheadOwnerDatasource 4 - extends PhabricatorTypeaheadCompositeDatasource { 5 - 6 - public function getBrowseTitle() { 7 - return pht('Browse Owners'); 8 - } 9 - 10 - public function getPlaceholderText() { 11 - return pht('Type a user name or "none"...'); 12 - } 13 - 14 - public function getComponentDatasources() { 15 - return array( 16 - new PhabricatorPeopleDatasource(), 17 - new PhabricatorTypeaheadNoOwnerDatasource(), 18 - ); 19 - } 20 - 21 - }