Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F18050269
D8695.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
18 KB
Referenced Files
None
Subscribers
None
D8695.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D8695: Support CustomField in Herald, mostly
Attached
Detach File
Event Timeline
Log In to Comment