Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/harbormaster/controller/HarbormasterController.php b/src/applications/harbormaster/controller/HarbormasterController.php
index 7202e356a2..c946cf7002 100644
--- a/src/applications/harbormaster/controller/HarbormasterController.php
+++ b/src/applications/harbormaster/controller/HarbormasterController.php
@@ -1,22 +1,5 @@
<?php
abstract class HarbormasterController extends PhabricatorController {
- protected function buildApplicationCrumbs() {
- $crumbs = parent::buildApplicationCrumbs();
-
- $can_create = $this->hasApplicationCapability(
- HarbormasterManagePlansCapability::CAPABILITY);
-
- $crumbs->addAction(
- id(new PHUIListItemView())
- ->setName(pht('New Build Plan'))
- ->setHref($this->getApplicationURI('plan/edit/'))
- ->setDisabled(!$can_create)
- ->setWorkflow(!$can_create)
- ->setIcon('fa-plus-square'));
-
- return $crumbs;
- }
-
}
diff --git a/src/applications/harbormaster/controller/HarbormasterPlanDisableController.php b/src/applications/harbormaster/controller/HarbormasterPlanDisableController.php
index 3937495343..28cb8340a6 100644
--- a/src/applications/harbormaster/controller/HarbormasterPlanDisableController.php
+++ b/src/applications/harbormaster/controller/HarbormasterPlanDisableController.php
@@ -1,76 +1,71 @@
<?php
final class HarbormasterPlanDisableController
extends HarbormasterPlanController {
- private $id;
-
- public function willProcessRequest(array $data) {
- $this->id = $data['id'];
- }
-
- public function processRequest() {
- $request = $this->getRequest();
- $viewer = $request->getUser();
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $this->getViewer();
$this->requireApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
- ->withIDs(array($this->id))
+ ->withIDs(array($request->getURIData('id')))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
->executeOne();
if (!$plan) {
return new Aphront404Response();
}
$plan_uri = $this->getApplicationURI('plan/'.$plan->getID().'/');
if ($request->isFormPost()) {
$type_status = HarbormasterBuildPlanTransaction::TYPE_STATUS;
$v_status = $plan->isDisabled()
? HarbormasterBuildPlan::STATUS_ACTIVE
: HarbormasterBuildPlan::STATUS_DISABLED;
$xactions = array();
$xactions[] = id(new HarbormasterBuildPlanTransaction())
->setTransactionType($type_status)
->setNewValue($v_status);
$editor = id(new HarbormasterBuildPlanEditor())
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->setContentSourceFromRequest($request);
$editor->applyTransactions($plan, $xactions);
return id(new AphrontRedirectResponse())->setURI($plan_uri);
}
if ($plan->isDisabled()) {
$title = pht('Enable Build Plan');
$body = pht('Enable this build plan?');
$button = pht('Enable Plan');
} else {
$title = pht('Disable Build Plan');
$body = pht(
'Disable this build plan? It will no longer be executed '.
'automatically.');
$button = pht('Disable Plan');
}
- $dialog = id(new AphrontDialogView())
- ->setUser($viewer)
+ return $this->newDialog()
->setTitle($title)
->appendChild($body)
->addSubmitButton($button)
->addCancelButton($plan_uri);
-
- return id(new AphrontDialogResponse())->setDialog($dialog);
}
}
diff --git a/src/applications/harbormaster/controller/HarbormasterPlanEditController.php b/src/applications/harbormaster/controller/HarbormasterPlanEditController.php
index 8679d9ce4c..dd43aa0bee 100644
--- a/src/applications/harbormaster/controller/HarbormasterPlanEditController.php
+++ b/src/applications/harbormaster/controller/HarbormasterPlanEditController.php
@@ -1,114 +1,114 @@
<?php
final class HarbormasterPlanEditController extends HarbormasterPlanController {
- private $id;
-
- public function willProcessRequest(array $data) {
- $this->id = idx($data, 'id');
- }
-
- public function processRequest() {
- $request = $this->getRequest();
- $viewer = $request->getUser();
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $this->getViewer();
$this->requireApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
- if ($this->id) {
+ $id = $request->getURIData('id');
+ if ($id) {
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
- ->withIDs(array($this->id))
+ ->withIDs(array($id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
->executeOne();
if (!$plan) {
return new Aphront404Response();
}
} else {
$plan = HarbormasterBuildPlan::initializeNewBuildPlan($viewer);
}
$e_name = true;
$v_name = $plan->getName();
$validation_exception = null;
if ($request->isFormPost()) {
$xactions = array();
$v_name = $request->getStr('name');
$e_name = null;
$type_name = HarbormasterBuildPlanTransaction::TYPE_NAME;
$xactions[] = id(new HarbormasterBuildPlanTransaction())
->setTransactionType($type_name)
->setNewValue($v_name);
$editor = id(new HarbormasterBuildPlanEditor())
->setActor($viewer)
+ ->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request);
try {
$editor->applyTransactions($plan, $xactions);
return id(new AphrontRedirectResponse())
->setURI($this->getApplicationURI('plan/'.$plan->getID().'/'));
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_name = $validation_exception->getShortMessage(
HarbormasterBuildPlanTransaction::TYPE_NAME);
}
}
$is_new = (!$plan->getID());
if ($is_new) {
$title = pht('New Build Plan');
$cancel_uri = $this->getApplicationURI();
$save_button = pht('Create Build Plan');
} else {
$id = $plan->getID();
$title = pht('Edit Build Plan');
$cancel_uri = $this->getApplicationURI('plan/'.$plan->getID().'/');
$save_button = pht('Save Build Plan');
}
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Plan Name'))
->setName('name')
->setError($e_name)
->setValue($v_name));
$form->appendChild(
id(new AphrontFormSubmitControl())
->setValue($save_button)
->addCancelButton($cancel_uri));
$box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setValidationException($validation_exception)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
if ($is_new) {
$crumbs->addTextCrumb(pht('New Build Plan'));
} else {
$id = $plan->getID();
$crumbs->addTextCrumb(
pht('Plan %d', $id),
$this->getApplicationURI("plan/{$id}/"));
$crumbs->addTextCrumb(pht('Edit'));
}
return $this->buildApplicationPage(
array(
$crumbs,
$box,
),
array(
'title' => $title,
));
}
}
diff --git a/src/applications/harbormaster/controller/HarbormasterPlanListController.php b/src/applications/harbormaster/controller/HarbormasterPlanListController.php
index 0e2cb3e233..e0f9ce07c7 100644
--- a/src/applications/harbormaster/controller/HarbormasterPlanListController.php
+++ b/src/applications/harbormaster/controller/HarbormasterPlanListController.php
@@ -1,47 +1,59 @@
<?php
final class HarbormasterPlanListController extends HarbormasterPlanController {
- private $queryKey;
-
public function shouldAllowPublic() {
return true;
}
- public function willProcessRequest(array $data) {
- $this->queryKey = idx($data, 'queryKey');
- }
-
- public function processRequest() {
+ public function handleRequest(AphrontRequest $request) {
$controller = id(new PhabricatorApplicationSearchController())
- ->setQueryKey($this->queryKey)
+ ->setQueryKey($request->getURIData('queryKey'))
->setSearchEngine(new HarbormasterBuildPlanSearchEngine())
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
public function buildSideNavView($for_app = false) {
$user = $this->getRequest()->getUser();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
if ($for_app) {
$nav->addFilter('new/', pht('New Build Plan'));
}
id(new HarbormasterBuildPlanSearchEngine())
->setViewer($user)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
public function buildApplicationMenu() {
return $this->buildSideNavView(true)->getMenu();
}
+ protected function buildApplicationCrumbs() {
+ $crumbs = parent::buildApplicationCrumbs();
+
+ $can_create = $this->hasApplicationCapability(
+ HarbormasterManagePlansCapability::CAPABILITY);
+
+ $crumbs->addAction(
+ id(new PHUIListItemView())
+ ->setName(pht('New Build Plan'))
+ ->setHref($this->getApplicationURI('plan/edit/'))
+ ->setDisabled(!$can_create)
+ ->setWorkflow(!$can_create)
+ ->setIcon('fa-plus-square'));
+
+ return $crumbs;
+ }
+
+
}
diff --git a/src/applications/harbormaster/controller/HarbormasterPlanRunController.php b/src/applications/harbormaster/controller/HarbormasterPlanRunController.php
index e4f94e05d1..0f5673087d 100644
--- a/src/applications/harbormaster/controller/HarbormasterPlanRunController.php
+++ b/src/applications/harbormaster/controller/HarbormasterPlanRunController.php
@@ -1,101 +1,96 @@
<?php
final class HarbormasterPlanRunController extends HarbormasterController {
- private $id;
-
- public function willProcessRequest(array $data) {
- $this->id = $data['id'];
- }
-
- public function processRequest() {
- $request = $this->getRequest();
- $viewer = $request->getUser();
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $this->getViewer();
$this->requireApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
- $plan_id = $this->id;
+ $plan_id = $request->getURIData('id');
+
+ // NOTE: At least for now, this only requires the "Can Manage Plans"
+ // capability, not the "Can Edit" capability. Possibly it should have
+ // a more stringent requirement, though.
+
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
->withIDs(array($plan_id))
->executeOne();
if (!$plan) {
return new Aphront404Response();
}
$e_name = true;
$v_name = null;
$errors = array();
if ($request->isFormPost()) {
$buildable = HarbormasterBuildable::initializeNewBuildable($viewer)
->setIsManualBuildable(true);
$v_name = $request->getStr('buildablePHID');
if ($v_name) {
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withNames(array($v_name))
->executeOne();
if ($object instanceof HarbormasterBuildableInterface) {
$buildable
->setBuildablePHID($object->getHarbormasterBuildablePHID())
->setContainerPHID($object->getHarbormasterContainerPHID());
} else {
$e_name = pht('Invalid');
$errors[] = pht('Enter the name of a revision or commit.');
}
} else {
$e_name = pht('Required');
$errors[] = pht('You must choose a revision or commit to build.');
}
if (!$errors) {
$buildable->save();
$buildable->applyPlan($plan);
$buildable_uri = '/B'.$buildable->getID();
return id(new AphrontRedirectResponse())->setURI($buildable_uri);
}
}
if ($errors) {
$errors = id(new PHUIInfoView())->setErrors($errors);
}
$title = pht('Run Build Plan Manually');
$cancel_uri = $this->getApplicationURI("plan/{$plan_id}/");
$save_button = pht('Run Plan Manually');
$form = id(new PHUIFormLayoutView())
->setUser($viewer)
->appendRemarkupInstructions(
pht(
"Enter the name of a commit or revision to run this plan on (for ".
"example, `rX123456` or `D123`).\n\n".
"For more detailed output, you can also run manual builds from ".
"the command line:\n\n".
" phabricator/ $ ./bin/harbormaster build <object> --plan %s",
$plan->getID()))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Buildable Name'))
->setName('buildablePHID')
->setError($e_name)
->setValue($v_name));
- $dialog = id(new AphrontDialogView())
+ return $this->newDialog()
->setWidth(AphrontDialogView::WIDTH_FULL)
- ->setUser($viewer)
->setTitle($title)
->appendChild($form)
->addCancelButton($cancel_uri)
->addSubmitButton($save_button);
-
- return id(new AphrontDialogResponse())->setDialog($dialog);
}
}
diff --git a/src/applications/harbormaster/controller/HarbormasterPlanViewController.php b/src/applications/harbormaster/controller/HarbormasterPlanViewController.php
index 8db8eaf118..3cde3ab5c8 100644
--- a/src/applications/harbormaster/controller/HarbormasterPlanViewController.php
+++ b/src/applications/harbormaster/controller/HarbormasterPlanViewController.php
@@ -1,475 +1,492 @@
<?php
final class HarbormasterPlanViewController extends HarbormasterPlanController {
- private $id;
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $this->getviewer();
- public function willProcessRequest(array $data) {
- $this->id = $data['id'];
- }
-
- public function processRequest() {
- $request = $this->getRequest();
- $viewer = $request->getUser();
-
- $id = $this->id;
+ $id = $request->getURIData('id');
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$plan) {
return new Aphront404Response();
}
$timeline = $this->buildTransactionTimeline(
$plan,
new HarbormasterBuildPlanTransactionQuery());
$timeline->setShouldTerminate(true);
$title = $plan->getName();
$header = id(new PHUIHeaderView())
->setHeader($plan->getName())
->setUser($viewer)
->setPolicyObject($plan);
$box = id(new PHUIObjectBoxView())
->setHeader($header);
$actions = $this->buildActionList($plan);
$this->buildPropertyLists($box, $plan, $actions);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Plan %d', $id));
list($step_list, $has_any_conflicts, $would_deadlock) =
$this->buildStepList($plan);
if ($would_deadlock) {
$box->setFormErrors(
array(
pht(
'This build plan will deadlock when executed, due to '.
'circular dependencies present in the build plan. '.
'Examine the step list and resolve the deadlock.'),
));
} else if ($has_any_conflicts) {
// A deadlocking build will also cause all the artifacts to be
// invalid, so we just skip showing this message if that's the
// case.
$box->setFormErrors(
array(
pht(
'This build plan has conflicts in one or more build steps. '.
'Examine the step list and resolve the listed errors.'),
));
}
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$step_list,
$timeline,
),
array(
'title' => $title,
));
}
private function buildStepList(HarbormasterBuildPlan $plan) {
- $request = $this->getRequest();
- $viewer = $request->getUser();
+ $viewer = $this->getViewer();
- $run_order =
- HarbormasterBuildGraph::determineDependencyExecution($plan);
+ $run_order = HarbormasterBuildGraph::determineDependencyExecution($plan);
$steps = id(new HarbormasterBuildStepQuery())
->setViewer($viewer)
->withBuildPlanPHIDs(array($plan->getPHID()))
->execute();
$steps = mpull($steps, null, 'getPHID');
- $can_edit = $this->hasApplicationCapability(
+ $has_manage = $this->hasApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
+ $has_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $plan,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $can_edit = ($has_manage && $has_edit);
+
$step_list = id(new PHUIObjectItemListView())
->setUser($viewer)
->setNoDataString(
pht('This build plan does not have any build steps yet.'));
$i = 1;
$last_depth = 0;
$has_any_conflicts = false;
$is_deadlocking = false;
foreach ($run_order as $run_ref) {
$step = $steps[$run_ref['node']->getPHID()];
$depth = $run_ref['depth'] + 1;
if ($last_depth !== $depth) {
$last_depth = $depth;
$i = 1;
} else {
$i++;
}
$implementation = null;
try {
$implementation = $step->getStepImplementation();
} catch (Exception $ex) {
// We can't initialize the implementation. This might be because
// it's been renamed or no longer exists.
$item = id(new PHUIObjectItemView())
->setObjectName(pht('Step %d.%d', $depth, $i))
->setHeader(pht('Unknown Implementation'))
->setBarColor('red')
->addAttribute(pht(
'This step has an invalid implementation (%s).',
$step->getClassName()))
->addAction(
id(new PHUIListItemView())
->setIcon('fa-times')
->addSigil('harbormaster-build-step-delete')
->setWorkflow(true)
->setRenderNameAsTooltip(true)
->setName(pht('Delete'))
->setHref(
$this->getApplicationURI('step/delete/'.$step->getID().'/')));
$step_list->addItem($item);
continue;
}
$item = id(new PHUIObjectItemView())
->setObjectName(pht('Step %d.%d', $depth, $i))
->setHeader($step->getName());
$item->addAttribute($implementation->getDescription());
$step_id = $step->getID();
$edit_uri = $this->getApplicationURI("step/edit/{$step_id}/");
$delete_uri = $this->getApplicationURI("step/delete/{$step_id}/");
if ($can_edit) {
$item->setHref($edit_uri);
}
$item
->setHref($edit_uri)
->addAction(
id(new PHUIListItemView())
->setIcon('fa-times')
->addSigil('harbormaster-build-step-delete')
->setWorkflow(true)
->setDisabled(!$can_edit)
->setHref(
$this->getApplicationURI('step/delete/'.$step->getID().'/')));
$depends = $step->getStepImplementation()->getDependencies($step);
$inputs = $step->getStepImplementation()->getArtifactInputs();
$outputs = $step->getStepImplementation()->getArtifactOutputs();
$has_conflicts = false;
if ($depends || $inputs || $outputs) {
$available_artifacts =
HarbormasterBuildStepImplementation::getAvailableArtifacts(
$plan,
$step,
null);
$available_artifacts = ipull($available_artifacts, 'type');
list($depends_ui, $has_conflicts) = $this->buildDependsOnList(
$depends,
pht('Depends On'),
$steps);
list($inputs_ui, $has_conflicts) = $this->buildArtifactList(
$inputs,
'in',
pht('Input Artifacts'),
$available_artifacts);
list($outputs_ui) = $this->buildArtifactList(
$outputs,
'out',
pht('Output Artifacts'),
array());
$item->appendChild(
phutil_tag(
'div',
array(
'class' => 'harbormaster-artifact-io',
),
array(
$depends_ui,
$inputs_ui,
$outputs_ui,
)));
}
if ($has_conflicts) {
$has_any_conflicts = true;
$item->setBarColor('red');
}
if ($run_ref['cycle']) {
$is_deadlocking = true;
}
if ($is_deadlocking) {
$item->setBarColor('red');
}
$step_list->addItem($item);
}
- return array($step_list, $has_any_conflicts, $is_deadlocking);
+ $step_list->setFlush(true);
+
+ $plan_id = $plan->getID();
+
+ $header = id(new PHUIHeaderView())
+ ->setHeader(pht('Build Steps'))
+ ->addActionLink(
+ id(new PHUIButtonView())
+ ->setText(pht('Add Build Step'))
+ ->setHref($this->getApplicationURI("step/add/{$plan_id}/"))
+ ->setTag('a')
+ ->setIcon(
+ id(new PHUIIconView())
+ ->setIconFont('fa-plus'))
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(true));
+
+ $step_box = id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->appendChild($step_list);
+
+ return array($step_box, $has_any_conflicts, $is_deadlocking);
}
private function buildActionList(HarbormasterBuildPlan $plan) {
- $request = $this->getRequest();
- $viewer = $request->getUser();
+ $viewer = $this->getViewer();
$id = $plan->getID();
$list = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($plan)
->setObjectURI($this->getApplicationURI("plan/{$id}/"));
- $can_edit = $this->hasApplicationCapability(
+ $has_manage = $this->hasApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
+ $has_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $plan,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $can_edit = ($has_manage && $has_edit);
+
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Plan'))
->setHref($this->getApplicationURI("plan/edit/{$id}/"))
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit)
->setIcon('fa-pencil'));
if ($plan->isDisabled()) {
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Enable Plan'))
->setHref($this->getApplicationURI("plan/disable/{$id}/"))
->setWorkflow(true)
->setDisabled(!$can_edit)
->setIcon('fa-check'));
} else {
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Disable Plan'))
->setHref($this->getApplicationURI("plan/disable/{$id}/"))
->setWorkflow(true)
->setDisabled(!$can_edit)
->setIcon('fa-ban'));
}
- $list->addAction(
- id(new PhabricatorActionView())
- ->setName(pht('Add Build Step'))
- ->setHref($this->getApplicationURI("step/add/{$id}/"))
- ->setWorkflow(true)
- ->setDisabled(!$can_edit)
- ->setIcon('fa-plus'));
-
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Run Plan Manually'))
->setHref($this->getApplicationURI("plan/run/{$id}/"))
->setWorkflow(true)
- ->setDisabled(!$can_edit)
+ ->setDisabled(!$has_manage)
->setIcon('fa-play-circle'));
return $list;
}
private function buildPropertyLists(
PHUIObjectBoxView $box,
HarbormasterBuildPlan $plan,
PhabricatorActionListView $actions) {
$request = $this->getRequest();
$viewer = $request->getUser();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($plan)
->setActionList($actions);
$box->addPropertyList($properties);
$properties->addProperty(
pht('Created'),
phabricator_datetime($plan->getDateCreated(), $viewer));
}
private function buildArtifactList(
array $artifacts,
$kind,
$name,
array $available_artifacts) {
$has_conflicts = false;
if (!$artifacts) {
return array(null, $has_conflicts);
}
$this->requireResource('harbormaster-css');
$header = phutil_tag(
'div',
array(
'class' => 'harbormaster-artifact-summary-header',
),
$name);
$is_input = ($kind == 'in');
$list = new PHUIStatusListView();
foreach ($artifacts as $artifact) {
$error = null;
$key = idx($artifact, 'key');
if (!strlen($key)) {
$bound = phutil_tag('em', array(), pht('(null)'));
if ($is_input) {
// This is an unbound input. For now, all inputs are always required.
$icon = PHUIStatusItemView::ICON_WARNING;
$color = 'red';
$icon_label = pht('Required Input');
$has_conflicts = true;
$error = pht('This input is required, but not configured.');
} else {
// This is an unnamed output. Outputs do not necessarily need to be
// named.
$icon = PHUIStatusItemView::ICON_OPEN;
$color = 'bluegrey';
$icon_label = pht('Unused Output');
}
} else {
$bound = phutil_tag('strong', array(), $key);
if ($is_input) {
if (isset($available_artifacts[$key])) {
if ($available_artifacts[$key] == idx($artifact, 'type')) {
$icon = PHUIStatusItemView::ICON_ACCEPT;
$color = 'green';
$icon_label = pht('Valid Input');
} else {
$icon = PHUIStatusItemView::ICON_WARNING;
$color = 'red';
$icon_label = pht('Bad Input Type');
$has_conflicts = true;
$error = pht(
'This input is bound to the wrong artifact type. It is bound '.
'to a "%s" artifact, but should be bound to a "%s" artifact.',
$available_artifacts[$key],
idx($artifact, 'type'));
}
} else {
$icon = PHUIStatusItemView::ICON_QUESTION;
$color = 'red';
$icon_label = pht('Unknown Input');
$has_conflicts = true;
$error = pht(
'This input is bound to an artifact ("%s") which does not exist '.
'at this stage in the build process.',
$key);
}
} else {
$icon = PHUIStatusItemView::ICON_DOWN;
$color = 'green';
$icon_label = pht('Valid Output');
}
}
if ($error) {
$note = array(
phutil_tag('strong', array(), pht('ERROR:')),
' ',
$error,
);
} else {
$note = $bound;
}
$list->addItem(
id(new PHUIStatusItemView())
->setIcon($icon, $color, $icon_label)
->setTarget($artifact['name'])
->setNote($note));
}
$ui = array(
$header,
$list,
);
return array($ui, $has_conflicts);
}
private function buildDependsOnList(
array $step_phids,
$name,
array $steps) {
$has_conflicts = false;
if (count($step_phids) === 0) {
return null;
}
$this->requireResource('harbormaster-css');
$steps = mpull($steps, null, 'getPHID');
$header = phutil_tag(
'div',
array(
'class' => 'harbormaster-artifact-summary-header',
),
$name);
$list = new PHUIStatusListView();
foreach ($step_phids as $step_phid) {
$error = null;
if (idx($steps, $step_phid) === null) {
$icon = PHUIStatusItemView::ICON_WARNING;
$color = 'red';
$icon_label = pht('Missing Dependency');
$has_conflicts = true;
$error = pht(
"This dependency specifies a build step which doesn't exist.");
} else {
$bound = phutil_tag(
'strong',
array(),
idx($steps, $step_phid)->getName());
$icon = PHUIStatusItemView::ICON_ACCEPT;
$color = 'green';
$icon_label = pht('Valid Input');
}
if ($error) {
$note = array(
phutil_tag('strong', array(), pht('ERROR:')),
' ',
$error,
);
} else {
$note = $bound;
}
$list->addItem(
id(new PHUIStatusItemView())
->setIcon($icon, $color, $icon_label)
->setTarget(pht('Build Step'))
->setNote($note));
}
$ui = array(
$header,
$list,
);
return array($ui, $has_conflicts);
}
}
diff --git a/src/applications/harbormaster/controller/HarbormasterStepAddController.php b/src/applications/harbormaster/controller/HarbormasterStepAddController.php
index 9b5817e074..2d72c02b0f 100644
--- a/src/applications/harbormaster/controller/HarbormasterStepAddController.php
+++ b/src/applications/harbormaster/controller/HarbormasterStepAddController.php
@@ -1,66 +1,64 @@
<?php
final class HarbormasterStepAddController extends HarbormasterController {
- private $id;
-
- public function willProcessRequest(array $data) {
- $this->id = $data['id'];
- }
-
- public function processRequest() {
- $request = $this->getRequest();
- $viewer = $request->getUser();
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $this->getViewer();
$this->requireApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
$plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer)
- ->withIDs(array($this->id))
+ ->withIDs(array($request->getURIData('id')))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
->executeOne();
if (!$plan) {
return new Aphront404Response();
}
$plan_id = $plan->getID();
$cancel_uri = $this->getApplicationURI("plan/{$plan_id}/");
$errors = array();
if ($request->isFormPost()) {
$class = $request->getStr('class');
if (!HarbormasterBuildStepImplementation::getImplementation($class)) {
$errors[] = pht('Choose the type of build step you want to add.');
}
if (!$errors) {
$new_uri = $this->getApplicationURI("step/new/{$plan_id}/{$class}/");
return id(new AphrontRedirectResponse())->setURI($new_uri);
}
}
$control = id(new AphrontFormRadioButtonControl())
->setName('class');
$all = HarbormasterBuildStepImplementation::getImplementations();
foreach ($all as $class => $implementation) {
$control->addButton(
$class,
$implementation->getName(),
$implementation->getGenericDescription());
}
if ($errors) {
$errors = id(new PHUIInfoView())
->setErrors($errors);
}
return $this->newDialog()
->setTitle(pht('Add New Step'))
->addSubmitButton(pht('Add Build Step'))
->addCancelButton($cancel_uri)
->appendChild($errors)
->appendParagraph(pht('Choose a type of build step to add:'))
->appendChild($control);
}
}
diff --git a/src/applications/harbormaster/controller/HarbormasterStepDeleteController.php b/src/applications/harbormaster/controller/HarbormasterStepDeleteController.php
index c0df595fad..94afff9d3b 100644
--- a/src/applications/harbormaster/controller/HarbormasterStepDeleteController.php
+++ b/src/applications/harbormaster/controller/HarbormasterStepDeleteController.php
@@ -1,51 +1,44 @@
<?php
final class HarbormasterStepDeleteController extends HarbormasterController {
- private $id;
-
- public function willProcessRequest(array $data) {
- $this->id = $data['id'];
- }
-
- public function processRequest() {
- $request = $this->getRequest();
- $viewer = $request->getUser();
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $this->getViewer();
$this->requireApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
- $id = $this->id;
+ $id = $request->getURIData('id');
$step = id(new HarbormasterBuildStepQuery())
->setViewer($viewer)
->withIDs(array($id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
->executeOne();
- if ($step === null) {
- throw new Exception(pht('Build step not found!'));
+ if (!$step) {
+ return new Aphront404Response();
}
$plan_id = $step->getBuildPlan()->getID();
$done_uri = $this->getApplicationURI('plan/'.$plan_id.'/');
if ($request->isDialogFormPost()) {
$step->delete();
return id(new AphrontRedirectResponse())->setURI($done_uri);
}
- $dialog = new AphrontDialogView();
- $dialog->setTitle(pht('Really Delete Step?'))
- ->setUser($viewer)
- ->addSubmitButton(pht('Delete Build Step'))
- ->addCancelButton($done_uri);
- $dialog->appendChild(
- phutil_tag(
- 'p',
- array(),
+ return $this->newDialog()
+ ->setTitle(pht('Really Delete Step?'))
+ ->appendParagraph(
pht(
"Are you sure you want to delete this step? ".
- "This can't be undone!")));
- return id(new AphrontDialogResponse())->setDialog($dialog);
+ "This can't be undone!"))
+ ->addCancelButton($done_uri)
+ ->addSubmitButton(pht('Delete Build Step'));
}
}
diff --git a/src/applications/harbormaster/controller/HarbormasterStepEditController.php b/src/applications/harbormaster/controller/HarbormasterStepEditController.php
index c753bd2181..9734f9652f 100644
--- a/src/applications/harbormaster/controller/HarbormasterStepEditController.php
+++ b/src/applications/harbormaster/controller/HarbormasterStepEditController.php
@@ -1,235 +1,237 @@
<?php
final class HarbormasterStepEditController extends HarbormasterController {
- private $id;
- private $planID;
- private $className;
-
- public function willProcessRequest(array $data) {
- $this->id = idx($data, 'id');
- $this->planID = idx($data, 'plan');
- $this->className = idx($data, 'class');
- }
-
- public function processRequest() {
- $request = $this->getRequest();
- $viewer = $request->getUser();
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $this->getViewer();
$this->requireApplicationCapability(
HarbormasterManagePlansCapability::CAPABILITY);
- if ($this->id) {
+ $id = $request->getURIData('id');
+ if ($id) {
$step = id(new HarbormasterBuildStepQuery())
->setViewer($viewer)
- ->withIDs(array($this->id))
+ ->withIDs(array($id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
->executeOne();
if (!$step) {
return new Aphront404Response();
}
$plan = $step->getBuildPlan();
$is_new = false;
} else {
+ $plan_id = $request->getURIData('plan');
+ $class = $request->getURIData('class');
+
$plan = id(new HarbormasterBuildPlanQuery())
- ->setViewer($viewer)
- ->withIDs(array($this->planID))
- ->executeOne();
+ ->setViewer($viewer)
+ ->withIDs(array($plan_id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
if (!$plan) {
return new Aphront404Response();
}
- $impl = HarbormasterBuildStepImplementation::getImplementation(
- $this->className);
+ $impl = HarbormasterBuildStepImplementation::getImplementation($class);
if (!$impl) {
return new Aphront404Response();
}
$step = HarbormasterBuildStep::initializeNewStep($viewer)
->setBuildPlanPHID($plan->getPHID())
- ->setClassName($this->className);
+ ->setClassName($class);
$is_new = true;
}
$plan_uri = $this->getApplicationURI('plan/'.$plan->getID().'/');
$implementation = $step->getStepImplementation();
$field_list = PhabricatorCustomField::getObjectFields(
$step,
PhabricatorCustomField::ROLE_EDIT);
$field_list
->setViewer($viewer)
->readFieldsFromStorage($step);
$e_name = true;
$v_name = $step->getName();
$e_description = true;
$v_description = $step->getDescription();
$e_depends_on = true;
$v_depends_on = $step->getDetail('dependsOn', array());
$errors = array();
$validation_exception = null;
if ($request->isFormPost()) {
$e_name = null;
$v_name = $request->getStr('name');
$e_description = null;
$v_description = $request->getStr('description');
$e_depends_on = null;
$v_depends_on = $request->getArr('dependsOn');
$xactions = $field_list->buildFieldTransactionsFromRequest(
new HarbormasterBuildStepTransaction(),
$request);
$editor = id(new HarbormasterBuildStepEditor())
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request);
$name_xaction = id(new HarbormasterBuildStepTransaction())
->setTransactionType(HarbormasterBuildStepTransaction::TYPE_NAME)
->setNewValue($v_name);
array_unshift($xactions, $name_xaction);
$depends_on_xaction = id(new HarbormasterBuildStepTransaction())
->setTransactionType(
HarbormasterBuildStepTransaction::TYPE_DEPENDS_ON)
->setNewValue($v_depends_on);
array_unshift($xactions, $depends_on_xaction);
$description_xaction = id(new HarbormasterBuildStepTransaction())
->setTransactionType(
HarbormasterBuildStepTransaction::TYPE_DESCRIPTION)
->setNewValue($v_description);
array_unshift($xactions, $description_xaction);
if ($is_new) {
// When creating a new step, make sure we have a create transaction
// so we'll apply the transactions even if the step has no
// configurable options.
$create_xaction = id(new HarbormasterBuildStepTransaction())
->setTransactionType(HarbormasterBuildStepTransaction::TYPE_CREATE);
array_unshift($xactions, $create_xaction);
}
try {
$editor->applyTransactions($step, $xactions);
return id(new AphrontRedirectResponse())->setURI($plan_uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
}
}
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setName('name')
->setLabel(pht('Name'))
->setError($e_name)
->setValue($v_name));
$form
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(id(new HarbormasterBuildDependencyDatasource())
->setParameters(array(
'planPHID' => $plan->getPHID(),
'stepPHID' => $is_new ? null : $step->getPHID(),
)))
->setName('dependsOn')
->setLabel(pht('Depends On'))
->setError($e_depends_on)
->setValue($v_depends_on));
$field_list->appendFieldsToForm($form);
$form
->appendChild(
id(new PhabricatorRemarkupControl())
->setUser($viewer)
->setName('description')
->setLabel(pht('Description'))
->setError($e_description)
->setValue($v_description));
if ($is_new) {
$submit = pht('Create Build Step');
$header = pht('New Step: %s', $implementation->getName());
$crumb = pht('Add Step');
} else {
$submit = pht('Save Build Step');
$header = pht('Edit Step: %s', $implementation->getName());
$crumb = pht('Edit Step');
}
$form->appendChild(
id(new AphrontFormSubmitControl())
->setValue($submit)
->addCancelButton($plan_uri));
$box = id(new PHUIObjectBoxView())
->setHeaderText($header)
->setValidationException($validation_exception)
->setForm($form);
$crumbs = $this->buildApplicationCrumbs();
$id = $plan->getID();
$crumbs->addTextCrumb(pht('Plan %d', $id), $plan_uri);
$crumbs->addTextCrumb($crumb);
$variables = $this->renderBuildVariablesTable();
if ($is_new) {
$xaction_view = null;
$timeline = null;
} else {
$timeline = $this->buildTransactionTimeline(
$step,
new HarbormasterBuildStepTransactionQuery());
$timeline->setShouldTerminate(true);
}
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$variables,
$timeline,
),
array(
'title' => $implementation->getName(),
));
}
private function renderBuildVariablesTable() {
$viewer = $this->getRequest()->getUser();
$variables = HarbormasterBuild::getAvailableBuildVariables();
ksort($variables);
$rows = array();
$rows[] = pht(
'The following variables can be used in most fields. '.
'To reference a variable, use `%s` in a field.',
'${name}');
$rows[] = pht('| Variable | Description |');
$rows[] = '|---|---|';
foreach ($variables as $name => $description) {
$rows[] = '| `'.$name.'` | '.$description.' |';
}
$rows = implode("\n", $rows);
$form = id(new AphrontFormView())
->setUser($viewer)
->appendRemarkupInstructions($rows);
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Build Variables'))
->appendChild($form);
}
}
diff --git a/src/applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php b/src/applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php
index fffc86c656..b63927b9d9 100644
--- a/src/applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php
+++ b/src/applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php
@@ -1,122 +1,128 @@
<?php
final class HarbormasterBuildPlan extends HarbormasterDAO
implements
PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface,
PhabricatorSubscribableInterface {
protected $name;
protected $planStatus;
const STATUS_ACTIVE = 'active';
const STATUS_DISABLED = 'disabled';
private $buildSteps = self::ATTACHABLE;
public static function initializeNewBuildPlan(PhabricatorUser $actor) {
return id(new HarbormasterBuildPlan())
->setPlanStatus(self::STATUS_ACTIVE);
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'sort128',
'planStatus' => 'text32',
),
self::CONFIG_KEY_SCHEMA => array(
'key_status' => array(
'columns' => array('planStatus'),
),
'key_name' => array(
'columns' => array('name'),
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
HarbormasterBuildPlanPHIDType::TYPECONST);
}
public function attachBuildSteps(array $steps) {
assert_instances_of($steps, 'HarbormasterBuildStep');
$this->buildSteps = $steps;
return $this;
}
public function getBuildSteps() {
return $this->assertAttached($this->buildSteps);
}
public function isDisabled() {
return ($this->getPlanStatus() == self::STATUS_DISABLED);
}
/* -( PhabricatorSubscribableInterface )----------------------------------- */
public function isAutomaticallySubscribed($phid) {
return false;
}
public function shouldShowSubscribersProperty() {
return true;
}
public function shouldAllowSubscription($phid) {
return true;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new HarbormasterBuildPlanEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new HarbormasterBuildPlanTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return PhabricatorPolicies::getMostOpenPolicy();
+ case PhabricatorPolicyCapability::CAN_EDIT:
+ // NOTE: In practice, this policy is always limited by the "Mangage
+ // Build Plans" policy.
+ return PhabricatorPolicies::getMostOpenPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return false;
}
public function describeAutomaticCapability($capability) {
return null;
}
+
}
diff --git a/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php
index 8446fa542c..90f4c33077 100644
--- a/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php
+++ b/src/applications/harbormaster/storage/configuration/HarbormasterBuildStep.php
@@ -1,158 +1,159 @@
<?php
final class HarbormasterBuildStep extends HarbormasterDAO
implements
PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface,
PhabricatorCustomFieldInterface {
protected $name;
protected $description;
protected $buildPlanPHID;
protected $className;
protected $details = array();
protected $sequence = 0;
private $buildPlan = self::ATTACHABLE;
private $customFields = self::ATTACHABLE;
private $implementation;
public static function initializeNewStep(PhabricatorUser $actor) {
return id(new HarbormasterBuildStep());
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'details' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'className' => 'text255',
'sequence' => 'uint32',
'description' => 'text',
// T6203/NULLABILITY
// This should not be nullable. Current `null` values indicate steps
// which predated editable names. These should be backfilled with
// default names, then the code for handling `null` shoudl be removed.
'name' => 'text255?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_plan' => array(
'columns' => array('buildPlanPHID'),
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
HarbormasterBuildStepPHIDType::TYPECONST);
}
public function attachBuildPlan(HarbormasterBuildPlan $plan) {
$this->buildPlan = $plan;
return $this;
}
public function getBuildPlan() {
return $this->assertAttached($this->buildPlan);
}
public function getDetail($key, $default = null) {
return idx($this->details, $key, $default);
}
public function setDetail($key, $value) {
$this->details[$key] = $value;
return $this;
}
public function getName() {
if (strlen($this->name)) {
return $this->name;
}
return $this->getStepImplementation()->getName();
}
public function getStepImplementation() {
if ($this->implementation === null) {
$obj = HarbormasterBuildStepImplementation::requireImplementation(
$this->className);
$obj->loadSettings($this);
$this->implementation = $obj;
}
return $this->implementation;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new HarbormasterBuildStepEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new HarbormasterBuildStepTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
return $this->getBuildPlan()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getBuildPlan()->hasAutomaticCapability($capability, $viewer);
}
public function describeAutomaticCapability($capability) {
return pht('A build step has the same policies as its build plan.');
}
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
public function getCustomFieldSpecificationForRole($role) {
return array();
}
public function getCustomFieldBaseClass() {
return 'HarbormasterBuildStepCustomField';
}
public function getCustomFields() {
return $this->assertAttached($this->customFields);
}
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
$this->customFields = $fields;
return $this;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Jun 9, 9:34 PM (4 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
140154
Default Alt Text
(50 KB)

Event Timeline