Page MenuHomestyx hydra

No OneTemporary

diff --git a/resources/sql/autopatches/20170524.nuance.01.command.sql b/resources/sql/autopatches/20170524.nuance.01.command.sql
new file mode 100644
index 0000000000..529756e748
--- /dev/null
+++ b/resources/sql/autopatches/20170524.nuance.01.command.sql
@@ -0,0 +1,8 @@
+ALTER TABLE {$NAMESPACE}_nuance.nuance_itemcommand
+ ADD dateCreated INT UNSIGNED NOT NULL;
+
+ALTER TABLE {$NAMESPACE}_nuance.nuance_itemcommand
+ ADD dateModified INT UNSIGNED NOT NULL;
+
+ALTER TABLE {$NAMESPACE}_nuance.nuance_itemcommand
+ ADD queuePHID VARBINARY(64);
diff --git a/resources/sql/autopatches/20170524.nuance.02.commandstatus.sql b/resources/sql/autopatches/20170524.nuance.02.commandstatus.sql
new file mode 100644
index 0000000000..14f57af053
--- /dev/null
+++ b/resources/sql/autopatches/20170524.nuance.02.commandstatus.sql
@@ -0,0 +1,5 @@
+ALTER TABLE {$NAMESPACE}_nuance.nuance_itemcommand
+ ADD status VARCHAR(64) NOT NULL;
+
+UPDATE {$NAMESPACE}_nuance.nuance_itemcommand
+ SET status = 'done' WHERE status = '';
diff --git a/src/applications/nuance/application/PhabricatorNuanceApplication.php b/src/applications/nuance/application/PhabricatorNuanceApplication.php
index 9f99c9e5bd..cd268dd95e 100644
--- a/src/applications/nuance/application/PhabricatorNuanceApplication.php
+++ b/src/applications/nuance/application/PhabricatorNuanceApplication.php
@@ -1,79 +1,81 @@
<?php
final class PhabricatorNuanceApplication extends PhabricatorApplication {
public function getName() {
return pht('Nuance');
}
public function getIcon() {
return 'fa-fax';
}
public function getTitleGlyph() {
return "\xE2\x98\x8E";
}
public function isPrototype() {
return true;
}
public function isLaunchable() {
// Try to hide this even more for now.
return false;
}
public function getBaseURI() {
return '/nuance/';
}
public function getShortDescription() {
return pht('High-Volume Task Queues');
}
public function getRoutes() {
return array(
'/nuance/' => array(
'' => 'NuanceConsoleController',
'item/' => array(
$this->getQueryRoutePattern() => 'NuanceItemListController',
'view/(?P<id>[1-9]\d*)/' => 'NuanceItemViewController',
'manage/(?P<id>[1-9]\d*)/' => 'NuanceItemManageController',
'action/(?P<id>[1-9]\d*)/(?P<action>[^/]+)/'
=> 'NuanceItemActionController',
),
'source/' => array(
$this->getQueryRoutePattern() => 'NuanceSourceListController',
$this->getEditRoutePattern('edit/') => 'NuanceSourceEditController',
'view/(?P<id>[1-9]\d*)/' => 'NuanceSourceViewController',
),
'queue/' => array(
$this->getQueryRoutePattern() => 'NuanceQueueListController',
$this->getEditRoutePattern('edit/') => 'NuanceQueueEditController',
'view/(?P<id>[1-9]\d*)/' => 'NuanceQueueViewController',
'work/(?P<id>[1-9]\d*)/' => 'NuanceQueueWorkController',
+ 'action/(?P<queueID>[1-9]\d*)/(?P<action>[^/]+)/(?P<id>[1-9]\d*)/'
+ => 'NuanceItemActionController',
),
),
'/action/' => array(
'(?P<id>[1-9]\d*)/(?P<path>.*)' => 'NuanceSourceActionController',
),
);
}
protected function getCustomCapabilities() {
return array(
NuanceSourceDefaultViewCapability::CAPABILITY => array(
'caption' => pht('Default view policy for newly created sources.'),
'template' => NuanceSourcePHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
NuanceSourceDefaultEditCapability::CAPABILITY => array(
'caption' => pht('Default edit policy for newly created sources.'),
'template' => NuanceSourcePHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
),
NuanceSourceManageCapability::CAPABILITY => array(),
);
}
}
diff --git a/src/applications/nuance/controller/NuanceItemActionController.php b/src/applications/nuance/controller/NuanceItemActionController.php
index c64ac5f6ac..c6dc139b11 100644
--- a/src/applications/nuance/controller/NuanceItemActionController.php
+++ b/src/applications/nuance/controller/NuanceItemActionController.php
@@ -1,26 +1,90 @@
<?php
final class NuanceItemActionController extends NuanceController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$id = $request->getURIData('id');
+ if (!$request->validateCSRF()) {
+ return new Aphront400Response();
+ }
+
+ // NOTE: This controller can be reached from an individual item (usually
+ // by a user) or while working through a queue (usually by staff). When
+ // a command originates from a queue, the URI will have a queue ID.
+
$item = id(new NuanceItemQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$item) {
return new Aphront404Response();
}
+ $cancel_uri = $item->getURI();
+
+ $queue_id = $request->getURIData('queueID');
+ $queue = null;
+ if ($queue_id) {
+ $queue = id(new NuanceQueueQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($queue_id))
+ ->executeOne();
+ if (!$queue) {
+ return new Aphront404Response();
+ }
+
+ $item_queue = $item->getQueue();
+ if (!$item_queue || ($item_queue->getPHID() != $queue->getPHID())) {
+ return $this->newDialog()
+ ->setTitle(pht('Wrong Queue'))
+ ->appendParagraph(
+ pht(
+ 'You are trying to act on this item from the wrong queue: it '.
+ 'is currently in a different queue.'))
+ ->addCancelButton($cancel_uri);
+ }
+ }
+
$action = $request->getURIData('action');
$impl = $item->getImplementation();
$impl->setViewer($viewer);
$impl->setController($this);
- return $impl->buildActionResponse($item, $action);
+ $command = NuanceItemCommand::initializeNewCommand()
+ ->setItemPHID($item->getPHID())
+ ->setAuthorPHID($viewer->getPHID())
+ ->setCommand($action);
+
+ if ($queue) {
+ $command->setQueuePHID($queue->getPHID());
+ }
+
+ $command->save();
+
+ // TODO: Here, we should check if the command should be tried immediately,
+ // and just defer it to the daemons if not. If we're going to try to apply
+ // the command directly, we should first acquire the worker lock. If we
+ // can not, we should defer the command even if it's an immediate command.
+ // For the moment, skip all this stuff by deferring unconditionally.
+
+ $should_defer = true;
+ if ($should_defer) {
+ $item->scheduleUpdate();
+ } else {
+ // ...
+ }
+
+ if ($queue) {
+ $done_uri = $queue->getWorkURI();
+ } else {
+ $done_uri = $item->getURI();
+ }
+
+ return id(new AphrontRedirectResponse())
+ ->setURI($done_uri);
}
}
diff --git a/src/applications/nuance/controller/NuanceItemViewController.php b/src/applications/nuance/controller/NuanceItemViewController.php
index 7ef5d06682..a902dc3b06 100644
--- a/src/applications/nuance/controller/NuanceItemViewController.php
+++ b/src/applications/nuance/controller/NuanceItemViewController.php
@@ -1,126 +1,92 @@
<?php
final class NuanceItemViewController extends NuanceController {
public function handleRequest(AphrontRequest $request) {
$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());
$name = $item->getDisplayName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Items'),
$this->getApplicationURI('item/'));
$crumbs->addTextCrumb($title);
$crumbs->setBorder(true);
$curtain = $this->buildCurtain($item);
$content = $this->buildContent($item);
- $commands = $this->buildCommands($item);
$timeline = $this->buildTransactionTimeline(
$item,
new NuanceItemTransactionQuery());
$main = array(
- $commands,
$content,
$timeline,
);
$header = id(new PHUIHeaderView())
->setHeader($name);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn($main);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildCurtain(NuanceItem $item) {
$viewer = $this->getViewer();
$id = $item->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$item,
PhabricatorPolicyCapability::CAN_EDIT);
$curtain = $this->newCurtainView($item);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Manage Item'))
->setIcon('fa-cogs')
->setHref($this->getApplicationURI("item/manage/{$id}/")));
$impl = $item->getImplementation();
$impl->setViewer($viewer);
foreach ($impl->getItemActions($item) as $action) {
$curtain->addAction($action);
}
foreach ($impl->getItemCurtainPanels($item) as $panel) {
$curtain->addPanel($panel);
}
return $curtain;
}
private function buildContent(NuanceItem $item) {
$viewer = $this->getViewer();
$impl = $item->getImplementation();
$impl->setViewer($viewer);
return $impl->buildItemView($item);
}
- private function buildCommands(NuanceItem $item) {
- $viewer = $this->getViewer();
-
- $commands = id(new NuanceItemCommandQuery())
- ->setViewer($viewer)
- ->withItemPHIDs(array($item->getPHID()))
- ->execute();
- $commands = msort($commands, 'getID');
-
- if (!$commands) {
- return null;
- }
-
- $rows = array();
- foreach ($commands as $command) {
- $rows[] = array(
- $command->getCommand(),
- );
- }
-
- $table = id(new AphrontTableView($rows))
- ->setHeaders(
- array(
- pht('Command'),
- ));
-
- return id(new PHUIObjectBoxView())
- ->setHeaderText(pht('Pending Commands'))
- ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
- ->setTable($table);
- }
-
}
diff --git a/src/applications/nuance/controller/NuanceQueueWorkController.php b/src/applications/nuance/controller/NuanceQueueWorkController.php
index fb979c4e02..8703a69334 100644
--- a/src/applications/nuance/controller/NuanceQueueWorkController.php
+++ b/src/applications/nuance/controller/NuanceQueueWorkController.php
@@ -1,123 +1,186 @@
<?php
final class NuanceQueueWorkController
extends NuanceQueueController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$queue = id(new NuanceQueueQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->executeOne();
if (!$queue) {
return new Aphront404Response();
}
$title = $queue->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Queues'), $this->getApplicationURI('queue/'));
$crumbs->addTextCrumb($queue->getName(), $queue->getURI());
$crumbs->addTextCrumb(pht('Work'));
$crumbs->setBorder(true);
// For now, just pick the first open item.
$items = id(new NuanceItemQuery())
->setViewer($viewer)
->withQueuePHIDs(
array(
$queue->getPHID(),
))
->withStatuses(
array(
NuanceItem::STATUS_OPEN,
))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->setLimit(5)
->execute();
if (!$items) {
return $this->newDialog()
->setTitle(pht('Queue Empty'))
->appendParagraph(
pht(
'This queue has no open items which you have permission to '.
'work on.'))
->addCancelButton($queue->getURI());
}
$item = head($items);
$curtain = $this->buildCurtain($queue, $item);
$timeline = $this->buildTransactionTimeline(
$item,
new NuanceItemTransactionQuery());
$timeline->setShouldTerminate(true);
$impl = $item->getImplementation()
->setViewer($viewer);
+ $commands = $this->buildCommands($item);
$work_content = $impl->buildItemWorkView($item);
$view = id(new PHUITwoColumnView())
->setCurtain($curtain)
->setMainColumn(
array(
+ $commands,
$work_content,
$timeline,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildCurtain(NuanceQueue $queue, NuanceItem $item) {
$viewer = $this->getViewer();
$id = $queue->getID();
$curtain = $this->newCurtainView();
$impl = $item->getImplementation();
$commands = $impl->buildWorkCommands($item);
foreach ($commands as $command) {
$command_key = $command->getCommandKey();
$item_id = $item->getID();
+ $action_uri = "queue/action/{$id}/{$command_key}/{$item_id}/";
+ $action_uri = $this->getApplicationURI($action_uri);
+
$curtain->addAction(
id(new PhabricatorActionView())
->setName($command->getName())
->setIcon($command->getIcon())
- ->setHref("queue/command/{$id}/{$command_key}/{$item_id}/"))
- ->setWorkflow(true);
+ ->setHref($action_uri)
+ ->setWorkflow(true));
}
$curtain->addAction(
id(new PhabricatorActionView())
->setType(PhabricatorActionView::TYPE_DIVIDER));
$curtain->addAction(
id(new PhabricatorActionView())
->setType(PhabricatorActionView::TYPE_LABEL)
->setName(pht('Queue Actions')));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Manage Queue'))
->setIcon('fa-cog')
->setHref($this->getApplicationURI("queue/view/{$id}/")));
return $curtain;
}
+ private function buildCommands(NuanceItem $item) {
+ $viewer = $this->getViewer();
+
+ $commands = id(new NuanceItemCommandQuery())
+ ->setViewer($viewer)
+ ->withItemPHIDs(array($item->getPHID()))
+ ->withStatuses(
+ array(
+ NuanceItemCommand::STATUS_ISSUED,
+ NuanceItemCommand::STATUS_EXECUTING,
+ NuanceItemCommand::STATUS_FAILED,
+ ))
+ ->execute();
+ $commands = msort($commands, 'getID');
+
+ if (!$commands) {
+ return null;
+ }
+
+ $rows = array();
+ foreach ($commands as $command) {
+ $icon = $command->getStatusIcon();
+ $color = $command->getStatusColor();
+
+ $rows[] = array(
+ $command->getID(),
+ id(new PHUIIconView())
+ ->setIcon($icon, $color),
+ $viewer->renderHandle($command->getAuthorPHID()),
+ $command->getCommand(),
+ phabricator_datetime($command->getDateCreated(), $viewer),
+ );
+ }
+
+ $table = id(new AphrontTableView($rows))
+ ->setHeaders(
+ array(
+ pht('ID'),
+ null,
+ pht('Actor'),
+ pht('Command'),
+ pht('Date'),
+ ))
+ ->setColumnClasses(
+ array(
+ null,
+ 'icon',
+ null,
+ 'pri',
+ 'wide right',
+ ));
+
+ return id(new PHUIObjectBoxView())
+ ->setHeaderText(pht('Pending Commands'))
+ ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
+ ->setTable($table);
+ }
+
}
diff --git a/src/applications/nuance/item/NuanceFormItemType.php b/src/applications/nuance/item/NuanceFormItemType.php
index 401d20fd87..cbdde0e89c 100644
--- a/src/applications/nuance/item/NuanceFormItemType.php
+++ b/src/applications/nuance/item/NuanceFormItemType.php
@@ -1,50 +1,54 @@
<?php
final class NuanceFormItemType
extends NuanceItemType {
const ITEMTYPE = 'form.item';
public function getItemTypeDisplayName() {
return pht('Form');
}
public function getItemDisplayName(NuanceItem $item) {
return pht('Complaint');
}
protected function newWorkCommands(NuanceItem $item) {
return array(
$this->newCommand('trash')
->setIcon('fa-trash')
->setName(pht('Throw In Trash')),
);
}
protected function newItemView(NuanceItem $item) {
$viewer = $this->getViewer();
$content = $item->getItemProperty('complaint');
$content_view = id(new PHUIRemarkupView($viewer, $content))
->setContextObject($item);
$content_section = id(new PHUIPropertyListView())
->addTextContent(
phutil_tag(
'div',
array(
'class' => 'phabricator-remarkup',
),
$content_view));
$content_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Complaint'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($content_section);
return array(
$content_box,
);
}
+ protected function handleAction(NuanceItem $item, $action) {
+ return null;
+ }
+
}
diff --git a/src/applications/nuance/item/NuanceItemType.php b/src/applications/nuance/item/NuanceItemType.php
index c0c78c9efa..de64977cb3 100644
--- a/src/applications/nuance/item/NuanceItemType.php
+++ b/src/applications/nuance/item/NuanceItemType.php
@@ -1,174 +1,168 @@
<?php
abstract class NuanceItemType
extends Phobject {
private $viewer;
private $controller;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setController(PhabricatorController $controller) {
$this->controller = $controller;
return $this;
}
public function getController() {
return $this->controller;
}
public function canUpdateItems() {
return false;
}
final public function buildItemView(NuanceItem $item) {
return $this->newItemView($item);
}
final public function buildItemWorkView(NuanceItem $item) {
return $this->newItemView($item);
}
protected function newItemView(NuanceItem $item) {
return null;
}
public function getItemTypeDisplayIcon() {
return null;
}
public function getItemActions(NuanceItem $item) {
return array();
}
public function getItemCurtainPanels(NuanceItem $item) {
return array();
}
abstract public function getItemTypeDisplayName();
abstract public function getItemDisplayName(NuanceItem $item);
final public function updateItem(NuanceItem $item) {
if (!$this->canUpdateItems()) {
throw new Exception(
pht(
'This item type ("%s", of class "%s") can not update items.',
$this->getItemTypeConstant(),
get_class($this)));
}
$this->updateItemFromSource($item);
}
protected function updateItemFromSource(NuanceItem $item) {
throw new PhutilMethodNotImplementedException();
}
final public function getItemTypeConstant() {
return $this->getPhobjectClassConstant('ITEMTYPE', 64);
}
final public static function getAllItemTypes() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getItemTypeConstant')
->execute();
}
final protected function newItemAction(NuanceItem $item, $key) {
$id = $item->getID();
$action_uri = "/nuance/item/action/{$id}/{$key}/";
return id(new PhabricatorActionView())
->setHref($action_uri);
}
final protected function newCurtainPanel(NuanceItem $item) {
return id(new PHUICurtainPanelView());
}
final public function buildActionResponse(NuanceItem $item, $action) {
- $response = $this->handleAction($item, $action);
-
- if ($response === null) {
- return new Aphront404Response();
- }
-
- return $response;
+ return $this->handleAction($item, $action);
}
protected function handleAction(NuanceItem $item, $action) {
return null;
}
final public function buildWorkCommands(NuanceItem $item) {
return $this->newWorkCommands($item);
}
final public function applyCommand(
NuanceItem $item,
NuanceItemCommand $command) {
$result = $this->handleCommand($item, $command);
if ($result === null) {
return;
}
$xaction = id(new NuanceItemTransaction())
->setTransactionType(NuanceItemCommandTransaction::TRANSACTIONTYPE)
->setNewValue(
array(
'command' => $command->getCommand(),
'parameters' => $command->getParameters(),
'result' => $result,
));
$viewer = $this->getViewer();
// TODO: Maybe preserve the actor's original content source?
$source = PhabricatorContentSource::newForSource(
PhabricatorDaemonContentSource::SOURCECONST);
$editor = id(new NuanceItemEditor())
->setActor($viewer)
->setActingAsPHID($command->getAuthorPHID())
->setContentSource($source)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true)
->applyTransactions($item, array($xaction));
}
protected function handleCommand(
NuanceItem $item,
NuanceItemCommand $command) {
return null;
}
final protected function newContentSource(
NuanceItem $item,
$agent_phid) {
return PhabricatorContentSource::newForSource(
NuanceContentSource::SOURCECONST,
array(
'itemPHID' => $item->getPHID(),
'agentPHID' => $agent_phid,
));
}
protected function getActingAsPHID(NuanceItem $item) {
return id(new PhabricatorNuanceApplication())->getPHID();
}
protected function newCommand($command_key) {
return id(new NuanceItemCommandSpec())
->setCommandKey($command_key);
}
}
diff --git a/src/applications/nuance/query/NuanceItemCommandQuery.php b/src/applications/nuance/query/NuanceItemCommandQuery.php
index cb20610187..27137cf8f6 100644
--- a/src/applications/nuance/query/NuanceItemCommandQuery.php
+++ b/src/applications/nuance/query/NuanceItemCommandQuery.php
@@ -1,47 +1,60 @@
<?php
final class NuanceItemCommandQuery
extends NuanceQuery {
private $ids;
private $itemPHIDs;
+ private $statuses;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withItemPHIDs(array $item_phids) {
$this->itemPHIDs = $item_phids;
return $this;
}
+ public function withStatuses(array $statuses) {
+ $this->statuses = $statuses;
+ return $this;
+ }
+
public function newResultObject() {
return new NuanceItemCommand();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->itemPHIDs !== null) {
$where[] = qsprintf(
$conn,
'itemPHID IN (%Ls)',
$this->itemPHIDs);
}
+ if ($this->statuses !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'status IN (%Ls)',
+ $this->statuses);
+ }
+
return $where;
}
}
diff --git a/src/applications/nuance/storage/NuanceItemCommand.php b/src/applications/nuance/storage/NuanceItemCommand.php
index bda6860ff5..21d3792ff2 100644
--- a/src/applications/nuance/storage/NuanceItemCommand.php
+++ b/src/applications/nuance/storage/NuanceItemCommand.php
@@ -1,51 +1,105 @@
<?php
final class NuanceItemCommand
extends NuanceDAO
implements PhabricatorPolicyInterface {
+ const STATUS_ISSUED = 'issued';
+ const STATUS_EXECUTING = 'executing';
+ const STATUS_DONE = 'done';
+ const STATUS_FAILED = 'failed';
+
protected $itemPHID;
protected $authorPHID;
+ protected $queuePHID;
protected $command;
- protected $parameters;
+ protected $status;
+ protected $parameters = array();
public static function initializeNewCommand() {
- return new self();
+ return id(new self())
+ ->setStatus(self::STATUS_ISSUED);
}
protected function getConfiguration() {
return array(
- self::CONFIG_TIMESTAMPS => false,
self::CONFIG_SERIALIZATION => array(
'parameters' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'command' => 'text64',
+ 'status' => 'text64',
+ 'queuePHID' => 'phid?',
),
self::CONFIG_KEY_SCHEMA => array(
- 'key_item' => array(
- 'columns' => array('itemPHID'),
+ 'key_pending' => array(
+ 'columns' => array('itemPHID', 'status'),
),
),
) + parent::getConfiguration();
}
+ public static function getStatusMap() {
+ return array(
+ self::STATUS_ISSUED => array(
+ 'name' => pht('Issued'),
+ 'icon' => 'fa-clock-o',
+ 'color' => 'bluegrey',
+ ),
+ self::STATUS_EXECUTING => array(
+ 'name' => pht('Executing'),
+ 'icon' => 'fa-play',
+ 'color' => 'green',
+ ),
+ self::STATUS_DONE => array(
+ 'name' => pht('Done'),
+ 'icon' => 'fa-check',
+ 'color' => 'blue',
+ ),
+ self::STATUS_FAILED => array(
+ 'name' => pht('Failed'),
+ 'icon' => 'fa-times',
+ 'color' => 'red',
+ ),
+ );
+ }
+
+ private function getStatusSpec() {
+ $map = self::getStatusMap();
+ return idx($map, $this->getStatus(), array());
+ }
+
+ public function getStatusIcon() {
+ $spec = $this->getStatusSpec();
+ return idx($spec, 'icon', 'fa-question');
+ }
+
+ public function getStatusColor() {
+ $spec = $this->getStatusSpec();
+ return idx($spec, 'color', 'indigo');
+ }
+
+ public function getStatusName() {
+ $spec = $this->getStatusSpec();
+ return idx($spec, 'name', $this->getStatus());
+ }
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
return PhabricatorPolicies::getMostOpenPolicy();
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
}
diff --git a/src/applications/nuance/storage/NuanceQueue.php b/src/applications/nuance/storage/NuanceQueue.php
index 9691a42e2f..f0ba5bb45c 100644
--- a/src/applications/nuance/storage/NuanceQueue.php
+++ b/src/applications/nuance/storage/NuanceQueue.php
@@ -1,92 +1,96 @@
<?php
final class NuanceQueue
extends NuanceDAO
implements
PhabricatorPolicyInterface,
PhabricatorApplicationTransactionInterface {
protected $name;
protected $mailKey;
protected $viewPolicy;
protected $editPolicy;
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'text255?',
'mailKey' => 'bytes20',
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
NuanceQueuePHIDType::TYPECONST);
}
public static function initializeNewQueue() {
return id(new self())
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
->setEditPolicy(PhabricatorPolicies::POLICY_USER);
}
public function save() {
if (!$this->getMailKey()) {
$this->setMailKey(Filesystem::readRandomCharacters(20));
}
return parent::save();
}
public function getURI() {
return '/nuance/queue/view/'.$this->getID().'/';
}
+ public function getWorkURI() {
+ return '/nuance/queue/work/'.$this->getID().'/';
+ }
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new NuanceQueueEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new NuanceQueueTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
}
diff --git a/src/applications/nuance/worker/NuanceItemUpdateWorker.php b/src/applications/nuance/worker/NuanceItemUpdateWorker.php
index 57be20edae..5f33f885f9 100644
--- a/src/applications/nuance/worker/NuanceItemUpdateWorker.php
+++ b/src/applications/nuance/worker/NuanceItemUpdateWorker.php
@@ -1,73 +1,92 @@
<?php
final class NuanceItemUpdateWorker
extends NuanceWorker {
protected function doWork() {
$item_phid = $this->getTaskDataValue('itemPHID');
$hash = PhabricatorHash::digestForIndex($item_phid);
$lock_key = "nuance.item.{$hash}";
$lock = PhabricatorGlobalLock::newLock($lock_key);
$lock->lock(1);
try {
$item = $this->loadItem($item_phid);
$this->updateItem($item);
$this->routeItem($item);
$this->applyCommands($item);
} catch (Exception $ex) {
$lock->unlock();
throw $ex;
}
$lock->unlock();
}
private function updateItem(NuanceItem $item) {
$impl = $item->getImplementation();
if (!$impl->canUpdateItems()) {
return null;
}
$viewer = $this->getViewer();
$impl->setViewer($viewer);
$impl->updateItem($item);
}
private function routeItem(NuanceItem $item) {
$status = $item->getStatus();
if ($status != NuanceItem::STATUS_ROUTING) {
return;
}
$source = $item->getSource();
// For now, always route items into the source's default queue.
$item
->setQueuePHID($source->getDefaultQueuePHID())
->setStatus(NuanceItem::STATUS_OPEN)
->save();
}
private function applyCommands(NuanceItem $item) {
$viewer = $this->getViewer();
$impl = $item->getImplementation();
$impl->setViewer($viewer);
$commands = id(new NuanceItemCommandQuery())
->setViewer($viewer)
->withItemPHIDs(array($item->getPHID()))
+ ->withStatuses(
+ array(
+ NuanceItemCommand::STATUS_ISSUED,
+ ))
->execute();
$commands = msort($commands, 'getID');
foreach ($commands as $command) {
- $impl->applyCommand($item, $command);
- $command->delete();
+ $command
+ ->setStatus(NuanceItemCommand::STATUS_EXECUTING)
+ ->save();
+
+ try {
+ $impl->applyCommand($item, $command);
+
+ $command
+ ->setStatus(NuanceItemCommand::STATUS_DONE)
+ ->save();
+ } catch (Exception $ex) {
+ $command
+ ->setStatus(NuanceItemCommand::STATUS_FAILED)
+ ->save();
+
+ throw $ex;
+ }
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jul 29, 6:51 AM (2 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
188171
Default Alt Text
(31 KB)

Event Timeline