1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 16:52:41 +01:00

Allow triggers to be attached to and removed from workboard columns

Summary:
Depends on D20286. Ref T5474. Attaches triggers to columns and makes "Remove Trigger" work.

(There's no "pick an existing named trigger from a list" UI yet, but I plan to add that at some point.)

Test Plan: Attached and removed triggers, saw column UI update appropriately.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T5474

Differential Revision: https://secure.phabricator.com/D20287
This commit is contained in:
epriestley 2019-03-14 08:36:43 -07:00
parent 0204489a52
commit 916bf1a8f9
8 changed files with 257 additions and 40 deletions

View file

@ -4070,6 +4070,7 @@ phutil_register_library_map(array(
'PhabricatorProjectColumnPositionQuery' => 'applications/project/query/PhabricatorProjectColumnPositionQuery.php', 'PhabricatorProjectColumnPositionQuery' => 'applications/project/query/PhabricatorProjectColumnPositionQuery.php',
'PhabricatorProjectColumnPriorityOrder' => 'applications/project/order/PhabricatorProjectColumnPriorityOrder.php', 'PhabricatorProjectColumnPriorityOrder' => 'applications/project/order/PhabricatorProjectColumnPriorityOrder.php',
'PhabricatorProjectColumnQuery' => 'applications/project/query/PhabricatorProjectColumnQuery.php', 'PhabricatorProjectColumnQuery' => 'applications/project/query/PhabricatorProjectColumnQuery.php',
'PhabricatorProjectColumnRemoveTriggerController' => 'applications/project/controller/PhabricatorProjectColumnRemoveTriggerController.php',
'PhabricatorProjectColumnSearchEngine' => 'applications/project/query/PhabricatorProjectColumnSearchEngine.php', 'PhabricatorProjectColumnSearchEngine' => 'applications/project/query/PhabricatorProjectColumnSearchEngine.php',
'PhabricatorProjectColumnStatusOrder' => 'applications/project/order/PhabricatorProjectColumnStatusOrder.php', 'PhabricatorProjectColumnStatusOrder' => 'applications/project/order/PhabricatorProjectColumnStatusOrder.php',
'PhabricatorProjectColumnStatusTransaction' => 'applications/project/xaction/column/PhabricatorProjectColumnStatusTransaction.php', 'PhabricatorProjectColumnStatusTransaction' => 'applications/project/xaction/column/PhabricatorProjectColumnStatusTransaction.php',
@ -4078,6 +4079,7 @@ phutil_register_library_map(array(
'PhabricatorProjectColumnTransactionEditor' => 'applications/project/editor/PhabricatorProjectColumnTransactionEditor.php', 'PhabricatorProjectColumnTransactionEditor' => 'applications/project/editor/PhabricatorProjectColumnTransactionEditor.php',
'PhabricatorProjectColumnTransactionQuery' => 'applications/project/query/PhabricatorProjectColumnTransactionQuery.php', 'PhabricatorProjectColumnTransactionQuery' => 'applications/project/query/PhabricatorProjectColumnTransactionQuery.php',
'PhabricatorProjectColumnTransactionType' => 'applications/project/xaction/column/PhabricatorProjectColumnTransactionType.php', 'PhabricatorProjectColumnTransactionType' => 'applications/project/xaction/column/PhabricatorProjectColumnTransactionType.php',
'PhabricatorProjectColumnTriggerTransaction' => 'applications/project/xaction/column/PhabricatorProjectColumnTriggerTransaction.php',
'PhabricatorProjectConfigOptions' => 'applications/project/config/PhabricatorProjectConfigOptions.php', 'PhabricatorProjectConfigOptions' => 'applications/project/config/PhabricatorProjectConfigOptions.php',
'PhabricatorProjectConfiguredCustomField' => 'applications/project/customfield/PhabricatorProjectConfiguredCustomField.php', 'PhabricatorProjectConfiguredCustomField' => 'applications/project/customfield/PhabricatorProjectConfiguredCustomField.php',
'PhabricatorProjectController' => 'applications/project/controller/PhabricatorProjectController.php', 'PhabricatorProjectController' => 'applications/project/controller/PhabricatorProjectController.php',
@ -10184,6 +10186,7 @@ phutil_register_library_map(array(
'PhabricatorProjectColumnPositionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorProjectColumnPositionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorProjectColumnPriorityOrder' => 'PhabricatorProjectColumnOrder', 'PhabricatorProjectColumnPriorityOrder' => 'PhabricatorProjectColumnOrder',
'PhabricatorProjectColumnQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorProjectColumnQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorProjectColumnRemoveTriggerController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectColumnSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorProjectColumnSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorProjectColumnStatusOrder' => 'PhabricatorProjectColumnOrder', 'PhabricatorProjectColumnStatusOrder' => 'PhabricatorProjectColumnOrder',
'PhabricatorProjectColumnStatusTransaction' => 'PhabricatorProjectColumnTransactionType', 'PhabricatorProjectColumnStatusTransaction' => 'PhabricatorProjectColumnTransactionType',
@ -10192,6 +10195,7 @@ phutil_register_library_map(array(
'PhabricatorProjectColumnTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorProjectColumnTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorProjectColumnTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorProjectColumnTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorProjectColumnTransactionType' => 'PhabricatorModularTransactionType', 'PhabricatorProjectColumnTransactionType' => 'PhabricatorModularTransactionType',
'PhabricatorProjectColumnTriggerTransaction' => 'PhabricatorProjectColumnTransactionType',
'PhabricatorProjectConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorProjectConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorProjectConfiguredCustomField' => array( 'PhabricatorProjectConfiguredCustomField' => array(
'PhabricatorProjectStandardCustomField', 'PhabricatorProjectStandardCustomField',

View file

@ -89,6 +89,10 @@ final class PhabricatorProjectApplication extends PhabricatorApplication {
'background/' 'background/'
=> 'PhabricatorProjectBoardBackgroundController', => 'PhabricatorProjectBoardBackgroundController',
), ),
'column/' => array(
'remove/(?P<id>\d+)/' =>
'PhabricatorProjectColumnRemoveTriggerController',
),
'trigger/' => array( 'trigger/' => array(
$this->getQueryRoutePattern() => $this->getQueryRoutePattern() =>
'PhabricatorProjectTriggerListController', 'PhabricatorProjectTriggerListController',

View file

@ -574,6 +574,11 @@ final class PhabricatorProjectBoardViewController
$column_menu = $this->buildColumnMenu($project, $column); $column_menu = $this->buildColumnMenu($project, $column);
$panel->addHeaderAction($column_menu); $panel->addHeaderAction($column_menu);
if ($column->canHaveTrigger()) {
$trigger_menu = $this->buildTriggerMenu($column);
$panel->addHeaderAction($trigger_menu);
}
$count_tag = id(new PHUITagView()) $count_tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE) ->setType(PHUITagView::TYPE_SHADE)
->setColor(PHUITagView::COLOR_BLUE) ->setColor(PHUITagView::COLOR_BLUE)
@ -1172,40 +1177,6 @@ final class PhabricatorProjectBoardViewController
->setWorkflow(true); ->setWorkflow(true);
} }
if ($column->canHaveTrigger()) {
$column_items[] = id(new PhabricatorActionView())
->setType(PhabricatorActionView::TYPE_DIVIDER);
$trigger = $column->getTrigger();
if (!$trigger) {
$set_uri = $this->getApplicationURI(
new PhutilURI(
'trigger/edit/',
array(
'columnPHID' => $column->getPHID(),
)));
$column_items[] = id(new PhabricatorActionView())
->setIcon('fa-cogs')
->setName(pht('New Trigger...'))
->setHref($set_uri)
->setDisabled(!$can_edit);
} else {
$column_items[] = id(new PhabricatorActionView())
->setIcon('fa-cogs')
->setName(pht('View Trigger'))
->setHref($trigger->getURI())
->setDisabled(!$can_edit);
}
$column_items[] = id(new PhabricatorActionView())
->setIcon('fa-times')
->setName(pht('Remove Trigger'))
->setHref('#')
->setWorkflow(true)
->setDisabled(!$can_edit || !$trigger);
}
$column_menu = id(new PhabricatorActionListView()) $column_menu = id(new PhabricatorActionListView())
->setUser($viewer); ->setUser($viewer);
foreach ($column_items as $item) { foreach ($column_items as $item) {
@ -1213,7 +1184,7 @@ final class PhabricatorProjectBoardViewController
} }
$column_button = id(new PHUIIconView()) $column_button = id(new PHUIIconView())
->setIcon('fa-caret-down') ->setIcon('fa-pencil')
->setHref('#') ->setHref('#')
->addSigil('boards-dropdown-menu') ->addSigil('boards-dropdown-menu')
->setMetadata( ->setMetadata(
@ -1224,6 +1195,85 @@ final class PhabricatorProjectBoardViewController
return $column_button; return $column_button;
} }
private function buildTriggerMenu(PhabricatorProjectColumn $column) {
$viewer = $this->getViewer();
$trigger = $column->getTrigger();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$column,
PhabricatorPolicyCapability::CAN_EDIT);
$trigger_items = array();
if (!$trigger) {
$set_uri = $this->getApplicationURI(
new PhutilURI(
'trigger/edit/',
array(
'columnPHID' => $column->getPHID(),
)));
$trigger_items[] = id(new PhabricatorActionView())
->setIcon('fa-cogs')
->setName(pht('New Trigger...'))
->setHref($set_uri)
->setDisabled(!$can_edit);
} else {
$trigger_items[] = id(new PhabricatorActionView())
->setIcon('fa-cogs')
->setName(pht('View Trigger'))
->setHref($trigger->getURI())
->setDisabled(!$can_edit);
}
$remove_uri = $this->getApplicationURI(
new PhutilURI(
urisprintf(
'column/remove/%d/',
$column->getID())));
$trigger_items[] = id(new PhabricatorActionView())
->setIcon('fa-times')
->setName(pht('Remove Trigger'))
->setHref($remove_uri)
->setWorkflow(true)
->setDisabled(!$can_edit || !$trigger);
$trigger_menu = id(new PhabricatorActionListView())
->setUser($viewer);
foreach ($trigger_items as $item) {
$trigger_menu->addAction($item);
}
if ($trigger) {
$trigger_icon = 'fa-cogs';
} else {
$trigger_icon = 'fa-cogs grey';
}
if ($trigger) {
$trigger_tip = array(
pht('%s: %s', $trigger->getObjectName(), $trigger->getDisplayName()),
$trigger->getRulesDescription(),
);
$trigger_tip = implode("\n", $trigger_tip);
} else {
$trigger_tip = pht('No column trigger.');
}
$trigger_button = id(new PHUIIconView())
->setIcon($trigger_icon)
->setHref('#')
->addSigil('boards-dropdown-menu')
->addSigil('has-tooltip')
->setMetadata(
array(
'items' => hsprintf('%s', $trigger_menu),
'tip' => $trigger_tip,
));
return $trigger_button;
}
/** /**
* Add current state parameters (like order and the visibility of hidden * Add current state parameters (like order and the visibility of hidden

View file

@ -0,0 +1,60 @@
<?php
final class PhabricatorProjectColumnRemoveTriggerController
extends PhabricatorProjectBoardController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$column = id(new PhabricatorProjectColumnQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$column) {
return new Aphront404Response();
}
$done_uri = $column->getBoardURI();
if (!$column->getTriggerPHID()) {
return $this->newDialog()
->setTitle(pht('No Trigger'))
->appendParagraph(
pht('This column does not have a trigger.'))
->addCancelButton($done_uri);
}
if ($request->isFormPost()) {
$column_xactions = array();
$column_xactions[] = $column->getApplicationTransactionTemplate()
->setTransactionType(
PhabricatorProjectColumnTriggerTransaction::TRANSACTIONTYPE)
->setNewValue(null);
$column_editor = $column->getApplicationTransactionEditor()
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$column_editor->applyTransactions($column, $column_xactions);
return id(new AphrontRedirectResponse())->setURI($done_uri);
}
$body = pht('Really remove the trigger from this column?');
return $this->newDialog()
->setTitle(pht('Remove Trigger'))
->appendParagraph($body)
->addSubmitButton(pht('Remove Trigger'))
->addCancelButton($done_uri);
}
}

View file

@ -93,13 +93,16 @@ final class PhabricatorProjectTriggerEditController
if ($column) { if ($column) {
$column_xactions = array(); $column_xactions = array();
// TODO: Modularize column transactions so we can change the column $column_xactions[] = $column->getApplicationTransactionTemplate()
// trigger here. For now, this does nothing. ->setTransactionType(
PhabricatorProjectColumnTriggerTransaction::TRANSACTIONTYPE)
->setNewValue($trigger->getPHID());
$column_editor = $column->getApplicationTransactionEditor() $column_editor = $column->getApplicationTransactionEditor()
->setActor($viewer) ->setActor($viewer)
->setContentSourceFromRequest($request) ->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true); ->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$column_editor->applyTransactions($column, $column_xactions); $column_editor->applyTransactions($column, $column_xactions);

View file

@ -148,7 +148,7 @@ final class PhabricatorProjectColumnQuery
$triggers = id(new PhabricatorProjectTriggerQuery()) $triggers = id(new PhabricatorProjectTriggerQuery())
->setViewer($this->getViewer()) ->setViewer($this->getViewer())
->setParentQuery($this) ->setParentQuery($this)
->withPHIDs(array($this->getPHID())) ->withPHIDs($trigger_phids)
->execute(); ->execute();
$triggers = mpull($triggers, null, 'getPHID'); $triggers = mpull($triggers, null, 'getPHID');
} else { } else {

View file

@ -60,6 +60,11 @@ final class PhabricatorProjectTrigger
return pht('Trigger %d', $this->getID()); return pht('Trigger %d', $this->getID());
} }
public function getRulesDescription() {
// TODO: Summarize the trigger rules in human-readable text.
return pht('Does things.');
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */ /* -( PhabricatorApplicationTransactionInterface )------------------------- */
@ -102,7 +107,20 @@ final class PhabricatorProjectTrigger
public function destroyObjectPermanently( public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) { PhabricatorDestructionEngine $engine) {
$this->delete();
$this->openTransaction();
$conn = $this->establishConnection('w');
// Remove the reference to this trigger from any columns which use it.
queryfx(
$conn,
'UPDATE %R SET triggerPHID = null WHERE triggerPHID = %s',
new PhabricatorProjectColumn(),
$this->getPHID());
$this->delete();
$this->saveTransaction();
} }
} }

View file

@ -0,0 +1,78 @@
<?php
final class PhabricatorProjectColumnTriggerTransaction
extends PhabricatorProjectColumnTransactionType {
const TRANSACTIONTYPE = 'trigger';
public function generateOldValue($object) {
return $object->getTriggerPHID();
}
public function applyInternalEffects($object, $value) {
$object->setTriggerPHID($value);
}
public function getTitle() {
$old = $this->getOldValue();
$new = $this->getNewValue();
if (!$old) {
return pht(
'%s set the column trigger to %s.',
$this->renderAuthor(),
$this->renderNewHandle());
} else if (!$new) {
return pht(
'%s removed the trigger for this column (was %s).',
$this->renderAuthor(),
$this->renderOldHandle());
} else {
return pht(
'%s changed the trigger for this column from %s to %s.',
$this->renderAuthor(),
$this->renderOldHandle(),
$this->renderNewHandle());
}
}
public function validateTransactions($object, array $xactions) {
$actor = $this->getActor();
$errors = array();
foreach ($xactions as $xaction) {
$trigger_phid = $xaction->getNewValue();
// You can always remove a trigger.
if (!$trigger_phid) {
continue;
}
// You can't put a trigger on a column that can't have triggers, like
// a backlog column or a proxy column.
if (!$object->canHaveTrigger()) {
$errors[] = $this->newInvalidError(
pht('This column can not have a trigger.'),
$xaction);
continue;
}
$trigger = id(new PhabricatorProjectTriggerQuery())
->setViewer($actor)
->withPHIDs(array($trigger_phid))
->execute();
if (!$trigger) {
$errors[] = $this->newInvalidError(
pht(
'Trigger "%s" is not a valid trigger, or you do not have '.
'permission to view it.',
$trigger_phid),
$xaction);
continue;
}
}
return $errors;
}
}