1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-23 14:00:56 +01:00

Add a trigger rule to reassign a task

Summary:
Ref T13269. Workboard triggers can now reassign tasks on column drop. Also sprinkles some `setViewer()` calls in places that needed them.

This mostly works, but a few issues:

* To set the owner to unassigned, you must explicitly put the "No Owner" token in the typeahead. Maybe this should just figure out you've put nothing in that field and set it for you?
* I'm pretty sure this was already broken, but if you change the rule type from a tokenizer to a different type, the default for the field doesn't populate correctly: {F6312227}

Also adds a new hook for trigger rules: `getValueForField($value)` which allows you to transform a value stored in the DB into a form suitable for setting on a form control.

Test Plan: Dragged tasks between columns and observed new owners as expected. Didn't try to get fancy to assign tasks to deleted users, users that the viewer can't see, bot users, etc etc. I'm relying on the underlying transaction to hopefully do the right thing.

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin

Maniphest Tasks: T13269

Differential Revision: https://secure.phabricator.com/D20329
This commit is contained in:
Austin McKinley 2019-04-04 16:30:42 -07:00
parent e45ccdd892
commit 7e1743a959
8 changed files with 170 additions and 9 deletions

View file

@ -4202,6 +4202,7 @@ phutil_register_library_map(array(
'PhabricatorProjectTriggerEditor' => 'applications/project/editor/PhabricatorProjectTriggerEditor.php', 'PhabricatorProjectTriggerEditor' => 'applications/project/editor/PhabricatorProjectTriggerEditor.php',
'PhabricatorProjectTriggerInvalidRule' => 'applications/project/trigger/PhabricatorProjectTriggerInvalidRule.php', 'PhabricatorProjectTriggerInvalidRule' => 'applications/project/trigger/PhabricatorProjectTriggerInvalidRule.php',
'PhabricatorProjectTriggerListController' => 'applications/project/controller/trigger/PhabricatorProjectTriggerListController.php', 'PhabricatorProjectTriggerListController' => 'applications/project/controller/trigger/PhabricatorProjectTriggerListController.php',
'PhabricatorProjectTriggerManiphestOwnerRule' => 'applications/project/trigger/PhabricatorProjectTriggerManiphestOwnerRule.php',
'PhabricatorProjectTriggerManiphestPriorityRule' => 'applications/project/trigger/PhabricatorProjectTriggerManiphestPriorityRule.php', 'PhabricatorProjectTriggerManiphestPriorityRule' => 'applications/project/trigger/PhabricatorProjectTriggerManiphestPriorityRule.php',
'PhabricatorProjectTriggerManiphestStatusRule' => 'applications/project/trigger/PhabricatorProjectTriggerManiphestStatusRule.php', 'PhabricatorProjectTriggerManiphestStatusRule' => 'applications/project/trigger/PhabricatorProjectTriggerManiphestStatusRule.php',
'PhabricatorProjectTriggerNameTransaction' => 'applications/project/xaction/trigger/PhabricatorProjectTriggerNameTransaction.php', 'PhabricatorProjectTriggerNameTransaction' => 'applications/project/xaction/trigger/PhabricatorProjectTriggerNameTransaction.php',
@ -10368,6 +10369,7 @@ phutil_register_library_map(array(
'PhabricatorProjectTriggerEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorProjectTriggerEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorProjectTriggerInvalidRule' => 'PhabricatorProjectTriggerRule', 'PhabricatorProjectTriggerInvalidRule' => 'PhabricatorProjectTriggerRule',
'PhabricatorProjectTriggerListController' => 'PhabricatorProjectTriggerController', 'PhabricatorProjectTriggerListController' => 'PhabricatorProjectTriggerController',
'PhabricatorProjectTriggerManiphestOwnerRule' => 'PhabricatorProjectTriggerRule',
'PhabricatorProjectTriggerManiphestPriorityRule' => 'PhabricatorProjectTriggerRule', 'PhabricatorProjectTriggerManiphestPriorityRule' => 'PhabricatorProjectTriggerRule',
'PhabricatorProjectTriggerManiphestStatusRule' => 'PhabricatorProjectTriggerRule', 'PhabricatorProjectTriggerManiphestStatusRule' => 'PhabricatorProjectTriggerRule',
'PhabricatorProjectTriggerNameTransaction' => 'PhabricatorProjectTriggerTransactionType', 'PhabricatorProjectTriggerNameTransaction' => 'PhabricatorProjectTriggerTransactionType',

View file

@ -577,6 +577,11 @@ final class PhabricatorProjectBoardViewController
$panel->addHeaderAction($column_menu); $panel->addHeaderAction($column_menu);
if ($column->canHaveTrigger()) { if ($column->canHaveTrigger()) {
$trigger = $column->getTrigger();
if ($trigger) {
$trigger->setViewer($viewer);
}
$trigger_menu = $this->buildTriggerMenu($column); $trigger_menu = $this->buildTriggerMenu($column);
$panel->addHeaderAction($trigger_menu); $panel->addHeaderAction($trigger_menu);
} }

View file

@ -25,6 +25,8 @@ final class PhabricatorProjectTriggerEditController
$trigger = PhabricatorProjectTrigger::initializeNewTrigger(); $trigger = PhabricatorProjectTrigger::initializeNewTrigger();
} }
$trigger->setViewer($viewer);
$column_phid = $request->getStr('columnPHID'); $column_phid = $request->getStr('columnPHID');
if ($column_phid) { if ($column_phid) {
$column = id(new PhabricatorProjectColumnQuery()) $column = id(new PhabricatorProjectColumnQuery())
@ -272,6 +274,10 @@ final class PhabricatorProjectTriggerEditController
$rule_list = array_values($rule_list); $rule_list = array_values($rule_list);
$type_list = PhabricatorProjectTriggerRule::getAllTriggerRules(); $type_list = PhabricatorProjectTriggerRule::getAllTriggerRules();
foreach ($type_list as $rule) {
$rule->setViewer($this->getViewer());
}
$type_list = mpull($type_list, 'newTemplate'); $type_list = mpull($type_list, 'newTemplate');
$type_list = array_values($type_list); $type_list = array_values($type_list);

View file

@ -20,6 +20,7 @@ final class PhabricatorProjectTriggerViewController
if (!$trigger) { if (!$trigger) {
return new Aphront404Response(); return new Aphront404Response();
} }
$trigger->setViewer($viewer);
$rules_view = $this->newRulesView($trigger); $rules_view = $this->newRulesView($trigger);
$columns_view = $this->newColumnsView($trigger); $columns_view = $this->newColumnsView($trigger);

View file

@ -13,6 +13,7 @@ final class PhabricatorProjectTrigger
protected $editPolicy; protected $editPolicy;
private $triggerRules; private $triggerRules;
private $viewer;
private $usage = self::ATTACHABLE; private $usage = self::ATTACHABLE;
public static function initializeNewTrigger() { public static function initializeNewTrigger() {
@ -41,6 +42,15 @@ final class PhabricatorProjectTrigger
return PhabricatorProjectTriggerPHIDType::TYPECONST; return PhabricatorProjectTriggerPHIDType::TYPECONST;
} }
public function getViewer() {
return $this->viewer;
}
public function setViewer(PhabricatorUser $user) {
$this->viewer = $user;
return $this;
}
public function getDisplayName() { public function getDisplayName() {
$name = $this->getName(); $name = $this->getName();
if (strlen($name)) { if (strlen($name)) {
@ -72,11 +82,16 @@ final class PhabricatorProjectTrigger
parent::setRuleset($ruleset); parent::setRuleset($ruleset);
} }
public function getTriggerRules() { public function getTriggerRules($viewer = null) {
if ($this->triggerRules === null) { if ($this->triggerRules === null) {
if (!$viewer) {
$viewer = $this->getViewer();
}
$trigger_rules = self::newTriggerRulesFromRuleSpecifications( $trigger_rules = self::newTriggerRulesFromRuleSpecifications(
$this->getRuleset(), $this->getRuleset(),
$allow_invalid = true); $allow_invalid = true,
$viewer);
$this->triggerRules = $trigger_rules; $this->triggerRules = $trigger_rules;
} }
@ -86,7 +101,8 @@ final class PhabricatorProjectTrigger
public static function newTriggerRulesFromRuleSpecifications( public static function newTriggerRulesFromRuleSpecifications(
array $list, array $list,
$allow_invalid) { $allow_invalid,
PhabricatorUser $viewer) {
// NOTE: With "$allow_invalid" set, we're trying to preserve the database // NOTE: With "$allow_invalid" set, we're trying to preserve the database
// state in the rule structure, even if it includes rule types we don't // state in the rule structure, even if it includes rule types we don't
@ -124,7 +140,7 @@ final class PhabricatorProjectTrigger
if (!is_array($rule)) { if (!is_array($rule)) {
throw new PhabricatorProjectTriggerCorruptionException( throw new PhabricatorProjectTriggerCorruptionException(
pht( pht(
'Trigger ruleset is corrupt: rule (with key "%s") should be a '. 'Trigger ruleset is corrupt: rule (at index "%s") should be a '.
'rule specification, but is actually "%s".', 'rule specification, but is actually "%s".',
$key, $key,
phutil_describe_type($rule))); phutil_describe_type($rule)));
@ -140,7 +156,7 @@ final class PhabricatorProjectTrigger
} catch (PhutilTypeCheckException $ex) { } catch (PhutilTypeCheckException $ex) {
throw new PhabricatorProjectTriggerCorruptionException( throw new PhabricatorProjectTriggerCorruptionException(
pht( pht(
'Trigger ruleset is corrupt: rule (with key "%s") is not a '. 'Trigger ruleset is corrupt: rule (at index "%s") is not a '.
'valid rule specification: %s', 'valid rule specification: %s',
$key, $key,
$ex->getMessage())); $ex->getMessage()));
@ -179,6 +195,7 @@ final class PhabricatorProjectTrigger
->setRecord($record) ->setRecord($record)
->setException($ex); ->setException($ex);
} }
$rule->setViewer($viewer);
$trigger_rules[] = $rule; $trigger_rules[] = $rule;
} }
@ -206,9 +223,8 @@ final class PhabricatorProjectTrigger
$object) { $object) {
$trigger_xactions = array(); $trigger_xactions = array();
foreach ($this->getTriggerRules() as $rule) { foreach ($this->getTriggerRules($viewer) as $rule) {
$rule $rule
->setViewer($viewer)
->setTrigger($this) ->setTrigger($this)
->setColumn($column) ->setColumn($column)
->setObject($object); ->setObject($object);

View file

@ -0,0 +1,126 @@
<?php
final class PhabricatorProjectTriggerManiphestOwnerRule
extends PhabricatorProjectTriggerRule {
const TRIGGERTYPE = 'task.owner';
public function getSelectControlName() {
return pht('Assign task to');
}
protected function getValueForEditorField() {
return $this->getDatasource()->getWireTokens($this->getValue());
}
private function convertTokenizerValueToOwner($value) {
$value = head($value);
if ($value === PhabricatorPeopleNoOwnerDatasource::FUNCTION_TOKEN) {
$value = null;
}
return $value;
}
protected function assertValidRuleValue($value) {
if (!is_array($value)) {
throw new Exception(
pht(
'Owner rule value should be a list, but is not (value is "%s").',
phutil_describe_type($value)));
}
if (count($value) != 1) {
throw new Exception(
pht(
'Owner rule value should be a list of exactly one user PHID, or the '.
'token "none()" (value is "%s").',
implode(', ', $value)));
}
}
protected function newDropTransactions($object, $value) {
$value = $this->convertTokenizerValueToOwner($value);
return array(
$this->newTransaction()
->setTransactionType(ManiphestTaskOwnerTransaction::TRANSACTIONTYPE)
->setNewValue($value),
);
}
protected function newDropEffects($value) {
$owner_value = $this->convertTokenizerValueToOwner($value);
return array(
$this->newEffect()
->setIcon('fa-user')
->setContent($this->getRuleViewDescription($value))
->addCondition('owner', '!=', $owner_value),
);
}
protected function getDefaultValue() {
return null;
}
protected function getPHUIXControlType() {
return 'tokenizer';
}
protected function getDatasource() {
$datasource = id(new ManiphestAssigneeDatasource())
->setLimit(1);
if ($this->getViewer()) {
$datasource->setViewer($this->getViewer());
}
return $datasource;
}
protected function getPHUIXControlSpecification() {
$template = id(new AphrontTokenizerTemplateView())
->setViewer($this->getViewer());
$template_markup = $template->render();
$datasource = $this->getDatasource();
return array(
'markup' => (string)hsprintf('%s', $template_markup),
'config' => array(
'src' => $datasource->getDatasourceURI(),
'browseURI' => $datasource->getBrowseURI(),
'placeholder' => $datasource->getPlaceholderText(),
'limit' => $datasource->getLimit(),
),
'value' => null,
);
}
public function getRuleViewLabel() {
return pht('Change Owner');
}
public function getRuleViewDescription($value) {
$value = $this->convertTokenizerValueToOwner($value);
if (!$value) {
return pht('Unassign task.');
} else {
return pht(
'Assign task to %s.',
phutil_tag(
'strong',
array(),
$this->getViewer()
->renderHandle($value)
->render()));
}
}
public function getRuleViewIcon($value) {
return id(new PHUIIconView())
->setIcon('fa-user', 'green');
}
}

View file

@ -37,6 +37,10 @@ abstract class PhabricatorProjectTriggerRule
return $this->getRecord()->getValue(); return $this->getRecord()->getValue();
} }
protected function getValueForEditorField() {
return $this->getValue();
}
abstract public function getSelectControlName(); abstract public function getSelectControlName();
abstract public function getRuleViewLabel(); abstract public function getRuleViewLabel();
abstract public function getRuleViewDescription($value); abstract public function getRuleViewDescription($value);
@ -130,7 +134,7 @@ abstract class PhabricatorProjectTriggerRule
return array( return array(
'type' => $record->getType(), 'type' => $record->getType(),
'value' => $record->getValue(), 'value' => $this->getValueForEditorField(),
'isValidRule' => $is_valid, 'isValidRule' => $is_valid,
'invalidView' => $invalid_view, 'invalidView' => $invalid_view,
); );

View file

@ -28,7 +28,8 @@ final class PhabricatorProjectTriggerRulesetTransaction
try { try {
PhabricatorProjectTrigger::newTriggerRulesFromRuleSpecifications( PhabricatorProjectTrigger::newTriggerRulesFromRuleSpecifications(
$ruleset, $ruleset,
$allow_invalid = false); $allow_invalid = false,
$xaction->getViewer());
} catch (PhabricatorProjectTriggerCorruptionException $ex) { } catch (PhabricatorProjectTriggerCorruptionException $ex) {
$errors[] = $this->newInvalidError( $errors[] = $this->newInvalidError(
pht( pht(