Differential D21691 Diff 51704 src/applications/harbormaster/controller/HarbormasterBuildableActionController.php
Changeset View
Changeset View
Standalone View
Standalone View
src/applications/harbormaster/controller/HarbormasterBuildableActionController.php
Show All 16 Lines | $buildable = id(new HarbormasterBuildableQuery()) | ||||
PhabricatorPolicyCapability::CAN_VIEW, | PhabricatorPolicyCapability::CAN_VIEW, | ||||
PhabricatorPolicyCapability::CAN_EDIT, | PhabricatorPolicyCapability::CAN_EDIT, | ||||
)) | )) | ||||
->executeOne(); | ->executeOne(); | ||||
if (!$buildable) { | if (!$buildable) { | ||||
return new Aphront404Response(); | return new Aphront404Response(); | ||||
} | } | ||||
$issuable = array(); | $message = | ||||
HarbormasterBuildMessageTransaction::getTransactionObjectForMessageType( | |||||
$builds = $buildable->getBuilds(); | $action); | ||||
foreach ($builds as $key => $build) { | if (!$message) { | ||||
switch ($action) { | return new Aphront404Response(); | ||||
case HarbormasterBuildCommand::COMMAND_RESTART: | |||||
if ($build->canRestartBuild()) { | |||||
$issuable[$key] = $build; | |||||
} | |||||
break; | |||||
case HarbormasterBuildCommand::COMMAND_PAUSE: | |||||
if ($build->canPauseBuild()) { | |||||
$issuable[$key] = $build; | |||||
} | |||||
break; | |||||
case HarbormasterBuildCommand::COMMAND_RESUME: | |||||
if ($build->canResumeBuild()) { | |||||
$issuable[$key] = $build; | |||||
} | |||||
break; | |||||
case HarbormasterBuildCommand::COMMAND_ABORT: | |||||
if ($build->canAbortBuild()) { | |||||
$issuable[$key] = $build; | |||||
} | |||||
break; | |||||
default: | |||||
return new Aphront400Response(); | |||||
} | |||||
} | |||||
$restricted = false; | |||||
foreach ($issuable as $key => $build) { | |||||
if (!$build->canIssueCommand($viewer, $action)) { | |||||
$restricted = true; | |||||
unset($issuable[$key]); | |||||
} | |||||
} | |||||
$building = false; | |||||
foreach ($issuable as $key => $build) { | |||||
if ($build->isBuilding()) { | |||||
$building = true; | |||||
break; | |||||
} | |||||
} | } | ||||
$return_uri = '/'.$buildable->getMonogram(); | $return_uri = '/'.$buildable->getMonogram(); | ||||
if ($request->isDialogFormPost() && $issuable) { | |||||
$editor = id(new HarbormasterBuildableTransactionEditor()) | |||||
->setActor($viewer) | |||||
->setContentSourceFromRequest($request) | |||||
->setContinueOnNoEffect(true) | |||||
->setContinueOnMissingFields(true); | |||||
$xaction = id(new HarbormasterBuildableTransaction()) | |||||
->setTransactionType(HarbormasterBuildableTransaction::TYPE_COMMAND) | |||||
->setNewValue($action); | |||||
$editor->applyTransactions($buildable, array($xaction)); | |||||
$build_editor = id(new HarbormasterBuildTransactionEditor()) | |||||
->setActor($viewer) | |||||
->setContentSourceFromRequest($request) | |||||
->setContinueOnNoEffect(true) | |||||
->setContinueOnMissingFields(true); | |||||
foreach ($issuable as $build) { | |||||
$xaction = id(new HarbormasterBuildTransaction()) | |||||
->setTransactionType(HarbormasterBuildTransaction::TYPE_COMMAND) | |||||
->setNewValue($action); | |||||
$build_editor->applyTransactions($build, array($xaction)); | |||||
} | |||||
return id(new AphrontRedirectResponse())->setURI($return_uri); | |||||
} | |||||
$width = AphrontDialogView::WIDTH_DEFAULT; | // See T13348. Actions may apply to only a subset of builds, so give the | ||||
// user a preview of what will happen. | |||||
switch ($action) { | |||||
case HarbormasterBuildCommand::COMMAND_RESTART: | |||||
// See T13348. The "Restart Builds" action may restart only a subset | |||||
// of builds, so show the user a preview of which builds will actually | |||||
// restart. | |||||
$body = array(); | |||||
if ($issuable) { | |||||
$title = pht('Restart Builds'); | |||||
$submit = pht('Restart Builds'); | |||||
} else { | |||||
$title = pht('Unable to Restart Builds'); | |||||
} | |||||
if ($builds) { | $can_send = array(); | ||||
$width = AphrontDialogView::WIDTH_FORM; | |||||
$body[] = pht('Builds for this buildable:'); | |||||
$rows = array(); | $rows = array(); | ||||
$builds = $buildable->getBuilds(); | |||||
foreach ($builds as $key => $build) { | foreach ($builds as $key => $build) { | ||||
if (isset($issuable[$key])) { | $exception = null; | ||||
$icon = id(new PHUIIconView()) | |||||
->setIcon('fa-repeat green'); | |||||
$build_note = pht('Will Restart'); | |||||
} else { | |||||
$icon = null; | |||||
try { | try { | ||||
$build->assertCanRestartBuild(); | $message->assertCanSendMessage($viewer, $build); | ||||
$can_send[$key] = $build; | |||||
} catch (HarbormasterRestartException $ex) { | } catch (HarbormasterRestartException $ex) { | ||||
$icon = id(new PHUIIconView()) | $exception = $ex; | ||||
->setIcon('fa-times red'); | |||||
$build_note = pht( | |||||
'%s: %s', | |||||
phutil_tag('strong', array(), pht('Not Restartable')), | |||||
$ex->getTitle()); | |||||
} | } | ||||
if (!$icon) { | if (!$exception) { | ||||
try { | $icon_icon = $message->getIcon(); | ||||
$build->assertCanIssueCommand($viewer, $action); | $icon_color = 'green'; | ||||
} catch (PhabricatorPolicyException $ex) { | |||||
$icon = id(new PHUIIconView()) | $title = $message->getHarbormasterBuildMessageName(); | ||||
->setIcon('fa-lock red'); | $body = $message->getHarbormasterBuildableMessageEffect(); | ||||
$build_note = pht( | } else { | ||||
'%s: %s', | $icon_icon = 'fa-times'; | ||||
phutil_tag('strong', array(), pht('Not Restartable')), | $icon_color = 'red'; | ||||
pht('You do not have permission to restart this build.')); | |||||
} | $title = $ex->getTitle(); | ||||
$body = $ex->getBody(); | |||||
} | } | ||||
if (!$icon) { | |||||
$icon = id(new PHUIIconView()) | $icon = id(new PHUIIconView()) | ||||
->setIcon('fa-times red'); | ->setIcon($icon_icon) | ||||
$build_note = pht('Will Not Restart'); | ->setColor($icon_color); | ||||
} | |||||
} | |||||
$build_name = phutil_tag( | $build_name = phutil_tag( | ||||
'a', | 'a', | ||||
array( | array( | ||||
'href' => $build->getURI(), | 'href' => $build->getURI(), | ||||
'target' => '_blank', | 'target' => '_blank', | ||||
), | ), | ||||
pht('%s %s', $build->getObjectName(), $build->getName())); | pht('%s %s', $build->getObjectName(), $build->getName())); | ||||
$rows[] = array( | $rows[] = array( | ||||
$icon, | $icon, | ||||
$build_name, | $build_name, | ||||
$build_note, | $title, | ||||
$body, | |||||
); | ); | ||||
} | } | ||||
$table = id(new AphrontTableView($rows)) | $table = id(new AphrontTableView($rows)) | ||||
->setHeaders( | ->setHeaders( | ||||
array( | array( | ||||
null, | null, | ||||
pht('Build'), | pht('Build'), | ||||
pht('Action'), | pht('Action'), | ||||
pht('Details'), | |||||
)) | )) | ||||
->setColumnClasses( | ->setColumnClasses( | ||||
array( | array( | ||||
null, | null, | ||||
null, | |||||
'pri', | 'pri', | ||||
'wide', | 'wide', | ||||
)); | )); | ||||
$table = phutil_tag( | $table = phutil_tag( | ||||
'div', | 'div', | ||||
array( | array( | ||||
'class' => 'mlt mlb', | 'class' => 'mlt mlb', | ||||
), | ), | ||||
$table); | $table); | ||||
$body[] = $table; | if ($request->isDialogFormPost() && $can_send) { | ||||
} | $editor = id(new HarbormasterBuildableTransactionEditor()) | ||||
->setActor($viewer) | |||||
if ($issuable) { | ->setContentSourceFromRequest($request) | ||||
$warnings = array(); | ->setContinueOnNoEffect(true) | ||||
->setContinueOnMissingFields(true); | |||||
if ($restricted) { | |||||
$warnings[] = pht( | |||||
'You only have permission to restart some builds.'); | |||||
} | |||||
if ($building) { | $xaction = id(new HarbormasterBuildableTransaction()) | ||||
$warnings[] = pht( | ->setTransactionType(HarbormasterBuildableTransaction::TYPE_COMMAND) | ||||
'Progress on running builds will be discarded.'); | ->setNewValue($action); | ||||
} | |||||
$warnings[] = pht( | $editor->applyTransactions($buildable, array($xaction)); | ||||
'When a build is restarted, side effects associated with '. | |||||
'the build may occur again.'); | |||||
$body[] = id(new PHUIInfoView()) | $build_editor = id(new HarbormasterBuildTransactionEditor()) | ||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING) | ->setActor($viewer) | ||||
->setErrors($warnings); | ->setContentSourceFromRequest($request) | ||||
->setContinueOnNoEffect(true) | |||||
->setContinueOnMissingFields(true); | |||||
$body[] = pht('Really restart builds?'); | foreach ($can_send as $build) { | ||||
} else { | $build->sendMessage( | ||||
if ($restricted) { | $viewer, | ||||
$body[] = pht('You do not have permission to restart any builds.'); | $message->getHarbormasterBuildMessageType()); | ||||
} else { | |||||
$body[] = pht('No builds can be restarted.'); | |||||
} | |||||
} | } | ||||
break; | return id(new AphrontRedirectResponse())->setURI($return_uri); | ||||
case HarbormasterBuildCommand::COMMAND_PAUSE: | |||||
if ($issuable) { | |||||
$title = pht('Really pause builds?'); | |||||
if ($restricted) { | |||||
$body = pht( | |||||
'You only have permission to pause some builds. Once the '. | |||||
'current steps complete, work will halt on builds you have '. | |||||
'permission to pause. You can resume the builds later.'); | |||||
} else { | |||||
$body = pht( | |||||
'If you pause all builds, work will halt once the current steps '. | |||||
'complete. You can resume the builds later.'); | |||||
} | } | ||||
$submit = pht('Pause Builds'); | |||||
} else { | |||||
$title = pht('Unable to Pause Builds'); | |||||
if ($restricted) { | if (!$builds) { | ||||
$body = pht('You do not have permission to pause any builds.'); | $title = pht('No Builds'); | ||||
} else { | |||||
$body = pht('No builds can be paused.'); | |||||
} | |||||
} | |||||
break; | |||||
case HarbormasterBuildCommand::COMMAND_ABORT: | |||||
if ($issuable) { | |||||
$title = pht('Really abort builds?'); | |||||
if ($restricted) { | |||||
$body = pht( | |||||
'You only have permission to abort some builds. Work will '. | |||||
'halt immediately on builds you have permission to abort. '. | |||||
'Progress will be discarded, and builds must be completely '. | |||||
'restarted if you want them to complete.'); | |||||
} else { | |||||
$body = pht( | $body = pht( | ||||
'If you abort all builds, work will halt immediately. Work '. | 'This buildable has no builds, so you can not issue any commands.'); | ||||
'will be discarded, and builds must be completely restarted.'); | |||||
} | |||||
$submit = pht('Abort Builds'); | |||||
} else { | } else { | ||||
$title = pht('Unable to Abort Builds'); | if ($can_send) { | ||||
$title = $message->newBuildableConfirmPromptTitle( | |||||
if ($restricted) { | $builds, | ||||
$body = pht('You do not have permission to abort any builds.'); | $can_send); | ||||
$body = $message->newBuildableConfirmPromptBody( | |||||
$builds, | |||||
$can_send); | |||||
} else { | } else { | ||||
$body = pht('No builds can be aborted.'); | $title = pht('Unable to Send Command'); | ||||
} | |||||
} | |||||
break; | |||||
case HarbormasterBuildCommand::COMMAND_RESUME: | |||||
if ($issuable) { | |||||
$title = pht('Really resume builds?'); | |||||
if ($restricted) { | |||||
$body = pht( | $body = pht( | ||||
'You only have permission to resume some builds. Work will '. | 'You can not send this command to any of the current builds '. | ||||
'continue on builds you have permission to resume.'); | 'for this buildable.'); | ||||
} else { | |||||
$body = pht('Work will continue on all builds. Really resume?'); | |||||
} | } | ||||
$submit = pht('Resume Builds'); | $body = array( | ||||
} else { | pht('Builds for this buildable:'), | ||||
$title = pht('Unable to Resume Builds'); | $table, | ||||
if ($restricted) { | $body, | ||||
$body = pht('You do not have permission to resume any builds.'); | ); | ||||
} else { | |||||
$body = pht('No builds can be resumed.'); | |||||
} | |||||
} | } | ||||
break; | |||||
$warnings = $message->newBuildableConfirmPromptWarnings( | |||||
$builds, | |||||
$can_send); | |||||
if ($warnings) { | |||||
$body[] = id(new PHUIInfoView()) | |||||
->setSeverity(PHUIInfoView::SEVERITY_WARNING) | |||||
->setErrors($warnings); | |||||
} | } | ||||
$dialog = id(new AphrontDialogView()) | $submit = $message->getHarbormasterBuildableMessageName(); | ||||
->setUser($viewer) | |||||
->setWidth($width) | $dialog = $this->newDialog() | ||||
->setWidth(AphrontDialogView::WIDTH_FULL) | |||||
->setTitle($title) | ->setTitle($title) | ||||
->appendChild($body) | ->appendChild($body) | ||||
->addCancelButton($return_uri); | ->addCancelButton($return_uri); | ||||
if ($issuable) { | if ($can_send) { | ||||
$dialog->addSubmitButton($submit); | $dialog->addSubmitButton($submit); | ||||
} | } | ||||
return id(new AphrontDialogResponse())->setDialog($dialog); | return $dialog; | ||||
} | } | ||||
} | } |