mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-22 04:31:13 +01:00
Support custom actions in Herald
Summary: This was significantly easier than expected. Here's an example of what an extension class might look like: ``` <?php final class AddRiskReviewHeraldCustomAction extends HeraldCustomAction { public function appliesToAdapter(HeraldAdapter $adapter) { return $adapter instanceof HeraldDifferentialRevisionAdapter; } public function appliesToRuleType($rule_type) { return $rule_type == HeraldRuleTypeConfig::RULE_TYPE_GLOBAL || $rule_type == HeraldRuleTypeConfig::RULE_TYPE_OBJECT; } public function getActionKey() { return 'custom:add-risk'; } public function getActionName() { return 'Add risk rating (JSON)'; } public function getActionType() { return HeraldAdapter::VALUE_TEXT; } public function applyEffect( HeraldAdapter $adapter, $object, HeraldEffect $effect) { $key = "phragile:risk-rating"; // Read existing value. $field_list = PhabricatorCustomField::getObjectFields( $object, PhabricatorCustomField::ROLE_VIEW); $field_list->readFieldsFromStorage($object); $field_list = mpull($field_list->getFields(), null, 'getFieldKey'); $field = $field_list[$key]; $field->setObject($object); $field->setViewer(PhabricatorUser::getOmnipotentUser()); $risk = $field->getValue(); $old_risk = $risk; // PHP copies arrays by default! // Add new value to array. $herald_args = phutil_json_decode($effect->getTarget()); $risk[$herald_args['key']] = array( 'value' => $herald_args['value'], 'reason' => $herald_args['reason']); $risk_key = $herald_args['key']; // Set new value. $adapter->queueTransaction( id(new DifferentialTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD) ->setMetadataValue('customfield:key', $key) ->setOldValue($old_risk) ->setNewValue($risk)); return new HeraldApplyTranscript( $effect, true, pht( 'Modifying automatic risk ratings (key: %s)!', $risk_key)); } } ``` Test Plan: Created a custom action for differential revisions, set up a Herald rule to match and trigger the custom action, did 'arc diff' and saw the action trigger in the transcripts. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: locutus, edutibau, ite-klass, epriestley, Korvin Maniphest Tasks: T4884 Differential Revision: https://secure.phabricator.com/D8784
This commit is contained in:
parent
c9366acbec
commit
88aba65d54
10 changed files with 212 additions and 75 deletions
|
@ -806,6 +806,7 @@ phutil_register_library_map(array(
|
|||
'HeraldCondition' => 'applications/herald/storage/HeraldCondition.php',
|
||||
'HeraldConditionTranscript' => 'applications/herald/storage/transcript/HeraldConditionTranscript.php',
|
||||
'HeraldController' => 'applications/herald/controller/HeraldController.php',
|
||||
'HeraldCustomAction' => 'applications/herald/extension/HeraldCustomAction.php',
|
||||
'HeraldDAO' => 'applications/herald/storage/HeraldDAO.php',
|
||||
'HeraldDifferentialRevisionAdapter' => 'applications/herald/adapter/HeraldDifferentialRevisionAdapter.php',
|
||||
'HeraldDisableController' => 'applications/herald/controller/HeraldDisableController.php',
|
||||
|
|
|
@ -101,6 +101,24 @@ abstract class HeraldAdapter {
|
|||
private $contentSource;
|
||||
private $isNewObject;
|
||||
private $customFields = false;
|
||||
private $customActions = null;
|
||||
private $queuedTransactions = array();
|
||||
|
||||
public function getCustomActions() {
|
||||
if ($this->customActions === null) {
|
||||
$this->customActions = id(new PhutilSymbolLoader())
|
||||
->setAncestorClass('HeraldCustomAction')
|
||||
->loadObjects();
|
||||
|
||||
foreach ($this->customActions as $key => $object) {
|
||||
if (!$object->appliesToAdapter($this)) {
|
||||
unset($this->customActions[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->customActions;
|
||||
}
|
||||
|
||||
public function setContentSource(PhabricatorContentSource $content_source) {
|
||||
$this->contentSource = $content_source;
|
||||
|
@ -145,7 +163,24 @@ abstract class HeraldAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
abstract public function applyHeraldEffects(array $effects);
|
||||
public abstract function applyHeraldEffects(array $effects);
|
||||
|
||||
protected function handleCustomHeraldEffect(HeraldEffect $effect) {
|
||||
foreach ($this->getCustomActions() as $custom_action) {
|
||||
if ($effect->getAction() == $custom_action->getActionKey()) {
|
||||
$result = $custom_action->applyEffect(
|
||||
$this,
|
||||
$this->getObject(),
|
||||
$effect);
|
||||
|
||||
if ($result !== null) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function isAvailableToUser(PhabricatorUser $viewer) {
|
||||
$applications = id(new PhabricatorApplicationQuery())
|
||||
|
@ -157,6 +192,14 @@ abstract class HeraldAdapter {
|
|||
return !empty($applications);
|
||||
}
|
||||
|
||||
public function queueTransaction($transaction) {
|
||||
$this->queuedTransactions[] = $transaction;
|
||||
}
|
||||
|
||||
public function getQueuedTransactions() {
|
||||
return $this->queuedTransactions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* NOTE: You generally should not override this; it exists to support legacy
|
||||
|
@ -645,13 +688,21 @@ abstract class HeraldAdapter {
|
|||
|
||||
/* -( Actions )------------------------------------------------------------ */
|
||||
|
||||
abstract public function getActions($rule_type);
|
||||
public function getActions($rule_type) {
|
||||
$results = array();
|
||||
foreach ($this->getCustomActions() as $custom_action) {
|
||||
if ($custom_action->appliesToRuleType($rule_type)) {
|
||||
$results[] = $custom_action->getActionKey();
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function getActionNameMap($rule_type) {
|
||||
switch ($rule_type) {
|
||||
case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL:
|
||||
case HeraldRuleTypeConfig::RULE_TYPE_OBJECT:
|
||||
return array(
|
||||
$standard = array(
|
||||
self::ACTION_NOTHING => pht('Do nothing'),
|
||||
self::ACTION_ADD_CC => pht('Add emails to CC'),
|
||||
self::ACTION_REMOVE_CC => pht('Remove emails from CC'),
|
||||
|
@ -666,8 +717,9 @@ abstract class HeraldAdapter {
|
|||
self::ACTION_REQUIRE_SIGNATURE => pht('Require legal signatures'),
|
||||
self::ACTION_BLOCK => pht('Block change with message'),
|
||||
);
|
||||
break;
|
||||
case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL:
|
||||
return array(
|
||||
$standard = array(
|
||||
self::ACTION_NOTHING => pht('Do nothing'),
|
||||
self::ACTION_ADD_CC => pht('Add me to CC'),
|
||||
self::ACTION_REMOVE_CC => pht('Remove me from CC'),
|
||||
|
@ -680,9 +732,19 @@ abstract class HeraldAdapter {
|
|||
self::ACTION_ADD_BLOCKING_REVIEWERS =>
|
||||
pht('Add me as a blocking reviewer'),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unknown rule type '{$rule_type}'!");
|
||||
}
|
||||
|
||||
foreach ($this->getCustomActions() as $custom_action) {
|
||||
if ($custom_action->appliesToRuleType($rule_type)) {
|
||||
$standard[$custom_action->getActionKey()] =
|
||||
$custom_action->getActionName();
|
||||
}
|
||||
}
|
||||
|
||||
return $standard;
|
||||
}
|
||||
|
||||
public function willSaveAction(
|
||||
|
@ -814,7 +876,7 @@ abstract class HeraldAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
public static function getValueTypeForAction($action, $rule_type) {
|
||||
public function getValueTypeForAction($action, $rule_type) {
|
||||
$is_personal = ($rule_type == HeraldRuleTypeConfig::RULE_TYPE_PERSONAL);
|
||||
|
||||
if ($is_personal) {
|
||||
|
@ -832,8 +894,6 @@ abstract class HeraldAdapter {
|
|||
return self::VALUE_FLAG_COLOR;
|
||||
case self::ACTION_ADD_PROJECTS:
|
||||
return self::VALUE_PROJECT;
|
||||
default:
|
||||
throw new Exception("Unknown or invalid action '{$action}'.");
|
||||
}
|
||||
} else {
|
||||
switch ($action) {
|
||||
|
@ -859,10 +919,18 @@ abstract class HeraldAdapter {
|
|||
return self::VALUE_LEGAL_DOCUMENTS;
|
||||
case self::ACTION_BLOCK:
|
||||
return self::VALUE_TEXT;
|
||||
default:
|
||||
throw new Exception("Unknown or invalid action '{$action}'.");
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->getCustomActions() as $custom_action) {
|
||||
if ($custom_action->appliesToRuleType($rule_type)) {
|
||||
if ($action === $custom_action->getActionKey()) {
|
||||
return $custom_action->getActionType();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("Unknown or invalid action '".$action."'.");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -139,21 +139,25 @@ final class HeraldCommitAdapter extends HeraldAdapter {
|
|||
switch ($rule_type) {
|
||||
case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL:
|
||||
case HeraldRuleTypeConfig::RULE_TYPE_OBJECT:
|
||||
return array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_EMAIL,
|
||||
self::ACTION_AUDIT,
|
||||
self::ACTION_APPLY_BUILD_PLANS,
|
||||
self::ACTION_NOTHING
|
||||
);
|
||||
return array_merge(
|
||||
array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_EMAIL,
|
||||
self::ACTION_AUDIT,
|
||||
self::ACTION_APPLY_BUILD_PLANS,
|
||||
self::ACTION_NOTHING
|
||||
),
|
||||
parent::getActions($rule_type));
|
||||
case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL:
|
||||
return array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_EMAIL,
|
||||
self::ACTION_FLAG,
|
||||
self::ACTION_AUDIT,
|
||||
self::ACTION_NOTHING,
|
||||
);
|
||||
return array_merge(
|
||||
array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_EMAIL,
|
||||
self::ACTION_FLAG,
|
||||
self::ACTION_AUDIT,
|
||||
self::ACTION_NOTHING,
|
||||
),
|
||||
parent::getActions($rule_type));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -544,7 +548,13 @@ final class HeraldCommitAdapter extends HeraldAdapter {
|
|||
$this->commit->getPHID());
|
||||
break;
|
||||
default:
|
||||
throw new Exception("No rules to handle action '{$action}'.");
|
||||
$custom_result = parent::handleCustomHeraldEffect($effect);
|
||||
if ($custom_result === null) {
|
||||
throw new Exception("No rules to handle action '{$action}'.");
|
||||
}
|
||||
|
||||
$result[] = $custom_result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
|
|
|
@ -345,26 +345,30 @@ final class HeraldDifferentialRevisionAdapter extends HeraldAdapter {
|
|||
public function getActions($rule_type) {
|
||||
switch ($rule_type) {
|
||||
case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL:
|
||||
return array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_REMOVE_CC,
|
||||
self::ACTION_EMAIL,
|
||||
self::ACTION_ADD_REVIEWERS,
|
||||
self::ACTION_ADD_BLOCKING_REVIEWERS,
|
||||
self::ACTION_APPLY_BUILD_PLANS,
|
||||
self::ACTION_REQUIRE_SIGNATURE,
|
||||
self::ACTION_NOTHING,
|
||||
);
|
||||
return array_merge(
|
||||
array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_REMOVE_CC,
|
||||
self::ACTION_EMAIL,
|
||||
self::ACTION_ADD_REVIEWERS,
|
||||
self::ACTION_ADD_BLOCKING_REVIEWERS,
|
||||
self::ACTION_APPLY_BUILD_PLANS,
|
||||
self::ACTION_REQUIRE_SIGNATURE,
|
||||
self::ACTION_NOTHING,
|
||||
),
|
||||
parent::getActions($rule_type));
|
||||
case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL:
|
||||
return array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_REMOVE_CC,
|
||||
self::ACTION_EMAIL,
|
||||
self::ACTION_FLAG,
|
||||
self::ACTION_ADD_REVIEWERS,
|
||||
self::ACTION_ADD_BLOCKING_REVIEWERS,
|
||||
self::ACTION_NOTHING,
|
||||
);
|
||||
return array_merge(
|
||||
array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_REMOVE_CC,
|
||||
self::ACTION_EMAIL,
|
||||
self::ACTION_FLAG,
|
||||
self::ACTION_ADD_REVIEWERS,
|
||||
self::ACTION_ADD_BLOCKING_REVIEWERS,
|
||||
self::ACTION_NOTHING,
|
||||
),
|
||||
parent::getActions($rule_type));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -491,7 +495,13 @@ final class HeraldDifferentialRevisionAdapter extends HeraldAdapter {
|
|||
pht('Required signatures.'));
|
||||
break;
|
||||
default:
|
||||
throw new Exception("No rules to handle action '{$action}'.");
|
||||
$custom_result = parent::handleCustomHeraldEffect($effect);
|
||||
if ($custom_result === null) {
|
||||
throw new Exception("No rules to handle action '{$action}'.");
|
||||
}
|
||||
|
||||
$result[] = $custom_result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
|
|
|
@ -98,21 +98,25 @@ final class HeraldManiphestTaskAdapter extends HeraldAdapter {
|
|||
public function getActions($rule_type) {
|
||||
switch ($rule_type) {
|
||||
case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL:
|
||||
return array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_EMAIL,
|
||||
self::ACTION_ASSIGN_TASK,
|
||||
self::ACTION_ADD_PROJECTS,
|
||||
self::ACTION_NOTHING,
|
||||
);
|
||||
return array_merge(
|
||||
array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_EMAIL,
|
||||
self::ACTION_ASSIGN_TASK,
|
||||
self::ACTION_ADD_PROJECTS,
|
||||
self::ACTION_NOTHING,
|
||||
),
|
||||
parent::getActions($rule_type));
|
||||
case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL:
|
||||
return array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_EMAIL,
|
||||
self::ACTION_FLAG,
|
||||
self::ACTION_ASSIGN_TASK,
|
||||
self::ACTION_NOTHING,
|
||||
);
|
||||
return array_merge(
|
||||
array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_EMAIL,
|
||||
self::ACTION_FLAG,
|
||||
self::ACTION_ASSIGN_TASK,
|
||||
self::ACTION_NOTHING,
|
||||
),
|
||||
parent::getActions($rule_type));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,7 +204,13 @@ final class HeraldManiphestTaskAdapter extends HeraldAdapter {
|
|||
pht('Added projects.'));
|
||||
break;
|
||||
default:
|
||||
throw new Exception("No rules to handle action '{$action}'.");
|
||||
$custom_result = parent::handleCustomHeraldEffect($effect);
|
||||
if ($custom_result === null) {
|
||||
throw new Exception("No rules to handle action '{$action}'.");
|
||||
}
|
||||
|
||||
$result[] = $custom_result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
|
|
|
@ -64,16 +64,20 @@ final class HeraldPholioMockAdapter extends HeraldAdapter {
|
|||
public function getActions($rule_type) {
|
||||
switch ($rule_type) {
|
||||
case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL:
|
||||
return array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_NOTHING,
|
||||
);
|
||||
return array_merge(
|
||||
array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_NOTHING,
|
||||
),
|
||||
parent::getActions($rule_type));
|
||||
case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL:
|
||||
return array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_FLAG,
|
||||
self::ACTION_NOTHING,
|
||||
);
|
||||
return array_merge(
|
||||
array(
|
||||
self::ACTION_ADD_CC,
|
||||
self::ACTION_FLAG,
|
||||
self::ACTION_NOTHING,
|
||||
),
|
||||
parent::getActions($rule_type));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,7 +137,13 @@ final class HeraldPholioMockAdapter extends HeraldAdapter {
|
|||
$this->getMock()->getPHID());
|
||||
break;
|
||||
default:
|
||||
throw new Exception("No rules to handle action '{$action}'.");
|
||||
$custom_result = parent::handleCustomHeraldEffect($effect);
|
||||
if ($custom_result === null) {
|
||||
throw new Exception("No rules to handle action '{$action}'.");
|
||||
}
|
||||
|
||||
$result[] = $custom_result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
|
|
|
@ -397,11 +397,15 @@ final class HeraldRuleController extends HeraldController {
|
|||
$current_value = $action->getTarget();
|
||||
break;
|
||||
default:
|
||||
$target_map = array();
|
||||
foreach ((array)$action->getTarget() as $fbid) {
|
||||
$target_map[$fbid] = $handles[$fbid]->getName();
|
||||
if (is_array($action->getTarget())) {
|
||||
$target_map = array();
|
||||
foreach ((array)$action->getTarget() as $fbid) {
|
||||
$target_map[$fbid] = $handles[$fbid]->getName();
|
||||
}
|
||||
$current_value = $target_map;
|
||||
} else {
|
||||
$current_value = $action->getTarget();
|
||||
}
|
||||
$current_value = $target_map;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -354,13 +354,15 @@ final class HeraldTranscriptController extends HeraldController {
|
|||
$target = $target;
|
||||
break;
|
||||
default:
|
||||
if ($target) {
|
||||
if (is_array($target) && $target) {
|
||||
foreach ($target as $k => $phid) {
|
||||
if (isset($handles[$phid])) {
|
||||
$target[$k] = $handles[$phid]->getName();
|
||||
}
|
||||
}
|
||||
$target = implode(', ', $target);
|
||||
} else if (is_string($target)) {
|
||||
$target = $target;
|
||||
} else {
|
||||
$target = '<empty>';
|
||||
}
|
||||
|
|
20
src/applications/herald/extension/HeraldCustomAction.php
Normal file
20
src/applications/herald/extension/HeraldCustomAction.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
abstract class HeraldCustomAction {
|
||||
|
||||
public abstract function appliesToAdapter(HeraldAdapter $adapter);
|
||||
|
||||
public abstract function appliesToRuleType($rule_type);
|
||||
|
||||
public abstract function getActionKey();
|
||||
|
||||
public abstract function getActionName();
|
||||
|
||||
public abstract function getActionType();
|
||||
|
||||
public abstract function applyEffect(
|
||||
HeraldAdapter $adapter,
|
||||
$object,
|
||||
HeraldEffect $effect);
|
||||
|
||||
}
|
|
@ -2197,7 +2197,9 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
$this->setHeraldAdapter($adapter);
|
||||
$this->setHeraldTranscript($xscript);
|
||||
|
||||
return $this->didApplyHeraldRules($object, $adapter, $xscript);
|
||||
return array_merge(
|
||||
$this->didApplyHeraldRules($object, $adapter, $xscript),
|
||||
$adapter->getQueuedTransactions());
|
||||
}
|
||||
|
||||
protected function didApplyHeraldRules(
|
||||
|
|
Loading…
Reference in a new issue