1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-26 07:20:57 +01:00

Modernize worker task detail view

Summary: Make mobile-friendly and provide UI to cancel/retry tasks. Remove display of task data to arbitrary users, as it may be sensitive.

Test Plan:
{F22502}
{F22503}
{F22504}
{F22505}
{F22506}

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2015

Differential Revision: https://secure.phabricator.com/D3854
This commit is contained in:
epriestley 2012-10-31 15:22:32 -07:00
parent 5903ed650c
commit fe329b9738
8 changed files with 270 additions and 147 deletions

View file

@ -51,8 +51,8 @@ celerity_register_resource_map(array(
), ),
'/rsrc/image/autosprite.png' => '/rsrc/image/autosprite.png' =>
array( array(
'hash' => 'e1735b5cadbaf1f70b70a857eab53601', 'hash' => 'bc9479b2a610a3ecee18dc88744c4ce6',
'uri' => '/res/e1735b5c/rsrc/image/autosprite.png', 'uri' => '/res/bc9479b2/rsrc/image/autosprite.png',
'disk' => '/rsrc/image/autosprite.png', 'disk' => '/rsrc/image/autosprite.png',
'type' => 'png', 'type' => 'png',
), ),
@ -713,7 +713,7 @@ celerity_register_resource_map(array(
), ),
'autosprite-css' => 'autosprite-css' =>
array( array(
'uri' => '/res/10fb7fdc/rsrc/css/autosprite.css', 'uri' => '/res/6be3e4b3/rsrc/css/autosprite.css',
'type' => 'css', 'type' => 'css',
'requires' => 'requires' =>
array( array(

View file

@ -31,129 +31,163 @@ final class PhabricatorWorkerTaskDetailController
$task = id(new PhabricatorWorkerActiveTask())->load($this->id); $task = id(new PhabricatorWorkerActiveTask())->load($this->id);
if (!$task) { if (!$task) {
$task = id(new PhabricatorWorkerArchiveTask())->load($this->id);
}
if (!$task) {
$title = pht('Task Does Not Exist');
$error_view = new AphrontErrorView(); $error_view = new AphrontErrorView();
$error_view->setTitle('No Such Task'); $error_view->setTitle('No Such Task');
$error_view->appendChild( $error_view->appendChild(
'<p>This task may have recently completed.</p>'); '<p>This task may have recently been garbage collected.</p>');
$error_view->setSeverity(AphrontErrorView::SEVERITY_WARNING); $error_view->setSeverity(AphrontErrorView::SEVERITY_NODATA);
return $this->buildStandardPageResponse(
$error_view, $content = $error_view;
array( } else {
'title' => 'Task Does Not Exist', $title = 'Task '.$task->getID();
));
$header = id(new PhabricatorHeaderView())
->setHeader('Task '.$task->getID().' ('.$task->getTaskClass().')');
$actions = $this->buildActionListView($task);
$properties = $this->buildPropertyListView($task);
$content = array(
$header,
$actions,
$properties,
);
} }
$data = id(new PhabricatorWorkerTaskData())->loadOneWhere(
'id = %d',
$task->getDataID());
$extra = null;
switch ($task->getTaskClass()) {
case 'PhabricatorRepositorySvnCommitChangeParserWorker':
case 'PhabricatorRepositoryGitCommitChangeParserWorker':
$commit_id = idx($data->getData(), 'commitID');
if ($commit_id) {
$commit = id(new PhabricatorRepositoryCommit())->load($commit_id);
if ($commit) {
$repository = id(new PhabricatorRepository())->load(
$commit->getRepositoryID());
if ($repository) {
$extra =
"<strong>NOTE:</strong> ".
"You can manually retry this task by running this script:".
"<pre>".
"phabricator/\$ ./scripts/repository/reparse.php ".
"r".
phutil_escape_html($repository->getCallsign()).
phutil_escape_html($commit->getCommitIdentifier()).
" ".
"--change".
"</pre>";
}
}
}
break;
default:
break;
}
if ($data) {
$data = json_encode($data->getData());
}
$form = id(new AphrontFormView())
->setUser($user)
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('ID')
->setValue($task->getID()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Type')
->setValue($task->getTaskClass()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Lease Owner')
->setValue($task->getLeaseOwner()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Lease Expires')
->setValue($task->getLeaseExpires() - time()))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel('Failure Count')
->setValue($task->getFailureCount()))
->appendChild(
id(new AphrontFormTextAreaControl())
->setLabel('Data')
->setValue($data));
if ($extra) {
$form->appendChild(
id(new AphrontFormMarkupControl())
->setLabel('More')
->setValue($extra));
}
$form
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton('/daemon/', 'Back'));
$panel = new AphrontPanelView();
$panel->setHeader('Task Detail');
$panel->setWidth(AphrontPanelView::WIDTH_WIDE);
$panel->appendChild($form);
$panel->addButton(
javelin_render_tag(
'a',
array(
'href' => '/daemon/task/'.$task->getID().'/delete/',
'class' => 'button grey',
'sigil' => 'workflow',
),
'Delete Task'));
$panel->addButton(
javelin_render_tag(
'a',
array(
'href' => '/daemon/task/'.$task->getID().'/release/',
'class' => 'button grey',
'sigil' => 'workflow',
),
'Free Lease'));
$nav = $this->buildSideNavView(); $nav = $this->buildSideNavView();
$nav->selectFilter(''); $nav->selectFilter('');
$nav->appendChild($panel); $nav->appendChild($content);
return $this->buildApplicationPage( return $this->buildApplicationPage(
$nav, $nav,
array( array(
'title' => 'Task', 'title' => $title,
)); ));
} }
private function buildActionListView(PhabricatorWorkerTask $task) {
$user = $this->getRequest()->getUser();
$view = new PhabricatorActionListView();
$view->setUser($user);
$id = $task->getID();
if ($task->isArchived()) {
$result_success = PhabricatorWorkerArchiveTask::RESULT_SUCCESS;
$can_retry = ($task->getResult() != $result_success);
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Retry Task'))
->setHref($this->getApplicationURI('/task/'.$id.'/retry/'))
->setIcon('undo')
->setWorkflow(true)
->setDisabled(!$can_retry));
} else {
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Cancel Task'))
->setHref($this->getApplicationURI('/task/'.$id.'/cancel/'))
->setIcon('delete')
->setWorkflow(true));
}
$can_release = (!$task->isArchived()) &&
($task->getLeaseOwner());
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Free Lease'))
->setHref($this->getApplicationURI('/task/'.$id.'/release/'))
->setIcon('unlock')
->setWorkflow(true)
->setDisabled(!$can_release));
return $view;
}
private function buildPropertyListView(PhabricatorWorkerTask $task) {
$view = new PhabricatorPropertyListView();
if ($task->isArchived()) {
switch ($task->getResult()) {
case PhabricatorWorkerArchiveTask::RESULT_SUCCESS:
$status = pht('Complete');
break;
case PhabricatorWorkerArchiveTask::RESULT_FAILURE:
$status = pht('Failed');
break;
case PhabricatorWorkerArchiveTask::RESULT_CANCELLED:
$status = pht('Cancelled');
break;
default:
throw new Exception("Unknown task status!");
}
} else {
$status = pht('Queued');
}
$view->addProperty(
pht('Task Status'),
$status);
$view->addProperty(
pht('Task Class'),
phutil_escape_html($task->getTaskClass()));
if ($task->getLeaseExpires()) {
if ($task->getLeaseExpires() > time()) {
$lease_status = pht('Leased');
} else {
$lease_status = pht('Lease Expired');
}
} else {
$lease_status = '<em>'.pht('Not Leased').'</em>';
}
$view->addProperty(
pht('Lease Status'),
$lease_status);
$view->addProperty(
pht('Lease Owner'),
$task->getLeaseOwner()
? phutil_escape_html($task->getLeaseOwner())
: '<em>'.pht('None').'</em>');
if ($task->getLeaseExpires() && $task->getLeaseOwner()) {
$expires = ($task->getLeaseExpires() - time());
$expires = phabricator_format_relative_time_detailed($expires);
} else {
$expires = '<em>'.pht('None').'</em>';
}
$view->addProperty(
pht('Lease Expires'),
$expires);
$view->addProperty(
pht('Failure Count'),
phutil_escape_html($task->getFailureCount()));
if ($task->isArchived()) {
$duration = phutil_escape_html(number_format($task->getDuration()).' us');
} else {
$duration = '<em>'.pht('Not Completed').'</em>';
}
$view->addProperty(
pht('Duration'),
$duration);
return $view;
}
} }

View file

@ -32,48 +32,104 @@ final class PhabricatorWorkerTaskUpdateController
$user = $request->getUser(); $user = $request->getUser();
$task = id(new PhabricatorWorkerActiveTask())->load($this->id); $task = id(new PhabricatorWorkerActiveTask())->load($this->id);
if (!$task) {
$task = id(new PhabricatorWorkerArchiveTask())->load($this->id);
}
if (!$task) { if (!$task) {
return new Aphront404Response(); return new Aphront404Response();
} }
$result_success = PhabricatorWorkerArchiveTask::RESULT_SUCCESS;
$can_retry = ($task->isArchived()) &&
($task->getResult() != $result_success);
$can_cancel = !$task->isArchived();
$can_release = (!$task->isArchived()) &&
($task->getLeaseOwner());
$next_uri = $this->getApplicationURI('/task/'.$task->getID().'/');
if ($request->isFormPost()) { if ($request->isFormPost()) {
switch ($this->action) { switch ($this->action) {
case 'delete': case 'retry':
$task->delete(); if ($can_retry) {
$task->unarchiveTask();
}
break;
case 'cancel':
if ($can_cancel) {
// Forcibly break the lease if one exists, so we can archive the
// task.
$task->setLeaseOwner(null);
$task->setLeaseExpires(time());
$task->archiveTask(
PhabricatorWorkerArchiveTask::RESULT_CANCELLED,
0);
}
break; break;
case 'release': case 'release':
if ($can_release) {
$task->setLeaseOwner(null); $task->setLeaseOwner(null);
$task->setLeaseExpires(time()); $task->setLeaseExpires(time());
$task->save(); $task->save();
}
break; break;
} }
return id(new AphrontRedirectResponse())->setURI('/daemon/'); return id(new AphrontRedirectResponse())
->setURI($next_uri);
} }
$dialog = new AphrontDialogView(); $dialog = new AphrontDialogView();
$dialog->setUser($user); $dialog->setUser($user);
switch ($this->action) { switch ($this->action) {
case 'delete': case 'retry':
$dialog->setTitle('Really delete task?'); if ($can_retry) {
$dialog->setTitle('Really retry task?');
$dialog->appendChild(
'<p>The task will be put back in the queue and executed '.
'again.</p>');
$dialog->addSubmitButton('Retry Task');
} else {
$dialog->setTitle('Can Not Retry');
$dialog->appendChild(
'<p>Only archived, unsuccessful tasks can be retried.</p>');
}
break;
case 'cancel':
if ($can_cancel) {
$dialog->setTitle('Really cancel task?');
$dialog->appendChild( $dialog->appendChild(
'<p>The work this task represents will never be performed if you '. '<p>The work this task represents will never be performed if you '.
'delete it. Are you sure you want to delete it?</p>'); 'cancel it. Are you sure you want to cancel it?</p>');
$dialog->addSubmitButton('Delete Task'); $dialog->addSubmitButton('Cancel Task');
} else {
$dialog->setTitle('Can Not Cancel');
$dialog->appendChild(
'<p>Only active tasks can be cancelled.</p>');
}
break; break;
case 'release': case 'release':
if ($can_release) {
$dialog->setTitle('Really free task lease?'); $dialog->setTitle('Really free task lease?');
$dialog->appendChild( $dialog->appendChild(
'<p>If the process which owns the task lease is still doing work '. '<p>If the process which owns the task lease is still doing work '.
'on it, the work may be performed twice. Are you sure you '. 'on it, the work may be performed twice. Are you sure you '.
'want to free the lease?</p>'); 'want to free the lease?</p>');
$dialog->addSubmitButton('Free Lease'); $dialog->addSubmitButton('Free Lease');
} else {
$dialog->setTitle('Can Not Free Lease');
$dialog->appendChild(
'<p>Only active, leased tasks may have their leases freed.</p>');
}
break; break;
default: default:
return new Aphront404Response(); return new Aphront404Response();
} }
$dialog->addCancelButton('/daemon/'); $dialog->addCancelButton($next_uri);
return id(new AphrontDialogResponse())->setDialog($dialog); return id(new AphrontDialogResponse())->setDialog($dialog);
} }

View file

@ -20,6 +20,7 @@ final class PhabricatorWorkerArchiveTask extends PhabricatorWorkerTask {
const RESULT_SUCCESS = 0; const RESULT_SUCCESS = 0;
const RESULT_FAILURE = 1; const RESULT_FAILURE = 1;
const RESULT_CANCELLED = 2;
protected $duration; protected $duration;
protected $result; protected $result;
@ -63,4 +64,22 @@ final class PhabricatorWorkerArchiveTask extends PhabricatorWorkerTask {
return $result; return $result;
} }
public function unarchiveTask() {
$this->openTransaction();
$active = id(new PhabricatorWorkerActiveTask())
->setID($this->getID())
->setTaskClass($this->getTaskClass())
->setLeaseOwner(null)
->setLeaseExpires(0)
->setFailureCount(0)
->setDataID($this->getDataID())
->insert();
$this->setDataID(null);
$this->delete();
$this->saveTransaction();
return $active;
}
} }

View file

@ -37,4 +37,8 @@ abstract class PhabricatorWorkerTask extends PhabricatorWorkerDAO {
return $this->data; return $this->data;
} }
public function isArchived() {
return ($this instanceof PhabricatorWorkerArchiveTask);
}
} }

View file

@ -151,6 +151,8 @@ final class PhabricatorActionView extends AphrontView {
'subscribe-add', 'subscribe-add',
'subscribe-auto', 'subscribe-auto',
'subscribe-delete', 'subscribe-delete',
'undo',
'unlock',
'unpublish', 'unpublish',
'world', 'world',
); );

View file

@ -808,50 +808,58 @@
background-position: 0px -7823px; background-position: 0px -7823px;
} }
.action-unpublish { .action-undo {
background-position: 0px -7840px; background-position: 0px -7840px;
} }
.action-world { .action-unlock {
background-position: 0px -7857px; background-position: 0px -7857px;
} }
.remarkup-assist-b { .action-unpublish {
background-position: 0px -7874px; background-position: 0px -7874px;
} }
.action-world {
background-position: 0px -7891px;
}
.remarkup-assist-b {
background-position: 0px -7908px;
}
.remarkup-assist-code { .remarkup-assist-code {
background-position: 0px -7889px; background-position: 0px -7923px;
} }
.remarkup-assist-i { .remarkup-assist-i {
background-position: 0px -7904px; background-position: 0px -7938px;
} }
.remarkup-assist-image { .remarkup-assist-image {
background-position: 0px -7919px; background-position: 0px -7953px;
} }
.remarkup-assist-ol { .remarkup-assist-ol {
background-position: 0px -7934px; background-position: 0px -7968px;
} }
.remarkup-assist-tag { .remarkup-assist-tag {
background-position: 0px -7949px; background-position: 0px -7983px;
} }
.remarkup-assist-tt { .remarkup-assist-tt {
background-position: 0px -7964px; background-position: 0px -7998px;
} }
.remarkup-assist-ul { .remarkup-assist-ul {
background-position: 0px -7979px; background-position: 0px -8013px;
} }
.remarkup-assist-help { .remarkup-assist-help {
background-position: 0px -7994px; background-position: 0px -8028px;
} }
.remarkup-assist-table { .remarkup-assist-table {
background-position: 0px -8009px; background-position: 0px -8043px;
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 153 KiB