diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1120,8 +1120,10 @@ 'NuanceQueueEditController' => 'applications/nuance/controller/NuanceQueueEditController.php', 'NuanceQueueEditor' => 'applications/nuance/editor/NuanceQueueEditor.php', 'NuanceQueueItem' => 'applications/nuance/storage/NuanceQueueItem.php', + 'NuanceQueueListController' => 'applications/nuance/controller/NuanceQueueListController.php', 'NuanceQueuePHIDType' => 'applications/nuance/phid/NuanceQueuePHIDType.php', 'NuanceQueueQuery' => 'applications/nuance/query/NuanceQueueQuery.php', + 'NuanceQueueSearchEngine' => 'applications/nuance/query/NuanceQueueSearchEngine.php', 'NuanceQueueTransaction' => 'applications/nuance/storage/NuanceQueueTransaction.php', 'NuanceQueueTransactionComment' => 'applications/nuance/storage/NuanceQueueTransactionComment.php', 'NuanceQueueTransactionQuery' => 'applications/nuance/query/NuanceQueueTransactionQuery.php', @@ -4463,8 +4465,10 @@ 'NuanceQueueEditController' => 'NuanceController', 'NuanceQueueEditor' => 'PhabricatorApplicationTransactionEditor', 'NuanceQueueItem' => 'NuanceDAO', + 'NuanceQueueListController' => 'NuanceController', 'NuanceQueuePHIDType' => 'PhabricatorPHIDType', 'NuanceQueueQuery' => 'NuanceQuery', + 'NuanceQueueSearchEngine' => 'PhabricatorApplicationSearchEngine', 'NuanceQueueTransaction' => 'NuanceTransaction', 'NuanceQueueTransactionComment' => 'PhabricatorApplicationTransactionComment', 'NuanceQueueTransactionQuery' => 'PhabricatorApplicationTransactionQuery', diff --git a/src/applications/nuance/application/PhabricatorNuanceApplication.php b/src/applications/nuance/application/PhabricatorNuanceApplication.php --- a/src/applications/nuance/application/PhabricatorNuanceApplication.php +++ b/src/applications/nuance/application/PhabricatorNuanceApplication.php @@ -51,6 +51,7 @@ 'create/' => 'NuanceSourceCreateController', ), 'queue/' => array( + '(?:query/(?P[^/]+)/)?' => 'NuanceQueueListController', 'view/(?P[1-9]\d*)/' => 'NuanceQueueViewController', 'edit/(?P[1-9]\d*)/' => 'NuanceQueueEditController', 'new/' => 'NuanceQueueEditController', diff --git a/src/applications/nuance/controller/NuanceQueueEditController.php b/src/applications/nuance/controller/NuanceQueueEditController.php --- a/src/applications/nuance/controller/NuanceQueueEditController.php +++ b/src/applications/nuance/controller/NuanceQueueEditController.php @@ -4,11 +4,13 @@ public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); + $queues_uri = $this->getApplicationURI('queue/'); $queue_id = $request->getURIData('id'); $is_new = !$queue_id; if ($is_new) { $queue = NuanceQueue::initializeNewQueue(); + $cancel_uri = $queues_uri; } else { $queue = id(new NuanceQueueQuery()) ->setViewer($viewer) @@ -17,12 +19,60 @@ if (!$queue) { return new Aphront404Response(); } + $cancel_uri = $queue->getURI(); + } + + $v_name = $queue->getName(); + $e_name = true; + $v_edit = $queue->getEditPolicy(); + $v_view = $queue->getViewPolicy(); + + $validation_exception = null; + if ($request->isFormPost()) { + $e_name = null; + + $v_name = $request->getStr('name'); + $v_edit = $request->getStr('editPolicy'); + $v_view = $request->getStr('viewPolicy'); + + $type_name = NuanceQueueTransaction::TYPE_NAME; + $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY; + $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY; + + $xactions = array(); + + $xactions[] = id(new NuanceQueueTransaction()) + ->setTransactionType($type_name) + ->setNewValue($v_name); + + $xactions[] = id(new NuanceQueueTransaction()) + ->setTransactionType($type_view) + ->setNewValue($v_view); + + $xactions[] = id(new NuanceQueueTransaction()) + ->setTransactionType($type_edit) + ->setNewValue($v_edit); + + $editor = id(new NuanceQueueEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true); + + try { + + $editor->applyTransactions($queue, $xactions); + + $uri = $queue->getURI(); + return id(new AphrontRedirectResponse())->setURI($uri); + } catch (PhabricatorApplicationTransactionValidationException $ex) { + $validation_exception = $ex; + + $e_name = $ex->getShortMessage($type_name); + } } $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb( - pht('Queues'), - $this->getApplicationURI('queue/')); + $crumbs->addTextCrumb(pht('Queues'), $queues_uri); if ($is_new) { $title = pht('Create Queue'); @@ -33,8 +83,50 @@ $crumbs->addTextCrumb(pht('Edit')); } + $policies = id(new PhabricatorPolicyQuery()) + ->setViewer($viewer) + ->setObject($queue) + ->execute(); + + $form = id(new AphrontFormView()) + ->setUser($viewer) + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel(pht('Name')) + ->setName('name') + ->setError($e_name) + ->setValue($v_name)) + ->appendChild( + id(new AphrontFormPolicyControl()) + ->setUser($viewer) + ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) + ->setPolicyObject($queue) + ->setPolicies($policies) + ->setValue($v_view) + ->setName('viewPolicy')) + ->appendChild( + id(new AphrontFormPolicyControl()) + ->setUser($viewer) + ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) + ->setPolicyObject($queue) + ->setPolicies($policies) + ->setValue($v_edit) + ->setName('editPolicy')) + ->appendChild( + id(new AphrontFormSubmitControl()) + ->addCancelButton($cancel_uri) + ->setValue(pht('Save'))); + + $box = id(new PHUIObjectBoxView()) + ->setHeaderText($title) + ->setValidationException($validation_exception) + ->appendChild($form); + return $this->buildApplicationPage( - $crumbs, + array( + $crumbs, + $box, + ), array( 'title' => $title, )); diff --git a/src/applications/nuance/controller/NuanceQueueListController.php b/src/applications/nuance/controller/NuanceQueueListController.php new file mode 100644 --- /dev/null +++ b/src/applications/nuance/controller/NuanceQueueListController.php @@ -0,0 +1,48 @@ +getRequest(); + $controller = id(new PhabricatorApplicationSearchController($request)) + ->setQueryKey($request->getURIData('queryKey')) + ->setSearchEngine(new NuanceQueueSearchEngine()) + ->setNavigation($this->buildSideNavView()); + + return $this->delegateToController($controller); + } + + public function buildSideNavView($for_app = false) { + $user = $this->getRequest()->getUser(); + + $nav = new AphrontSideNavFilterView(); + $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); + + id(new NuanceQueueSearchEngine()) + ->setViewer($user) + ->addNavigationItems($nav->getMenu()); + + $nav->selectFilter(null); + + return $nav; + } + + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + // TODO: Maybe use SourceManage capability? + $can_create = true; + + $crumbs->addAction( + id(new PHUIListItemView()) + ->setName(pht('Create Queue')) + ->setHref($this->getApplicationURI('queue/new/')) + ->setIcon('fa-plus-square') + ->setDisabled(!$can_create) + ->setWorkflow(!$can_create)); + + return $crumbs; + } + +} diff --git a/src/applications/nuance/controller/NuanceQueueViewController.php b/src/applications/nuance/controller/NuanceQueueViewController.php --- a/src/applications/nuance/controller/NuanceQueueViewController.php +++ b/src/applications/nuance/controller/NuanceQueueViewController.php @@ -2,41 +2,92 @@ final class NuanceQueueViewController extends NuanceController { - private $queueID; + public function handleRequest(AphrontRequest $request) { + $viewer = $this->getViewer(); - public function setQueueID($queue_id) { - $this->queueID = $queue_id; - return $this; - } - public function getQueueID() { - return $this->queueID; - } - - public function willProcessRequest(array $data) { - $this->setQueueID($data['id']); - } - - public function processRequest() { - $request = $this->getRequest(); - $user = $request->getUser(); - - $queue_id = $this->getQueueID(); $queue = id(new NuanceQueueQuery()) - ->setViewer($user) - ->withIDs(array($queue_id)) + ->setViewer($viewer) + ->withIDs(array($request->getURIData('id'))) ->executeOne(); - if (!$queue) { return new Aphront404Response(); } + $title = $queue->getName(); + $crumbs = $this->buildApplicationCrumbs(); - $title = pht('TODO'); + $crumbs->addTextCrumb(pht('Queues'), $this->getApplicationURI('queue/')); + $crumbs->addTextCrumb($queue->getName()); + + $header = $this->buildHeaderView($queue); + $actions = $this->buildActionView($queue); + $properties = $this->buildPropertyView($queue, $actions); + + $box = id(new PHUIObjectBoxView()) + ->setHeader($header) + ->addPropertyList($properties); + + $timeline = $this->buildTransactionTimeline( + $queue, + new NuanceQueueTransactionQuery()); + $timeline->setShouldTerminate(true); return $this->buildApplicationPage( - $crumbs, + array( + $crumbs, + $box, + $timeline, + ), array( 'title' => $title, )); } + + private function buildHeaderView(NuanceQueue $queue) { + $viewer = $this->getViewer(); + + $header = id(new PHUIHeaderView()) + ->setUser($viewer) + ->setHeader($queue->getName()) + ->setPolicyObject($queue); + + return $header; + } + + private function buildActionView(NuanceQueue $queue) { + $viewer = $this->getViewer(); + $id = $queue->getID(); + + $actions = id(new PhabricatorActionListView()) + ->setObjectURI($queue->getURI()) + ->setUser($viewer); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $queue, + PhabricatorPolicyCapability::CAN_EDIT); + + $actions->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Edit Queue')) + ->setIcon('fa-pencil') + ->setHref($this->getApplicationURI("queue/edit/{$id}/")) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + return $actions; + } + + private function buildPropertyView( + NuanceQueue $queue, + PhabricatorActionListView $actions) { + $viewer = $this->getViewer(); + + $properties = id(new PHUIPropertyListView()) + ->setUser($viewer) + ->setObject($queue) + ->setActionList($actions); + + return $properties; + } } diff --git a/src/applications/nuance/editor/NuanceQueueEditor.php b/src/applications/nuance/editor/NuanceQueueEditor.php --- a/src/applications/nuance/editor/NuanceQueueEditor.php +++ b/src/applications/nuance/editor/NuanceQueueEditor.php @@ -14,12 +14,88 @@ public function getTransactionTypes() { $types = parent::getTransactionTypes(); - $types[] = PhabricatorTransactions::TYPE_EDGE; - $types[] = PhabricatorTransactions::TYPE_COMMENT; + $types[] = NuanceQueueTransaction::TYPE_NAME; + $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; return $types; } + protected function getCustomTransactionOldValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case NuanceQueueTransaction::TYPE_NAME: + return $object->getName(); + } + + return parent::getCustomTransactionOldValue($object, $xaction); + } + + protected function getCustomTransactionNewValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case NuanceQueueTransaction::TYPE_NAME: + return $xaction->getNewValue(); + } + + return parent::getCustomTransactionNewValue($object, $xaction); + } + + protected function applyCustomInternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case NuanceQueueTransaction::TYPE_NAME: + $object->setName($xaction->getNewValue()); + break; + } + } + + protected function applyCustomExternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case NuanceQueueTransaction::TYPE_NAME: + return; + } + + return parent::applyCustomExternalTransaction($object, $xaction); + } + protected function validateTransaction( + PhabricatorLiskDAO $object, + $type, + array $xactions) { + + $errors = parent::validateTransaction($object, $type, $xactions); + + switch ($type) { + case NuanceQueueTransaction::TYPE_NAME: + $missing = $this->validateIsEmptyTextField( + $object->getName(), + $xactions); + + if ($missing) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Required'), + pht('A queue must have a name.'), + nonempty(last($xactions), null)); + + $error->setIsMissingFieldError(true); + $errors[] = $error; + } + break; + } + + return $errors; + } + + } diff --git a/src/applications/nuance/query/NuanceQueueSearchEngine.php b/src/applications/nuance/query/NuanceQueueSearchEngine.php new file mode 100644 --- /dev/null +++ b/src/applications/nuance/query/NuanceQueueSearchEngine.php @@ -0,0 +1,75 @@ + pht('All Queues'), + ); + + 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); + } + + protected function renderResultList( + array $queues, + PhabricatorSavedQuery $query, + array $handles) { + assert_instances_of($queues, 'NuanceQueue'); + + $viewer = $this->requireViewer(); + + $list = new PHUIObjectItemListView(); + $list->setUser($viewer); + foreach ($queues as $queue) { + $item = id(new PHUIObjectItemView()) + ->setObjectName(pht('Queue %d', $queue->getID())) + ->setHeader($queue->getName()) + ->setHref($queue->getURI()); + $list->addItem($item); + } + + return $list; + } + +} diff --git a/src/applications/nuance/storage/NuanceQueueTransaction.php b/src/applications/nuance/storage/NuanceQueueTransaction.php --- a/src/applications/nuance/storage/NuanceQueueTransaction.php +++ b/src/applications/nuance/storage/NuanceQueueTransaction.php @@ -2,6 +2,8 @@ final class NuanceQueueTransaction extends NuanceTransaction { + const TYPE_NAME = 'nuance.queue.name'; + public function getApplicationTransactionType() { return NuanceQueuePHIDType::TYPECONST; }