Phabricator:调整 Herald 规则以根据项目成员资格修改任务策略

Phabricator: Adjust Herald rules to modify task policies based on project membership

我尝试调整 Phabricator 的先驱规则,因此查看和编辑策略是根据项目成员设置的。目前无法自动调整基于项目的任务的查看和编辑策略。

我通过 Haskell 项目找到了 this modification,但这似乎不再有效。

谁能告诉我如何解决它们?

<?php
/**
 * Extends Herald with a custom 'Set "Visible To" policy' action for Maniphest
 * tasks.
 */
final class SetTaskViewPolicyHeraldAction extends HeraldAction {

  public function appliesToAdapter(HeraldAdapter $adapter) {
    return $adapter instanceof HeraldManiphestTaskAdapter;
  }

  public function appliesToRuleType($type) {
    return $type == HeraldRuleTypeConfig::RULE_TYPE_GLOBAL;
  }

  public function getActionKey() {
    return 'custom:view-policy';
  }

  public function getActionName() {
    return 'Set view policy to project';
  }

  public function getActionType() {
    return HeraldAdapter::VALUE_PROJECT;
  }

  public function applyEffect(
    HeraldAdapter $adapter,
    $object,
    HeraldEffect $effect) {

    // First off, ensure there's only one set project
    if (count($effect->getTarget()) != 1) {
      throw new HeraldInvalidConditionException(
        'Expected only one project to be set for visibility policy');
    }

    $project = $effect->getTarget();
    $project_phid = $project[0];

    // Set new value by queueing a transaction, and returning the transcript.
    $adapter->queueTransaction(
      id(new ManiphestTransaction())
      ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
      ->setNewValue($project_phid));

    return new HeraldApplyTranscript(
      $effect,
      true,
      pht('Set view policy of task'));
  }

}

我只添加了旧版本SetTaskViewPolicyHeraldAction.php的源代码,在this link还有文件SetTaskEditPolicyHeraldAction.php,这也与我的问题有关。

终于找到了解决方案,或者更准确地说是写了一个扩展。

<?php
final class HeraldManiphestMoveSpaceAction extends HeraldAction {
  // ACTIONCONST: internal ID, unique (assumably for mapping objects)
  const ACTIONCONST = 'space.move';
  // supposably key for mapping history entries
  const DO_MOVE_SPACE = 'do.move.space';
  // entry in Herald action selection drop down menu when configuring a rule
  public function getHeraldActionName() {
    return pht('Move to space');
  }
  // section in Herald action selection drop down menu
  public function getActionGroupKey() {
    return HeraldSupportActionGroup::ACTIONGROUPKEY;
  }
  // source for input field
  protected function getDatasource() {
    return new PhabricatorSpacesNamespaceDatasource();
  }
  // which UI element to show when configuring the action
  public function getHeraldActionStandardType() {
    return self::STANDARD_PHID_LIST;
  }
  // allowed applicable objects
  public function supportsObject($object) {
    return ($object instanceof PhabricatorProjectInterface);
  }
  // permitted user roles (globally or locally)
  public function supportsRuleType($rule_type) {
    return ($rule_type == HeraldRuleTypeConfig::RULE_TYPE_GLOBAL);
  }
  // appearance in transcript
  protected function getActionEffectMap() {
    return array(
      self::DO_MOVE_SPACE => array(
        'icon' => 'fa-diamond',
        'color' => 'green',
        'name' => pht('Moved to space'),
      ),
    );
  }
  // description of action that will be taken (present tense)
  public function renderActionDescription($value) {
    return pht('Move to space: %s.', $this->renderHandleList($value));
  }
  // description of action that has been taken (past tense, for history view etc.)
  protected function renderActionEffectDescription($type, $data) {
        switch ($type) {
      case self::DO_MOVE_SPACE:
        return pht(
          'Moved to %s space: %s.',
          phutil_count($data),
          $this->renderHandleList($data));
    }
  }
  // executed by Herald rules on objects that match condition (calls function applySpace)
  public function applyEffect($object, HeraldEffect $effect) {
    $current_space = array($object->getSpacePHID());
    // allowed objects for transaction
    $allowed_types = array(
      PhabricatorSpacesNamespacePHIDType::TYPECONST,
    );
    // loadStandardTargets() figures out the to-set spaces from the Phabricator IDs ($phids)
    // and excludes $current_space from this list, potentially resulting in an empty list (NULL).
    // Misconfigured Herald action may result in an empty $phids.
    $new_phids = $effect->getTarget();
    $new_spaces = $this->loadStandardTargets($new_phids, $allowed_types, $current_space);
    // if no spaces need to be set (either because of bad rule (see above comment), or space already manually set), avoid doing work
    if(!$new_spaces) {
      return;
    } else {
      // One object can only be at one space at a time. This silently fixes if one misconfigured Herald rule tries to move one object into different spaces. 
      $phid = head_key($new_spaces);
      $adapter = $this->getAdapter();
      $xaction = $adapter->newTransaction()
        ->setTransactionType(PhabricatorTransactions::TYPE_SPACE)
        ->setNewValue($phid);
      $adapter->queueTransaction($xaction);
      $this->logEffect(self::DO_MOVE_SPACE, array($phid));
    }
  }
}

这里应该注意一些事情(也记录在这个扩展的 Github repo 中)。我使用这个扩展主要是为了在相应的 space 中自动转移任务(基于项目管理),但它应该也适用于其他对象(所有可以有 space 的对象)——但我没有测试过它。

一个问题是,Herald(还)不明白对象只能有一个 space - 如果多个 Herald 规则适用,它们将全部执行,最后创建的那个将决定对象的space。作为解决方法,Github 存储库中还有一个补丁,它创建了一个先驱规则 "is exactly"。

diff --git a/src/applications/herald/adapter/HeraldAdapter.php b/src/applications/herald/adapter/HeraldAdapter.php
index 78ce86294..600033247 100644
--- a/src/applications/herald/adapter/HeraldAdapter.php
+++ b/src/applications/herald/adapter/HeraldAdapter.php
@@ -10,6 +10,7 @@ abstract class HeraldAdapter extends Phobject {
   const CONDITION_IS_NOT_ANY      = '!isany';
   const CONDITION_INCLUDE_ALL     = 'all';
   const CONDITION_INCLUDE_ANY     = 'any';
+  const CONDITION_INCLUDE_EXACTLY = 'exactly';
   const CONDITION_INCLUDE_NONE    = 'none';
   const CONDITION_IS_ME           = 'me';
   const CONDITION_IS_NOT_ME       = '!me';
@@ -335,6 +336,7 @@ abstract class HeraldAdapter extends Phobject {
       self::CONDITION_IS_NOT_ANY      => pht('is not any of'),
       self::CONDITION_INCLUDE_ALL     => pht('include all of'),
       self::CONDITION_INCLUDE_ANY     => pht('include any of'),
+      self::CONDITION_INCLUDE_EXACTLY => pht('is exactly'), // custom adaptation for projects
       self::CONDITION_INCLUDE_NONE    => pht('do not include'),
       self::CONDITION_IS_ME           => pht('is myself'),
       self::CONDITION_IS_NOT_ME       => pht('is not myself'),
@@ -432,6 +434,19 @@ abstract class HeraldAdapter extends Phobject {
         return (bool)array_select_keys(
           array_fuse($field_value),
           $condition_value);
+      case self::CONDITION_INCLUDE_EXACTLY:
+        if (!is_array($field_value)) {
+          throw new HeraldInvalidConditionException(
+            pht('Object produced non-array value!'));
+        }
+        if (!is_array($condition_value)) {
+          throw new HeraldInvalidConditionException(
+            pht('Expected condition value to be an array.'));
+        }
+
+        $have = array_select_keys(array_fuse($field_value), $condition_value);
+        return (count($have) == count($condition_value) &&
+                count($have) == count(array_fuse($field_value)));
       case self::CONDITION_INCLUDE_NONE:
         return !array_select_keys(
           array_fuse($field_value),
@@ -592,6 +607,7 @@ abstract class HeraldAdapter extends Phobject {
       case self::CONDITION_IS_NOT_ANY:
       case self::CONDITION_INCLUDE_ALL:
       case self::CONDITION_INCLUDE_ANY:
+      case self::CONDITION_INCLUDE_EXACTLY:
       case self::CONDITION_INCLUDE_NONE:
       case self::CONDITION_IS_ME:
       case self::CONDITION_IS_NOT_ME:
diff --git a/src/applications/herald/field/HeraldField.php b/src/applications/herald/field/HeraldField.php
index 2abed0ff1..8bbcfa3dd 100644
--- a/src/applications/herald/field/HeraldField.php
+++ b/src/applications/herald/field/HeraldField.php
@@ -58,6 +58,7 @@ abstract class HeraldField extends Phobject {
         return array(
           HeraldAdapter::CONDITION_INCLUDE_ALL,
           HeraldAdapter::CONDITION_INCLUDE_ANY,
+          HeraldAdapter::CONDITION_INCLUDE_EXACTLY,
           HeraldAdapter::CONDITION_INCLUDE_NONE,
           HeraldAdapter::CONDITION_EXISTS,
           HeraldAdapter::CONDITION_NOT_EXISTS,
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php
index 78c8caa5a..be267d2e4 100644
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php
@@ -235,6 +235,7 @@ abstract class PhabricatorStandardCustomFieldPHIDs
     return array(
       HeraldAdapter::CONDITION_INCLUDE_ALL,
       HeraldAdapter::CONDITION_INCLUDE_ANY,
+      HeraldAdapter::CONDITION_INCLUDE_EXACTLY,
       HeraldAdapter::CONDITION_INCLUDE_NONE,
       HeraldAdapter::CONDITION_EXISTS,
       HeraldAdapter::CONDITION_NOT_EXISTS,

查看 Github repo 中的 README.md 了解更多详情。