diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -806,6 +806,7 @@ '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', diff --git a/src/applications/herald/adapter/HeraldAdapter.php b/src/applications/herald/adapter/HeraldAdapter.php --- a/src/applications/herald/adapter/HeraldAdapter.php +++ b/src/applications/herald/adapter/HeraldAdapter.php @@ -101,6 +101,24 @@ 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 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 @@ 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 @@ /* -( 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 @@ 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 @@ 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 @@ } } - 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 @@ 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 @@ 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."'."); } diff --git a/src/applications/herald/adapter/HeraldCommitAdapter.php b/src/applications/herald/adapter/HeraldCommitAdapter.php --- a/src/applications/herald/adapter/HeraldCommitAdapter.php +++ b/src/applications/herald/adapter/HeraldCommitAdapter.php @@ -139,21 +139,25 @@ 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 @@ $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; diff --git a/src/applications/herald/adapter/HeraldDifferentialRevisionAdapter.php b/src/applications/herald/adapter/HeraldDifferentialRevisionAdapter.php --- a/src/applications/herald/adapter/HeraldDifferentialRevisionAdapter.php +++ b/src/applications/herald/adapter/HeraldDifferentialRevisionAdapter.php @@ -345,26 +345,30 @@ 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 @@ 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; diff --git a/src/applications/herald/adapter/HeraldManiphestTaskAdapter.php b/src/applications/herald/adapter/HeraldManiphestTaskAdapter.php --- a/src/applications/herald/adapter/HeraldManiphestTaskAdapter.php +++ b/src/applications/herald/adapter/HeraldManiphestTaskAdapter.php @@ -98,21 +98,25 @@ 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 @@ 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; diff --git a/src/applications/herald/adapter/HeraldPholioMockAdapter.php b/src/applications/herald/adapter/HeraldPholioMockAdapter.php --- a/src/applications/herald/adapter/HeraldPholioMockAdapter.php +++ b/src/applications/herald/adapter/HeraldPholioMockAdapter.php @@ -64,16 +64,20 @@ 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 @@ $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; diff --git a/src/applications/herald/controller/HeraldRuleController.php b/src/applications/herald/controller/HeraldRuleController.php --- a/src/applications/herald/controller/HeraldRuleController.php +++ b/src/applications/herald/controller/HeraldRuleController.php @@ -397,11 +397,15 @@ $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; } diff --git a/src/applications/herald/controller/HeraldTranscriptController.php b/src/applications/herald/controller/HeraldTranscriptController.php --- a/src/applications/herald/controller/HeraldTranscriptController.php +++ b/src/applications/herald/controller/HeraldTranscriptController.php @@ -354,13 +354,15 @@ $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 = ''; } diff --git a/src/applications/herald/extension/HeraldCustomAction.php b/src/applications/herald/extension/HeraldCustomAction.php new file mode 100644 --- /dev/null +++ b/src/applications/herald/extension/HeraldCustomAction.php @@ -0,0 +1,20 @@ +setHeraldAdapter($adapter); $this->setHeraldTranscript($xscript); - return $this->didApplyHeraldRules($object, $adapter, $xscript); + return array_merge( + $this->didApplyHeraldRules($object, $adapter, $xscript), + $adapter->getQueuedTransactions()); } protected function didApplyHeraldRules(