From c69d4658918c10d4f26fe5844fd219667d8e5799 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 23 Aug 2015 08:34:52 -0700 Subject: [PATCH] Add basic "View" and "Edit" features to Nuance Summary: Ref T8783. The "View" UI is where a user would check their request for feedback or a resolution, if it's something that makes sense for them to interact with from the web UI. The "Edit" UI is the manage/admin UI where you'd respond to a request. It's similar to the view UI but will have actions and eventually some queue UI, etc. (I don't think items need a normal "Edit" UI -- it doesn't make sense to "Edit" a tweet or inbound email -- but maybe this will shuffle around a little eventually.) Test Plan: View {F747218} Edit {F747219} Reviewers: chad Reviewed By: chad Maniphest Tasks: T8783 Differential Revision: https://secure.phabricator.com/D13980 --- .../controller/NuanceItemEditController.php | 85 ++++++++++++++++--- .../controller/NuanceItemViewController.php | 67 ++++++++++++++- .../nuance/phid/NuanceRequestorPHIDType.php | 4 +- .../nuance/query/NuanceItemQuery.php | 41 ++++++--- .../NuancePhabricatorFormSourceDefinition.php | 31 ++++++- .../nuance/source/NuanceSourceDefinition.php | 14 +++ .../nuance/storage/NuanceItem.php | 2 + 7 files changed, 214 insertions(+), 30 deletions(-) diff --git a/src/applications/nuance/controller/NuanceItemEditController.php b/src/applications/nuance/controller/NuanceItemEditController.php index 89bfbda5c9..bad409a801 100644 --- a/src/applications/nuance/controller/NuanceItemEditController.php +++ b/src/applications/nuance/controller/NuanceItemEditController.php @@ -3,30 +3,91 @@ final class NuanceItemEditController extends NuanceController { public function handleRequest(AphrontRequest $request) { - $viewer = $request->getViewer(); + $viewer = $this->getViewer(); $id = $request->getURIData('id'); - if (!$id) { - $item = new NuanceItem(); - } else { - $item = id(new NuanceItemQuery()) - ->setViewer($viewer) - ->withIDs(array($id)) - ->executeOne(); - } - + $item = id(new NuanceItemQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); if (!$item) { return new Aphront404Response(); } + $title = pht('Item %d', $item->getID()); + $crumbs = $this->buildApplicationCrumbs(); - $title = 'TODO'; + $crumbs->addTextCrumb($title); + $crumbs->addTextCrumb(pht('Edit')); + + $properties = $this->buildPropertyView($item); + $actions = $this->buildActionView($item); + $properties->setActionList($actions); + + $box = id(new PHUIObjectBoxView()) + ->setHeaderText($title) + ->addPropertyList($properties); return $this->buildApplicationPage( - $crumbs, + array( + $crumbs, + $box, + ), array( 'title' => $title, )); } + private function buildPropertyView(NuanceItem $item) { + $viewer = $this->getViewer(); + + $properties = id(new PHUIPropertyListView()) + ->setUser($viewer) + ->setObject($item); + + $properties->addProperty( + pht('Date Created'), + phabricator_datetime($item->getDateCreated(), $viewer)); + + $properties->addProperty( + pht('Requestor'), + $viewer->renderHandle($item->getRequestorPHID())); + + $properties->addProperty( + pht('Source'), + $viewer->renderHandle($item->getSourcePHID())); + + $source = $item->getSource(); + $definition = $source->requireDefinition(); + + $definition->renderItemEditProperties( + $viewer, + $item, + $properties); + + return $properties; + } + + private function buildActionView(NuanceItem $item) { + $viewer = $this->getViewer(); + $id = $item->getID(); + + $actions = id(new PhabricatorActionListView()) + ->setUser($viewer); + + $actions->addAction( + id(new PhabricatorActionView()) + ->setName(pht('View Item')) + ->setIcon('fa-eye') + ->setHref($this->getApplicationURI("item/view/{$id}/"))); + + return $actions; + } + + } diff --git a/src/applications/nuance/controller/NuanceItemViewController.php b/src/applications/nuance/controller/NuanceItemViewController.php index 9c7401cafe..d325afbd29 100644 --- a/src/applications/nuance/controller/NuanceItemViewController.php +++ b/src/applications/nuance/controller/NuanceItemViewController.php @@ -3,25 +3,84 @@ final class NuanceItemViewController extends NuanceController { public function handleRequest(AphrontRequest $request) { - $viewer = $request->getViewer(); + $viewer = $this->getViewer(); $id = $request->getURIData('id'); $item = id(new NuanceItemQuery()) ->setViewer($viewer) ->withIDs(array($id)) ->executeOne(); - if (!$item) { return new Aphront404Response(); } + $title = pht('Item %d', $item->getID()); + $crumbs = $this->buildApplicationCrumbs(); - $title = 'TODO'; + $crumbs->addTextCrumb($title); + + $properties = $this->buildPropertyView($item); + $actions = $this->buildActionView($item); + $properties->setActionList($actions); + + $box = id(new PHUIObjectBoxView()) + ->setHeaderText($title) + ->addPropertyList($properties); return $this->buildApplicationPage( - $crumbs, + array( + $crumbs, + $box, + ), array( 'title' => $title, )); } + + private function buildPropertyView(NuanceItem $item) { + $viewer = $this->getViewer(); + + $properties = id(new PHUIPropertyListView()) + ->setUser($viewer) + ->setObject($item); + + $properties->addProperty( + pht('Date Created'), + phabricator_datetime($item->getDateCreated(), $viewer)); + + $source = $item->getSource(); + $definition = $source->requireDefinition(); + + $definition->renderItemViewProperties( + $viewer, + $item, + $properties); + + return $properties; + } + + private function buildActionView(NuanceItem $item) { + $viewer = $this->getViewer(); + $id = $item->getID(); + + $actions = id(new PhabricatorActionListView()) + ->setUser($viewer); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $item, + PhabricatorPolicyCapability::CAN_EDIT); + + $actions->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Edit Item')) + ->setIcon('fa-pencil') + ->setHref($this->getApplicationURI("item/edit/{$id}/")) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + return $actions; + } + + } diff --git a/src/applications/nuance/phid/NuanceRequestorPHIDType.php b/src/applications/nuance/phid/NuanceRequestorPHIDType.php index 0e08322a80..2cf06bbb10 100644 --- a/src/applications/nuance/phid/NuanceRequestorPHIDType.php +++ b/src/applications/nuance/phid/NuanceRequestorPHIDType.php @@ -29,7 +29,9 @@ final class NuanceRequestorPHIDType extends PhabricatorPHIDType { foreach ($handles as $phid => $handle) { $requestor = $objects[$phid]; - $handle->setName($requestor->getBestName()); + // TODO: This is currently useless and should be far more informative. + $handle->setName(pht('Requestor %d', $requestor->getID())); + $handle->setURI($requestor->getURI()); } } diff --git a/src/applications/nuance/query/NuanceItemQuery.php b/src/applications/nuance/query/NuanceItemQuery.php index cc3f79c915..fbcac6e5b7 100644 --- a/src/applications/nuance/query/NuanceItemQuery.php +++ b/src/applications/nuance/query/NuanceItemQuery.php @@ -17,25 +17,42 @@ final class NuanceItemQuery return $this; } - public function withSourcePHIDs($source_phids) { + public function withSourcePHIDs(array $source_phids) { $this->sourcePHIDs = $source_phids; return $this; } + public function newResultObject() { + return new NuanceItem(); + } + protected function loadPage() { - $table = new NuanceItem(); - $conn = $table->establishConnection('r'); + return $this->loadStandardPage($this->newResultObject()); + } - $data = queryfx_all( - $conn, - '%Q FROM %T %Q %Q %Q', - $this->buildSelectClause($conn), - $table->getTableName(), - $this->buildWhereClause($conn), - $this->buildOrderClause($conn), - $this->buildLimitClause($conn)); + protected function willFilterPage(array $items) { + $source_phids = mpull($items, 'getSourcePHID'); - return $table->loadAllFromArray($data); + // NOTE: We always load sources, even if the viewer can't formally see + // them. If they can see the item, they're allowed to be aware of the + // source in some sense. + $sources = id(new NuanceSourceQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withPHIDs($source_phids) + ->execute(); + $sources = mpull($sources, null, 'getPHID'); + + foreach ($items as $key => $item) { + $source = idx($sources, $item->getSourcePHID()); + if (!$source) { + $this->didRejectResult($items[$key]); + unset($items[$key]); + continue; + } + $item->attachSource($source); + } + + return $items; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { diff --git a/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php b/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php index 4cc3d7c610..607b0eda6c 100644 --- a/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php +++ b/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php @@ -61,7 +61,7 @@ final class NuancePhabricatorFormSourceDefinition if ($request->isFormPost()) { $properties = array( - 'complaint' => (string)$request->getStr('text'), + 'complaint' => (string)$request->getStr('complaint'), ); $content_source = PhabricatorContentSource::newFromRequest($request); @@ -100,4 +100,33 @@ final class NuancePhabricatorFormSourceDefinition return $box; } + public function renderItemViewProperties( + PhabricatorUser $viewer, + NuanceItem $item, + PHUIPropertyListView $view) { + $this->renderItemCommonProperties($viewer, $item, $view); + } + + public function renderItemEditProperties( + PhabricatorUser $viewer, + NuanceItem $item, + PHUIPropertyListView $view) { + $this->renderItemCommonProperties($viewer, $item, $view); + } + + private function renderItemCommonProperties( + PhabricatorUser $viewer, + NuanceItem $item, + PHUIPropertyListView $view) { + + $complaint = $item->getNuanceProperty('complaint'); + $complaint = PhabricatorMarkupEngine::renderOneObject( + id(new PhabricatorMarkupOneOff())->setContent($complaint), + 'default', + $viewer); + + $view->addSectionHeader(pht('Complaint')); + $view->addTextContent($complaint); + } + } diff --git a/src/applications/nuance/source/NuanceSourceDefinition.php b/src/applications/nuance/source/NuanceSourceDefinition.php index 2c030badfa..06d302f587 100644 --- a/src/applications/nuance/source/NuanceSourceDefinition.php +++ b/src/applications/nuance/source/NuanceSourceDefinition.php @@ -268,6 +268,20 @@ abstract class NuanceSourceDefinition extends Phobject { return $item; } + public function renderItemViewProperties( + PhabricatorUser $viewer, + NuanceItem $item, + PHUIPropertyListView $view) { + return; + } + + public function renderItemEditProperties( + PhabricatorUser $viewer, + NuanceItem $item, + PHUIPropertyListView $view) { + return; + } + /* -( Handling Action Requests )------------------------------------------- */ diff --git a/src/applications/nuance/storage/NuanceItem.php b/src/applications/nuance/storage/NuanceItem.php index 196afc44ca..2335cd4d42 100644 --- a/src/applications/nuance/storage/NuanceItem.php +++ b/src/applications/nuance/storage/NuanceItem.php @@ -19,6 +19,8 @@ final class NuanceItem protected $mailKey; protected $dateNuanced; + private $source = self::ATTACHABLE; + public static function initializeNewItem() { return id(new NuanceItem()) ->setDateNuanced(time())