@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.

Rough in a Nuance "work" controller

Summary:
Ref T12738. This is mostly just laying in groundwork and prerequisites, like the ability to query items by queue.

Eventually, this will become the main UI which staff use to process a queue of items. For now, it does nothing and renders nonsense.

This and probably the next big chunk of changes are all going to be made-up, nonfinal things that just make basic operations work until we have fundamental flows -- like "assign", "comment", "close" -- working at a basic level and can think more about UI/workflow.

Test Plan:
Visited the page, it loaded a mostly-reasonable item and then rendered nonsense:

{F4975050}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T12738

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

+197 -4
+2
src/__phutil_library_map__.php
··· 1663 1663 'NuanceQueueTransactionQuery' => 'applications/nuance/query/NuanceQueueTransactionQuery.php', 1664 1664 'NuanceQueueTransactionType' => 'applications/nuance/xaction/NuanceQueueTransactionType.php', 1665 1665 'NuanceQueueViewController' => 'applications/nuance/controller/NuanceQueueViewController.php', 1666 + 'NuanceQueueWorkController' => 'applications/nuance/controller/NuanceQueueWorkController.php', 1666 1667 'NuanceSchemaSpec' => 'applications/nuance/storage/NuanceSchemaSpec.php', 1667 1668 'NuanceSource' => 'applications/nuance/storage/NuanceSource.php', 1668 1669 'NuanceSourceActionController' => 'applications/nuance/controller/NuanceSourceActionController.php', ··· 6788 6789 'NuanceQueueTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 6789 6790 'NuanceQueueTransactionType' => 'PhabricatorModularTransactionType', 6790 6791 'NuanceQueueViewController' => 'NuanceQueueController', 6792 + 'NuanceQueueWorkController' => 'NuanceQueueController', 6791 6793 'NuanceSchemaSpec' => 'PhabricatorConfigSchemaSpec', 6792 6794 'NuanceSource' => array( 6793 6795 'NuanceDAO',
+1
src/applications/nuance/application/PhabricatorNuanceApplication.php
··· 51 51 $this->getQueryRoutePattern() => 'NuanceQueueListController', 52 52 $this->getEditRoutePattern('edit/') => 'NuanceQueueEditController', 53 53 'view/(?P<id>[1-9]\d*)/' => 'NuanceQueueViewController', 54 + 'work/(?P<id>[1-9]\d*)/' => 'NuanceQueueWorkController', 54 55 ), 55 56 ), 56 57 '/action/' => array(
+8
src/applications/nuance/controller/NuanceQueueViewController.php
··· 70 70 ->setDisabled(!$can_edit) 71 71 ->setWorkflow(!$can_edit)); 72 72 73 + $curtain->addAction( 74 + id(new PhabricatorActionView()) 75 + ->setName(pht('Begin Work')) 76 + ->setIcon('fa-play-circle-o') 77 + ->setHref($this->getApplicationURI("queue/work/{$id}/")) 78 + ->setDisabled(!$can_edit) 79 + ->setWorkflow(!$can_edit)); 80 + 73 81 return $curtain; 74 82 } 75 83
+98
src/applications/nuance/controller/NuanceQueueWorkController.php
··· 1 + <?php 2 + 3 + final class NuanceQueueWorkController 4 + extends NuanceQueueController { 5 + 6 + public function handleRequest(AphrontRequest $request) { 7 + $viewer = $this->getViewer(); 8 + 9 + $queue = id(new NuanceQueueQuery()) 10 + ->setViewer($viewer) 11 + ->withIDs(array($request->getURIData('id'))) 12 + ->executeOne(); 13 + if (!$queue) { 14 + return new Aphront404Response(); 15 + } 16 + 17 + $title = $queue->getName(); 18 + 19 + $crumbs = $this->buildApplicationCrumbs(); 20 + $crumbs->addTextCrumb(pht('Queues'), $this->getApplicationURI('queue/')); 21 + $crumbs->addTextCrumb($queue->getName(), $queue->getURI()); 22 + $crumbs->addTextCrumb(pht('Work')); 23 + $crumbs->setBorder(true); 24 + 25 + // For now, just pick the first open item. 26 + 27 + $items = id(new NuanceItemQuery()) 28 + ->setViewer($viewer) 29 + ->withQueuePHIDs( 30 + array( 31 + $queue->getPHID(), 32 + )) 33 + ->withStatuses( 34 + array( 35 + NuanceItem::STATUS_OPEN, 36 + )) 37 + ->requireCapabilities( 38 + array( 39 + PhabricatorPolicyCapability::CAN_VIEW, 40 + PhabricatorPolicyCapability::CAN_EDIT, 41 + )) 42 + ->setLimit(5) 43 + ->execute(); 44 + 45 + if (!$items) { 46 + return $this->newDialog() 47 + ->setTitle(pht('Queue Empty')) 48 + ->appendParagraph( 49 + pht( 50 + 'This queue has no open items which you have permission to '. 51 + 'work on.')) 52 + ->addCancelButton($queue->getURI()); 53 + } 54 + 55 + $item = head($items); 56 + 57 + $curtain = $this->buildCurtain($queue); 58 + 59 + $timeline = $this->buildTransactionTimeline( 60 + $item, 61 + new NuanceItemTransactionQuery()); 62 + $timeline->setShouldTerminate(true); 63 + 64 + $view = id(new PHUITwoColumnView()) 65 + ->setCurtain($curtain) 66 + ->setMainColumn($timeline); 67 + 68 + return $this->newPage() 69 + ->setTitle($title) 70 + ->setCrumbs($crumbs) 71 + ->appendChild($view); 72 + } 73 + 74 + private function buildCurtain(NuanceQueue $queue) { 75 + $viewer = $this->getViewer(); 76 + $id = $queue->getID(); 77 + 78 + $curtain = $this->newCurtainView(); 79 + 80 + $curtain->addAction( 81 + id(new PhabricatorActionView()) 82 + ->setType(PhabricatorActionView::TYPE_DIVIDER)); 83 + 84 + $curtain->addAction( 85 + id(new PhabricatorActionView()) 86 + ->setType(PhabricatorActionView::TYPE_LABEL) 87 + ->setName(pht('Queue Actions'))); 88 + 89 + $curtain->addAction( 90 + id(new PhabricatorActionView()) 91 + ->setName(pht('Manage Queue')) 92 + ->setIcon('fa-cog') 93 + ->setHref($this->getApplicationURI("queue/view/{$id}/"))); 94 + 95 + return $curtain; 96 + } 97 + 98 + }
+65 -4
src/applications/nuance/query/NuanceItemQuery.php
··· 6 6 private $ids; 7 7 private $phids; 8 8 private $sourcePHIDs; 9 + private $queuePHIDs; 9 10 private $itemTypes; 10 11 private $itemKeys; 11 12 private $containerKeys; 13 + private $statuses; 12 14 13 15 public function withIDs(array $ids) { 14 16 $this->ids = $ids; ··· 25 27 return $this; 26 28 } 27 29 30 + public function withQueuePHIDs(array $queue_phids) { 31 + $this->queuePHIDs = $queue_phids; 32 + return $this; 33 + } 34 + 28 35 public function withItemTypes(array $item_types) { 29 36 $this->itemTypes = $item_types; 30 37 return $this; ··· 35 42 return $this; 36 43 } 37 44 45 + public function withStatuses(array $statuses) { 46 + $this->statuses = $statuses; 47 + return $this; 48 + } 49 + 38 50 public function withItemContainerKeys(array $container_keys) { 39 51 $this->containerKeys = $container_keys; 40 52 return $this; ··· 49 61 } 50 62 51 63 protected function willFilterPage(array $items) { 64 + $viewer = $this->getViewer(); 52 65 $source_phids = mpull($items, 'getSourcePHID'); 53 66 54 - // NOTE: We always load sources, even if the viewer can't formally see 55 - // them. If they can see the item, they're allowed to be aware of the 56 - // source in some sense. 57 67 $sources = id(new NuanceSourceQuery()) 58 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 68 + ->setViewer($viewer) 59 69 ->withPHIDs($source_phids) 60 70 ->execute(); 61 71 $sources = mpull($sources, null, 'getPHID'); ··· 81 91 $item->attachImplementation($type); 82 92 } 83 93 94 + $queue_phids = array(); 95 + foreach ($items as $item) { 96 + $queue_phid = $item->getQueuePHID(); 97 + if ($queue_phid) { 98 + $queue_phids[$queue_phid] = $queue_phid; 99 + } 100 + } 101 + 102 + if ($queue_phids) { 103 + $queues = id(new NuanceQueueQuery()) 104 + ->setViewer($viewer) 105 + ->withPHIDs($queue_phids) 106 + ->execute(); 107 + $queues = mpull($queues, null, 'getPHID'); 108 + } else { 109 + $queues = array(); 110 + } 111 + 112 + foreach ($items as $key => $item) { 113 + $queue_phid = $item->getQueuePHID(); 114 + 115 + if (!$queue_phid) { 116 + $item->attachQueue(null); 117 + continue; 118 + } 119 + 120 + $queue = idx($queues, $queue_phid); 121 + 122 + if (!$queue) { 123 + unset($items[$key]); 124 + $this->didRejectResult($item); 125 + continue; 126 + } 127 + 128 + $item->attachQueue($queue); 129 + } 130 + 84 131 return $items; 85 132 } 86 133 ··· 94 141 $this->sourcePHIDs); 95 142 } 96 143 144 + if ($this->queuePHIDs !== null) { 145 + $where[] = qsprintf( 146 + $conn, 147 + 'queuePHID IN (%Ls)', 148 + $this->queuePHIDs); 149 + } 150 + 97 151 if ($this->ids !== null) { 98 152 $where[] = qsprintf( 99 153 $conn, ··· 106 160 $conn, 107 161 'phid IN (%Ls)', 108 162 $this->phids); 163 + } 164 + 165 + if ($this->statuses !== null) { 166 + $where[] = qsprintf( 167 + $conn, 168 + 'status IN (%Ls)', 169 + $this->statuses); 109 170 } 110 171 111 172 if ($this->itemTypes !== null) {
+13
src/applications/nuance/query/NuanceItemSearchEngine.php
··· 72 72 $impl->getItemTypeDisplayIcon(), 73 73 $impl->getItemTypeDisplayName()); 74 74 75 + $queue = $item->getQueue(); 76 + if ($queue) { 77 + $view->addAttribute( 78 + phutil_tag( 79 + 'a', 80 + array( 81 + 'href' => $queue->getURI(), 82 + ), 83 + $queue->getName())); 84 + } else { 85 + $view->addAttribute(phutil_tag('em', array(), pht('Not in Queue'))); 86 + } 87 + 75 88 $list->addItem($view); 76 89 } 77 90
+10
src/applications/nuance/storage/NuanceItem.php
··· 23 23 protected $data = array(); 24 24 protected $mailKey; 25 25 26 + private $queue = self::ATTACHABLE; 26 27 private $source = self::ATTACHABLE; 27 28 private $implementation = self::ATTACHABLE; 28 29 ··· 173 174 174 175 public function attachImplementation(NuanceItemType $type) { 175 176 $this->implementation = $type; 177 + return $this; 178 + } 179 + 180 + public function getQueue() { 181 + return $this->assertAttached($this->queue); 182 + } 183 + 184 + public function attachQueue(NuanceQueue $queue = null) { 185 + $this->queue = $queue; 176 186 return $this; 177 187 } 178 188