diff --git a/resources/sql/autopatches/20150823.nuance.queue.1.sql b/resources/sql/autopatches/20150823.nuance.queue.1.sql new file mode 100644 index 0000000000..bb7dbba88b --- /dev/null +++ b/resources/sql/autopatches/20150823.nuance.queue.1.sql @@ -0,0 +1 @@ +DROP TABLE {$NAMESPACE}_nuance.nuance_queueitem; diff --git a/resources/sql/autopatches/20150823.nuance.queue.2.sql b/resources/sql/autopatches/20150823.nuance.queue.2.sql new file mode 100644 index 0000000000..ed906d42fe --- /dev/null +++ b/resources/sql/autopatches/20150823.nuance.queue.2.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_nuance.nuance_item + ADD queuePHID VARBINARY(64) NOT NULL; diff --git a/resources/sql/autopatches/20150823.nuance.queue.3.sql b/resources/sql/autopatches/20150823.nuance.queue.3.sql new file mode 100644 index 0000000000..deb343a031 --- /dev/null +++ b/resources/sql/autopatches/20150823.nuance.queue.3.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_nuance.nuance_source + ADD defaultQueuePHID VARBINARY(64) NOT NULL; diff --git a/resources/sql/autopatches/20150823.nuance.queue.4.sql b/resources/sql/autopatches/20150823.nuance.queue.4.sql new file mode 100644 index 0000000000..d782b9de98 --- /dev/null +++ b/resources/sql/autopatches/20150823.nuance.queue.4.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_nuance.nuance_item + DROP dateNuanced; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index c70fc08ca0..697a5ad11b 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1288,9 +1288,9 @@ phutil_register_library_map(array( 'NuancePhabricatorFormSourceDefinition' => 'applications/nuance/source/NuancePhabricatorFormSourceDefinition.php', 'NuanceQuery' => 'applications/nuance/query/NuanceQuery.php', 'NuanceQueue' => 'applications/nuance/storage/NuanceQueue.php', + 'NuanceQueueDatasource' => 'applications/nuance/typeahead/NuanceQueueDatasource.php', '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', @@ -5080,9 +5080,9 @@ phutil_register_library_map(array( 'PhabricatorPolicyInterface', 'PhabricatorApplicationTransactionInterface', ), + 'NuanceQueueDatasource' => 'PhabricatorTypeaheadDatasource', 'NuanceQueueEditController' => 'NuanceController', 'NuanceQueueEditor' => 'PhabricatorApplicationTransactionEditor', - 'NuanceQueueItem' => 'NuanceDAO', 'NuanceQueueListController' => 'NuanceController', 'NuanceQueuePHIDType' => 'PhabricatorPHIDType', 'NuanceQueueQuery' => 'NuanceQuery', diff --git a/src/applications/nuance/controller/NuanceItemEditController.php b/src/applications/nuance/controller/NuanceItemEditController.php index bad409a801..afb84e6471 100644 --- a/src/applications/nuance/controller/NuanceItemEditController.php +++ b/src/applications/nuance/controller/NuanceItemEditController.php @@ -33,10 +33,17 @@ final class NuanceItemEditController extends NuanceController { ->setHeaderText($title) ->addPropertyList($properties); + $timeline = $this->buildTransactionTimeline( + $item, + new NuanceItemTransactionQuery()); + + $timeline->setShouldTerminate(true); + return $this->buildApplicationPage( array( $crumbs, $box, + $timeline, ), array( 'title' => $title, @@ -62,6 +69,10 @@ final class NuanceItemEditController extends NuanceController { pht('Source'), $viewer->renderHandle($item->getSourcePHID())); + $properties->addProperty( + pht('Queue'), + $viewer->renderHandle($item->getQueuePHID())); + $source = $item->getSource(); $definition = $source->requireDefinition(); diff --git a/src/applications/nuance/controller/NuanceSourceViewController.php b/src/applications/nuance/controller/NuanceSourceViewController.php index e902029b11..7b8d9e8251 100644 --- a/src/applications/nuance/controller/NuanceSourceViewController.php +++ b/src/applications/nuance/controller/NuanceSourceViewController.php @@ -13,7 +13,7 @@ final class NuanceSourceViewController extends NuanceController { return new Aphront404Response(); } - $source_phid = $source->getPHID(); + $source_id = $source->getID(); $timeline = $this->buildTransactionTimeline( $source, @@ -34,10 +34,29 @@ final class NuanceSourceViewController extends NuanceController { $crumbs->addTextCrumb($title); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $source, + PhabricatorPolicyCapability::CAN_EDIT); + + $routing_list = id(new PHUIPropertyListView()) + ->addProperty( + pht('Default Queue'), + $viewer->renderHandle($source->getDefaultQueuePHID())); + + $routing_header = id(new PHUIHeaderView()) + ->setHeader(pht('Routing Rules')); + + $routing = id(new PHUIObjectBoxView()) + ->setHeader($routing_header) + ->addPropertyList($routing_list); + return $this->buildApplicationPage( array( $crumbs, $box, + $routing, $timeline, ), array( diff --git a/src/applications/nuance/editor/NuanceItemEditor.php b/src/applications/nuance/editor/NuanceItemEditor.php index 49382f5350..a331a65196 100644 --- a/src/applications/nuance/editor/NuanceItemEditor.php +++ b/src/applications/nuance/editor/NuanceItemEditor.php @@ -18,6 +18,7 @@ final class NuanceItemEditor $types[] = NuanceItemTransaction::TYPE_SOURCE; $types[] = NuanceItemTransaction::TYPE_REQUESTOR; $types[] = NuanceItemTransaction::TYPE_PROPERTY; + $types[] = NuanceItemTransaction::TYPE_QUEUE; $types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorTransactions::TYPE_COMMENT; @@ -38,6 +39,8 @@ final class NuanceItemEditor return $object->getSourcePHID(); case NuanceItemTransaction::TYPE_OWNER: return $object->getOwnerPHID(); + case NuanceItemTransaction::TYPE_QUEUE: + return $object->getQueuePHID(); case NuanceItemTransaction::TYPE_PROPERTY: $key = $xaction->getMetadataValue( NuanceItemTransaction::PROPERTY_KEY); @@ -56,6 +59,7 @@ final class NuanceItemEditor case NuanceItemTransaction::TYPE_SOURCE: case NuanceItemTransaction::TYPE_OWNER: case NuanceItemTransaction::TYPE_PROPERTY: + case NuanceItemTransaction::TYPE_QUEUE: return $xaction->getNewValue(); } @@ -76,6 +80,9 @@ final class NuanceItemEditor case NuanceItemTransaction::TYPE_OWNER: $object->setOwnerPHID($xaction->getNewValue()); break; + case NuanceItemTransaction::TYPE_QUEUE: + $object->setQueuePHID($xaction->getNewValue()); + break; case NuanceItemTransaction::TYPE_PROPERTY: $key = $xaction->getMetadataValue( NuanceItemTransaction::PROPERTY_KEY); @@ -93,6 +100,7 @@ final class NuanceItemEditor case NuanceItemTransaction::TYPE_SOURCE: case NuanceItemTransaction::TYPE_OWNER: case NuanceItemTransaction::TYPE_PROPERTY: + case NuanceItemTransaction::TYPE_QUEUE: return; } diff --git a/src/applications/nuance/editor/NuanceSourceEditor.php b/src/applications/nuance/editor/NuanceSourceEditor.php index 1e913b33a4..233b2ae163 100644 --- a/src/applications/nuance/editor/NuanceSourceEditor.php +++ b/src/applications/nuance/editor/NuanceSourceEditor.php @@ -15,6 +15,7 @@ final class NuanceSourceEditor $types = parent::getTransactionTypes(); $types[] = NuanceSourceTransaction::TYPE_NAME; + $types[] = NuanceSourceTransaction::TYPE_DEFAULT_QUEUE; $types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorTransactions::TYPE_COMMENT; @@ -31,6 +32,8 @@ final class NuanceSourceEditor switch ($xaction->getTransactionType()) { case NuanceSourceTransaction::TYPE_NAME: return $object->getName(); + case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE: + return $object->getDefaultQueuePHID(); } return parent::getCustomTransactionOldValue($object, $xaction); @@ -42,6 +45,7 @@ final class NuanceSourceEditor switch ($xaction->getTransactionType()) { case NuanceSourceTransaction::TYPE_NAME: + case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE: return $xaction->getNewValue(); } @@ -56,6 +60,9 @@ final class NuanceSourceEditor case NuanceSourceTransaction::TYPE_NAME: $object->setName($xaction->getNewValue()); break; + case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE: + $object->setDefaultQueuePHID($xaction->getNewValue()); + break; } } @@ -65,6 +72,7 @@ final class NuanceSourceEditor switch ($xaction->getTransactionType()) { case NuanceSourceTransaction::TYPE_NAME: + case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE: return; } @@ -95,6 +103,19 @@ final class NuanceSourceEditor $errors[] = $error; } break; + case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE: + foreach ($xactions as $xaction) { + if (!$xaction->getNewValue()) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Required'), + pht('Sources must have a default queue.'), + $xaction); + $error->setIsMissingFieldError(true); + $errors[] = $error; + } + } + break; } return $errors; diff --git a/src/applications/nuance/source/NuanceSourceDefinition.php b/src/applications/nuance/source/NuanceSourceDefinition.php index 06d302f587..4b37e39845 100644 --- a/src/applications/nuance/source/NuanceSourceDefinition.php +++ b/src/applications/nuance/source/NuanceSourceDefinition.php @@ -169,25 +169,39 @@ abstract class NuanceSourceDefinition extends Phobject { $form = $this->augmentEditForm($form, $ex); + $default_phid = $source->getDefaultQueuePHID(); + if ($default_phid) { + $default_queues = array($default_phid); + } else { + $default_queues = array(); + } + $form + ->appendControl( + id(new AphrontFormTokenizerControl()) + ->setLabel(pht('Default Queue')) + ->setName('defaultQueuePHIDs') + ->setLimit(1) + ->setDatasource(new NuanceQueueDatasource()) + ->setValue($default_queues)) ->appendChild( id(new AphrontFormPolicyControl()) - ->setUser($user) - ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) - ->setPolicyObject($source) - ->setPolicies($policies) - ->setName('viewPolicy')) + ->setUser($user) + ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) + ->setPolicyObject($source) + ->setPolicies($policies) + ->setName('viewPolicy')) ->appendChild( id(new AphrontFormPolicyControl()) - ->setUser($user) - ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) - ->setPolicyObject($source) - ->setPolicies($policies) - ->setName('editPolicy')) + ->setUser($user) + ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) + ->setPolicyObject($source) + ->setPolicies($policies) + ->setName('editPolicy')) ->appendChild( id(new AphrontFormSubmitControl()) - ->addCancelButton($source->getURI()) - ->setValue(pht('Save'))); + ->addCancelButton($source->getURI()) + ->setValue(pht('Save'))); return $form; } @@ -213,13 +227,19 @@ abstract class NuanceSourceDefinition extends Phobject { $transactions[] = id(new NuanceSourceTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY) ->setNewValue($request->getStr('editPolicy')); + $transactions[] = id(new NuanceSourceTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) ->setNewValue($request->getStr('viewPolicy')); - $transactions[] = id(new NuanceSourceTransaction()) + + $transactions[] = id(new NuanceSourceTransaction()) ->setTransactionType(NuanceSourceTransaction::TYPE_NAME) ->setNewvalue($request->getStr('name')); + $transactions[] = id(new NuanceSourceTransaction()) + ->setTransactionType(NuanceSourceTransaction::TYPE_DEFAULT_QUEUE) + ->setNewvalue(head($request->getArr('defaultQueuePHIDs'))); + return $transactions; } @@ -251,6 +271,12 @@ abstract class NuanceSourceDefinition extends Phobject { ->setTransactionType(NuanceItemTransaction::TYPE_REQUESTOR) ->setNewValue($requestor->getPHID()); + // TODO: Eventually, apply real routing rules. For now, just put everything + // in the default queue for the source. + $xactions[] = id(new NuanceItemTransaction()) + ->setTransactionType(NuanceItemTransaction::TYPE_QUEUE) + ->setNewValue($source->getDefaultQueuePHID()); + foreach ($properties as $key => $property) { $xactions[] = id(new NuanceItemTransaction()) ->setTransactionType(NuanceItemTransaction::TYPE_PROPERTY) diff --git a/src/applications/nuance/storage/NuanceItem.php b/src/applications/nuance/storage/NuanceItem.php index 2335cd4d42..7ce517333f 100644 --- a/src/applications/nuance/storage/NuanceItem.php +++ b/src/applications/nuance/storage/NuanceItem.php @@ -17,13 +17,12 @@ final class NuanceItem protected $sourceLabel; protected $data = array(); protected $mailKey; - protected $dateNuanced; + protected $queuePHID; private $source = self::ATTACHABLE; public static function initializeNewItem() { return id(new NuanceItem()) - ->setDateNuanced(time()) ->setStatus(self::STATUS_OPEN); } @@ -38,17 +37,19 @@ final class NuanceItem 'sourceLabel' => 'text255?', 'status' => 'uint32', 'mailKey' => 'bytes20', - 'dateNuanced' => 'epoch', ), self::CONFIG_KEY_SCHEMA => array( 'key_source' => array( - 'columns' => array('sourcePHID', 'status', 'dateNuanced', 'id'), + 'columns' => array('sourcePHID', 'status'), ), 'key_owner' => array( - 'columns' => array('ownerPHID', 'status', 'dateNuanced', 'id'), + 'columns' => array('ownerPHID', 'status'), ), - 'key_contacter' => array( - 'columns' => array('requestorPHID', 'status', 'dateNuanced', 'id'), + 'key_requestor' => array( + 'columns' => array('requestorPHID', 'status'), + ), + 'key_queue' => array( + 'columns' => array('queuePHID', 'status'), ), ), ) + parent::getConfiguration(); @@ -144,7 +145,6 @@ final class NuanceItem 'sourceLabel' => $this->getSourceLabel(), 'dateCreated' => $this->getDateCreated(), 'dateModified' => $this->getDateModified(), - 'dateNuanced' => $this->getDateNuanced(), ); } diff --git a/src/applications/nuance/storage/NuanceItemTransaction.php b/src/applications/nuance/storage/NuanceItemTransaction.php index e6fac7cde7..183596402f 100644 --- a/src/applications/nuance/storage/NuanceItemTransaction.php +++ b/src/applications/nuance/storage/NuanceItemTransaction.php @@ -9,6 +9,7 @@ final class NuanceItemTransaction const TYPE_REQUESTOR = 'nuance.item.requestor'; const TYPE_SOURCE = 'nuance.item.source'; const TYPE_PROPERTY = 'nuance.item.property'; + const TYPE_QUEUE = 'nuance.item.queue'; public function getApplicationTransactionType() { return NuanceItemPHIDType::TYPECONST; @@ -18,4 +19,55 @@ final class NuanceItemTransaction return new NuanceItemTransactionComment(); } + public function shouldHide() { + $old = $this->getOldValue(); + $type = $this->getTransactionType(); + + switch ($type) { + case self::TYPE_REQUESTOR: + case self::TYPE_SOURCE: + return ($old === null); + } + + return parent::shouldHide(); + } + + public function getRequiredHandlePHIDs() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + $type = $this->getTransactionType(); + + $phids = parent::getRequiredHandlePHIDs(); + switch ($type) { + case self::TYPE_QUEUE: + if ($old) { + $phids[] = $old; + } + if ($new) { + $phids[] = $new; + } + break; + } + + return $phids; + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + $type = $this->getTransactionType(); + + $author_phid = $this->getAuthorPHID(); + + switch ($type) { + case self::TYPE_QUEUE: + return pht( + '%s routed this item to the %s queue.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($new)); + } + + return parent::getTitle(); + } + } diff --git a/src/applications/nuance/storage/NuanceQueueItem.php b/src/applications/nuance/storage/NuanceQueueItem.php deleted file mode 100644 index 77a80ff181..0000000000 --- a/src/applications/nuance/storage/NuanceQueueItem.php +++ /dev/null @@ -1,34 +0,0 @@ - array( - 'itemStatus' => 'uint32', - 'itemDateNuanced' => 'epoch', - ), - self::CONFIG_KEY_SCHEMA => array( - 'key_one_per_queue' => array( - 'columns' => array('itemPHID', 'queuePHID'), - 'unique' => true, - ), - 'key_queue' => array( - 'columns' => array( - 'queuePHID', - 'itemStatus', - 'itemDateNuanced', - 'id', - ), - ), - ), - ) + parent::getConfiguration(); - } - -} diff --git a/src/applications/nuance/storage/NuanceQueueTransaction.php b/src/applications/nuance/storage/NuanceQueueTransaction.php index 46ace23cd1..c1630cdbd8 100644 --- a/src/applications/nuance/storage/NuanceQueueTransaction.php +++ b/src/applications/nuance/storage/NuanceQueueTransaction.php @@ -12,4 +12,34 @@ final class NuanceQueueTransaction extends NuanceTransaction { return new NuanceQueueTransactionComment(); } + public function shouldHide() { + $old = $this->getOldValue(); + $type = $this->getTransactionType(); + + switch ($type) { + case self::TYPE_NAME: + return ($old === null); + } + + return parent::shouldHide(); + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + $type = $this->getTransactionType(); + + $author_phid = $this->getAuthorPHID(); + + switch ($type) { + case self::TYPE_NAME: + return pht( + '%s renamed this queue from "%s" to "%s".', + $this->renderHandleLink($author_phid), + $old, + $new); + } + + return parent::getTitle(); + } } diff --git a/src/applications/nuance/storage/NuanceSource.php b/src/applications/nuance/storage/NuanceSource.php index 6ac04ba33a..4ce61289f9 100644 --- a/src/applications/nuance/storage/NuanceSource.php +++ b/src/applications/nuance/storage/NuanceSource.php @@ -11,6 +11,7 @@ final class NuanceSource extends NuanceDAO protected $mailKey; protected $viewPolicy; protected $editPolicy; + protected $defaultQueuePHID; private $definition; diff --git a/src/applications/nuance/storage/NuanceSourceTransaction.php b/src/applications/nuance/storage/NuanceSourceTransaction.php index 86f52104d7..0b18c81184 100644 --- a/src/applications/nuance/storage/NuanceSourceTransaction.php +++ b/src/applications/nuance/storage/NuanceSourceTransaction.php @@ -3,7 +3,8 @@ final class NuanceSourceTransaction extends NuanceTransaction { - const TYPE_NAME = 'name-source'; + const TYPE_NAME = 'source.name'; + const TYPE_DEFAULT_QUEUE = 'source.queue.default'; public function getApplicationTransactionType() { return NuanceSourcePHIDType::TYPECONST; @@ -13,27 +14,63 @@ final class NuanceSourceTransaction return new NuanceSourceTransactionComment(); } - public function getTitle() { + public function shouldHide() { $old = $this->getOldValue(); $new = $this->getNewValue(); - $author_phid = $this->getAuthorPHID(); + $type = $this->getTransactionType(); - switch ($this->getTransactionType()) { + switch ($type) { + case self::TYPE_DEFAULT_QUEUE: + return !$old; case self::TYPE_NAME: - if ($old === null) { - return pht( - '%s created this source.', - $this->renderHandleLink($author_phid)); - } else { - return pht( - '%s renamed this source from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $old, - $new); + return ($old === null); + } + + return parent::shouldHide(); + } + + public function getRequiredHandlePHIDs() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + $type = $this->getTransactionType(); + + $phids = parent::getRequiredHandlePHIDs(); + switch ($type) { + case self::TYPE_DEFAULT_QUEUE: + if ($old) { + $phids[] = $old; + } + if ($new) { + $phids[] = $new; } break; } + return $phids; + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + $type = $this->getTransactionType(); + $author_phid = $this->getAuthorPHID(); + + switch ($type) { + case self::TYPE_DEFAULT_QUEUE: + return pht( + '%s changed the default queue from %s to %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($old), + $this->renderHandleLink($new)); + case self::TYPE_NAME: + return pht( + '%s renamed this source from "%s" to "%s".', + $this->renderHandleLink($author_phid), + $old, + $new); + } + + return parent::getTitle(); } } diff --git a/src/applications/nuance/typeahead/NuanceQueueDatasource.php b/src/applications/nuance/typeahead/NuanceQueueDatasource.php new file mode 100644 index 0000000000..15b01fcecd --- /dev/null +++ b/src/applications/nuance/typeahead/NuanceQueueDatasource.php @@ -0,0 +1,38 @@ +getViewer(); + $raw_query = $this->getRawQuery(); + + $results = array(); + + // TODO: Make this use real typeahead logic. + $query = new NuanceQueueQuery(); + $queues = $this->executeQuery($query); + + foreach ($queues as $queue) { + $results[] = id(new PhabricatorTypeaheadResult()) + ->setName($queue->getName()) + ->setURI('/nuance/queue/'.$queue->getID().'/') + ->setPHID($queue->getPHID()); + } + + return $this->filterResultsAgainstTokens($results); + } + +}