Page MenuHomePhabricator

D8695.diff
No OneTemporary

D8695.diff

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
@@ -1,7 +1,7 @@
<?php
/**
- * @group herald
+ * @task customfield Custom Field Integration
*/
abstract class HeraldAdapter {
@@ -60,6 +60,7 @@
const CONDITION_EXISTS = 'exists';
const CONDITION_NOT_EXISTS = '!exists';
const CONDITION_UNCONDITIONALLY = 'unconditionally';
+ const CONDITION_NEVER = 'never';
const CONDITION_REGEXP_PAIR = 'regexp-pair';
const CONDITION_HAS_BIT = 'bit';
const CONDITION_NOT_BIT = '!bit';
@@ -97,6 +98,7 @@
private $contentSource;
private $isNewObject;
+ private $customFields = false;
public function setContentSource(PhabricatorContentSource $content_source) {
$this->contentSource = $content_source;
@@ -132,6 +134,10 @@
case self::FIELD_IS_NEW_OBJECT:
return $this->getIsNewObject();
default:
+ if ($this->isHeraldCustomKey($field_name)) {
+ return $this->getCustomFieldValue($field_name);
+ }
+
throw new Exception(
"Unknown field '{$field_name}'!");
}
@@ -195,10 +201,20 @@
public function getFields() {
- return array(
- self::FIELD_ALWAYS,
- self::FIELD_RULE,
- );
+ $fields = array();
+
+ $fields[] = self::FIELD_ALWAYS;
+ $fields[] = self::FIELD_RULE;
+
+ $custom_fields = $this->getCustomFields();
+ if ($custom_fields) {
+ foreach ($custom_fields->getFields() as $custom_field) {
+ $key = $custom_field->getFieldKey();
+ $fields[] = $this->getHeraldKeyFromCustomKey($key);
+ }
+ }
+
+ return $fields;
}
public function getFieldNameMap() {
@@ -242,7 +258,7 @@
self::FIELD_TASK_PRIORITY => pht('Task priority'),
self::FIELD_ARCANIST_PROJECT => pht('Arcanist Project'),
self::FIELD_PUSHER_IS_COMMITTER => pht('Pusher same as committer'),
- );
+ ) + $this->getCustomFieldNameMap();
}
@@ -270,6 +286,7 @@
self::CONDITION_EXISTS => pht('exists'),
self::CONDITION_NOT_EXISTS => pht('does not exist'),
self::CONDITION_UNCONDITIONALLY => '', // don't show anything!
+ self::CONDITION_NEVER => '', // don't show anything!
self::CONDITION_REGEXP_PAIR => pht('matches regexp pair'),
self::CONDITION_HAS_BIT => pht('has bit'),
self::CONDITION_NOT_BIT => pht('lacks bit'),
@@ -380,6 +397,9 @@
self::CONDITION_IS_FALSE,
);
default:
+ if ($this->isHeraldCustomKey($field)) {
+ return $this->getCustomFieldConditions($field);
+ }
throw new Exception(
"This adapter does not define conditions for field '{$field}'!");
}
@@ -456,6 +476,8 @@
return !$field_value;
case self::CONDITION_UNCONDITIONALLY:
return (bool)$field_value;
+ case self::CONDITION_NEVER:
+ return false;
case self::CONDITION_REGEXP:
foreach ((array)$field_value as $value) {
// We add the 'S' flag because we use the regexp multiple times.
@@ -602,6 +624,7 @@
case self::CONDITION_EXISTS:
case self::CONDITION_NOT_EXISTS:
case self::CONDITION_UNCONDITIONALLY:
+ case self::CONDITION_NEVER:
case self::CONDITION_HAS_BIT:
case self::CONDITION_NOT_BIT:
case self::CONDITION_IS_TRUE:
@@ -710,6 +733,16 @@
public function getValueTypeForFieldAndCondition($field, $condition) {
+
+ if ($this->isHeraldCustomKey($field)) {
+ $value_type = $this->getCustomFieldValueTypeForFieldAndCondition(
+ $field,
+ $condition);
+ if ($value_type !== null) {
+ return $value_type;
+ }
+ }
+
switch ($condition) {
case self::CONDITION_CONTAINS:
case self::CONDITION_NOT_CONTAINS:
@@ -766,6 +799,7 @@
case self::CONDITION_EXISTS:
case self::CONDITION_NOT_EXISTS:
case self::CONDITION_UNCONDITIONALLY:
+ case self::CONDITION_NEVER:
case self::CONDITION_IS_TRUE:
case self::CONDITION_IS_FALSE:
return self::VALUE_NONE;
@@ -959,7 +993,12 @@
array $handles) {
$field_type = $condition->getFieldName();
- $field_name = idx($this->getFieldNameMap(), $field_type);
+
+ $default = $this->isHeraldCustomKey($field_type)
+ ? pht('(Unknown Custom Field "%s")', $field_type)
+ : pht('(Unknown Field "%s")', $field_type);
+
+ $field_name = idx($this->getFieldNameMap(), $field_type, $default);
$condition_type = $condition->getFieldCondition();
$condition_name = idx($this->getConditionNameMap(), $condition_type);
@@ -1085,4 +1124,205 @@
return $phids;
}
+/* -( Custom Field Integration )------------------------------------------- */
+
+
+ /**
+ * Return an object which custom fields can be generated from while editing
+ * rules. Adapters must return an object from this method to enable custom
+ * field rules.
+ *
+ * Normally, you'll return an empty version of the adapted object, assuming
+ * it implements @{interface:PhabricatorCustomFieldInterface}:
+ *
+ * return new ApplicationObject();
+ *
+ * This is normally the only adapter method you need to override to enable
+ * Herald rules to run against custom fields.
+ *
+ * @return null|PhabricatorCustomFieldInterface Template object.
+ * @task customfield
+ */
+ protected function getCustomFieldTemplateObject() {
+ return null;
+ }
+
+
+ /**
+ * Returns the prefix used to namespace Herald fields which are based on
+ * custom fields.
+ *
+ * @return string Key prefix.
+ * @task customfield
+ */
+ private function getCustomKeyPrefix() {
+ return 'herald.custom/';
+ }
+
+
+ /**
+ * Determine if a field key is based on a custom field or a regular internal
+ * field.
+ *
+ * @param string Field key.
+ * @return bool True if the field key is based on a custom field.
+ * @task customfield
+ */
+ private function isHeraldCustomKey($key) {
+ $prefix = $this->getCustomKeyPrefix();
+ return (strncmp($key, $prefix, strlen($prefix)) == 0);
+ }
+
+
+ /**
+ * Convert a custom field key into a Herald field key.
+ *
+ * @param string Custom field key.
+ * @return string Herald field key.
+ * @task customfield
+ */
+ private function getHeraldKeyFromCustomKey($key) {
+ return $this->getCustomKeyPrefix().$key;
+ }
+
+
+ /**
+ * Get custom fields for this adapter, if appliable. This will either return
+ * a field list or `null` if the adapted object does not implement custom
+ * fields or the adapter does not support them.
+ *
+ * @return PhabricatorCustomFieldList|null List of fields, or `null`.
+ * @task customfield
+ */
+ private function getCustomFields() {
+ if ($this->customFields === false) {
+ $this->customFields = null;
+
+
+ $template_object = $this->getCustomFieldTemplateObject();
+ if ($template_object) {
+ $object = $this->getObject();
+ if (!$object) {
+ $object = $template_object;
+ }
+
+ $fields = PhabricatorCustomField::getObjectFields(
+ $object,
+ PhabricatorCustomField::ROLE_HERALD);
+ $fields->setViewer(PhabricatorUser::getOmnipotentUser());
+ $fields->readFieldsFromStorage($object);
+
+ $this->customFields = $fields;
+ }
+ }
+
+ return $this->customFields;
+ }
+
+
+ /**
+ * Get a custom field by Herald field key, or `null` if it does not exist
+ * or custom fields are not supported.
+ *
+ * @param string Herald field key.
+ * @return PhabricatorCustomField|null Matching field, if it exists.
+ * @task customfield
+ */
+ private function getCustomField($herald_field_key) {
+ $fields = $this->getCustomFields();
+ if (!$fields) {
+ return null;
+ }
+
+ foreach ($fields->getFields() as $custom_field) {
+ $key = $custom_field->getFieldKey();
+ if ($this->getHeraldKeyFromCustomKey($key) == $herald_field_key) {
+ return $custom_field;
+ }
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Get the field map for custom fields.
+ *
+ * @return map<string, string> Map of Herald field keys to field names.
+ * @task customfield
+ */
+ private function getCustomFieldNameMap() {
+ $fields = $this->getCustomFields();
+ if (!$fields) {
+ return array();
+ }
+
+ $map = array();
+ foreach ($fields->getFields() as $field) {
+ $key = $field->getFieldKey();
+ $name = $field->getHeraldFieldName();
+ $map[$this->getHeraldKeyFromCustomKey($key)] = $name;
+ }
+
+ return $map;
+ }
+
+
+ /**
+ * Get the value for a custom field.
+ *
+ * @param string Herald field key.
+ * @return wild Custom field value.
+ * @task customfield
+ */
+ private function getCustomFieldValue($field_key) {
+ $field = $this->getCustomField($field_key);
+ if (!$field) {
+ return null;
+ }
+
+ return $field->getHeraldFieldValue();
+ }
+
+
+ /**
+ * Get the Herald conditions for a custom field.
+ *
+ * @param string Herald field key.
+ * @return list<const> List of Herald conditions.
+ * @task customfield
+ */
+ private function getCustomFieldConditions($field_key) {
+ $field = $this->getCustomField($field_key);
+ if (!$field) {
+ return array(
+ self::CONDITION_NEVER,
+ );
+ }
+
+ return $field->getHeraldFieldConditions();
+ }
+
+
+ /**
+ * Get the Herald value type for a custom field and condition.
+ *
+ * @param string Herald field key.
+ * @param const Herald condition constant.
+ * @return const|null Herald value type constant, or null to use the default.
+ * @task customfield
+ */
+ private function getCustomFieldValueTypeForFieldAndCondition(
+ $field_key,
+ $condition) {
+
+ $field = $this->getCustomField($field_key);
+ if (!$field) {
+ return self::VALUE_NONE;
+ }
+
+ return $field->getHeraldFieldValueType($condition);
+ }
+
+
}
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
@@ -205,4 +205,9 @@
}
return $result;
}
+
+ protected function getCustomFieldTemplateObject() {
+ return new ManiphestTask();
+ }
+
}
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
@@ -424,6 +424,15 @@
$fields = $adapter->getFields();
$field_map = array_select_keys($all_fields, $fields);
+ // Populate any fields which exist in the rule but which we don't know the
+ // names of, so that saving a rule without touching anything doesn't change
+ // it.
+ foreach ($rule->getConditions() as $condition) {
+ if (empty($field_map[$condition->getFieldName()])) {
+ $field_map[$condition->getFieldName()] = pht('<Unknown Field>');
+ }
+ }
+
$actions = $adapter->getActions($rule->getRuleType());
$action_map = array_select_keys($all_actions, $actions);
diff --git a/src/applications/herald/storage/HeraldRule.php b/src/applications/herald/storage/HeraldRule.php
--- a/src/applications/herald/storage/HeraldRule.php
+++ b/src/applications/herald/storage/HeraldRule.php
@@ -17,7 +17,7 @@
protected $isDisabled = 0;
protected $triggerObjectPHID;
- protected $configVersion = 35;
+ protected $configVersion = 36;
// phids for which this rule has been applied
private $ruleApplied = self::ATTACHABLE;
diff --git a/src/infrastructure/customfield/field/PhabricatorCustomField.php b/src/infrastructure/customfield/field/PhabricatorCustomField.php
--- a/src/infrastructure/customfield/field/PhabricatorCustomField.php
+++ b/src/infrastructure/customfield/field/PhabricatorCustomField.php
@@ -13,6 +13,7 @@
* @task appxaction Integration with ApplicationTransactions
* @task xactionmail Integration with Transaction Mail
* @task globalsearch Integration with Global Search
+ * @task herald Integration with Herald
*/
abstract class PhabricatorCustomField {
@@ -30,6 +31,7 @@
const ROLE_LIST = 'list';
const ROLE_GLOBALSEARCH = 'GlobalSearch';
const ROLE_CONDUIT = 'conduit';
+ const ROLE_HERALD = 'herald';
/* -( Building Applications with Custom Fields )--------------------------- */
@@ -268,6 +270,8 @@
return $this->shouldAppearInConduitDictionary();
case self::ROLE_TRANSACTIONMAIL:
return $this->shouldAppearInTransactionMail();
+ case self::ROLE_HERALD:
+ return $this->shouldAppearInHerald();
case self::ROLE_DEFAULT:
return true;
default:
@@ -1257,4 +1261,80 @@
throw new PhabricatorCustomFieldImplementationIncompleteException($this);
}
+
+/* -( Herald )------------------------------------------------------------- */
+
+
+ /**
+ * Return `true` to make this field available in Herald.
+ *
+ * @return bool True to expose the field in Herald.
+ * @task herald
+ */
+ public function shouldAppearInHerald() {
+ if ($this->proxy) {
+ return $this->proxy->shouldAppearInHerald();
+ }
+ return false;
+ }
+
+
+ /**
+ * Get the name of the field in Herald. By default, this uses the
+ * normal field name.
+ *
+ * @return string Herald field name.
+ * @task herald
+ */
+ public function getHeraldFieldName() {
+ if ($this->proxy) {
+ return $this->proxy->getHeraldFieldName();
+ }
+ return $this->getFieldName();
+ }
+
+
+ /**
+ * Get the field value for evaluation by Herald.
+ *
+ * @return wild Field value.
+ * @task herald
+ */
+ public function getHeraldFieldValue() {
+ if ($this->proxy) {
+ return $this->proxy->getHeraldFieldValue();
+ }
+ throw new PhabricatorCustomFieldImplementationIncompleteException($this);
+ }
+
+
+ /**
+ * Get the available conditions for this field in Herald.
+ *
+ * @return list<const> List of Herald condition constants.
+ * @task herald
+ */
+ public function getHeraldFieldConditions() {
+ if ($this->proxy) {
+ return $this->proxy->getHeraldFieldConditions();
+ }
+ throw new PhabricatorCustomFieldImplementationIncompleteException($this);
+ }
+
+
+ /**
+ * Get the Herald value type for the given condition.
+ *
+ * @param const Herald condition constant.
+ * @return const|null Herald value type, or null to use the default.
+ * @task herald
+ */
+ public function getHeraldFieldValueType($condition) {
+ if ($this->proxy) {
+ return $this->proxy->getHeraldFieldValueType($condition);
+ }
+ return null;
+ }
+
+
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php
@@ -382,5 +382,8 @@
}
}
+ public function getHeraldFieldValue() {
+ return $this->getFieldValue();
+ }
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php
@@ -111,5 +111,15 @@
}
}
+ public function shouldAppearInHerald() {
+ return true;
+ }
+
+ public function getHeraldFieldConditions() {
+ return array(
+ HeraldAdapter::CONDITION_IS_TRUE,
+ HeraldAdapter::CONDITION_IS_FALSE,
+ );
+ }
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php
@@ -158,4 +158,18 @@
}
}
+ public function shouldAppearInHerald() {
+ return true;
+ }
+
+ public function getHeraldFieldConditions() {
+ return array(
+ HeraldAdapter::CONDITION_INCLUDE_ALL,
+ HeraldAdapter::CONDITION_INCLUDE_ANY,
+ HeraldAdapter::CONDITION_INCLUDE_NONE,
+ HeraldAdapter::CONDITION_EXISTS,
+ HeraldAdapter::CONDITION_NOT_EXISTS,
+ );
+ }
+
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php
@@ -49,5 +49,19 @@
$this->getFieldName());
}
+ public function shouldAppearInHerald() {
+ return true;
+ }
+
+ public function getHeraldFieldConditions() {
+ return array(
+ HeraldAdapter::CONDITION_CONTAINS,
+ HeraldAdapter::CONDITION_NOT_CONTAINS,
+ HeraldAdapter::CONDITION_IS,
+ HeraldAdapter::CONDITION_IS_NOT,
+ HeraldAdapter::CONDITION_REGEXP,
+ );
+ }
+
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldText.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldText.php
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldText.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldText.php
@@ -50,4 +50,18 @@
->setValue($value));
}
+ public function shouldAppearInHerald() {
+ return true;
+ }
+
+ public function getHeraldFieldConditions() {
+ return array(
+ HeraldAdapter::CONDITION_CONTAINS,
+ HeraldAdapter::CONDITION_NOT_CONTAINS,
+ HeraldAdapter::CONDITION_IS,
+ HeraldAdapter::CONDITION_IS_NOT,
+ HeraldAdapter::CONDITION_REGEXP,
+ );
+ }
+
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php
--- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php
@@ -44,4 +44,9 @@
$form->appendChild($control);
}
+
+ public function getHeraldFieldValueType($condition) {
+ return HeraldAdapter::VALUE_USER;
+ }
+
}

File Metadata

Mime Type
text/plain
Expires
Mon, Aug 4, 7:11 PM (3 w, 3 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
8941831
Default Alt Text
D8695.diff (18 KB)

Event Timeline