mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-24 06:20:56 +01:00
Support CustomField in Herald, mostly
Summary: Ref T655. Ref T418. This mostly supports CustomFields in Herald, for conditions only. Test Plan: {F137845} Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T418, T655 Differential Revision: https://secure.phabricator.com/D8695
This commit is contained in:
parent
d9cdbdb9fa
commit
e3b5737d02
11 changed files with 402 additions and 8 deletions
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group herald
|
||||
* @task customfield Custom Field Integration
|
||||
*/
|
||||
abstract class HeraldAdapter {
|
||||
|
||||
|
@ -60,6 +60,7 @@ abstract class HeraldAdapter {
|
|||
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 @@ abstract class HeraldAdapter {
|
|||
|
||||
private $contentSource;
|
||||
private $isNewObject;
|
||||
private $customFields = false;
|
||||
|
||||
public function setContentSource(PhabricatorContentSource $content_source) {
|
||||
$this->contentSource = $content_source;
|
||||
|
@ -132,6 +134,10 @@ abstract class HeraldAdapter {
|
|||
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 @@ abstract class HeraldAdapter {
|
|||
|
||||
|
||||
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 @@ abstract class HeraldAdapter {
|
|||
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 @@ abstract class HeraldAdapter {
|
|||
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 @@ abstract class HeraldAdapter {
|
|||
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 @@ abstract class HeraldAdapter {
|
|||
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 @@ abstract class HeraldAdapter {
|
|||
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 @@ abstract class HeraldAdapter {
|
|||
|
||||
|
||||
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 @@ abstract class HeraldAdapter {
|
|||
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 @@ abstract class HeraldAdapter {
|
|||
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 @@ abstract class HeraldAdapter {
|
|||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -205,4 +205,9 @@ final class HeraldManiphestTaskAdapter extends HeraldAdapter {
|
|||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function getCustomFieldTemplateObject() {
|
||||
return new ManiphestTask();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -424,6 +424,15 @@ final class HeraldRuleController extends HeraldController {
|
|||
$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);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ final class HeraldRule extends HeraldDAO
|
|||
protected $isDisabled = 0;
|
||||
protected $triggerObjectPHID;
|
||||
|
||||
protected $configVersion = 35;
|
||||
protected $configVersion = 36;
|
||||
|
||||
// phids for which this rule has been applied
|
||||
private $ruleApplied = self::ATTACHABLE;
|
||||
|
|
|
@ -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 @@ abstract class PhabricatorCustomField {
|
|||
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 @@ abstract class PhabricatorCustomField {
|
|||
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 @@ abstract class PhabricatorCustomField {
|
|||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -382,5 +382,8 @@ abstract class PhabricatorStandardCustomField
|
|||
}
|
||||
}
|
||||
|
||||
public function getHeraldFieldValue() {
|
||||
return $this->getFieldValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -111,5 +111,15 @@ final class PhabricatorStandardCustomFieldBool
|
|||
}
|
||||
}
|
||||
|
||||
public function shouldAppearInHerald() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getHeraldFieldConditions() {
|
||||
return array(
|
||||
HeraldAdapter::CONDITION_IS_TRUE,
|
||||
HeraldAdapter::CONDITION_IS_FALSE,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -158,4 +158,18 @@ abstract class PhabricatorStandardCustomFieldPHIDs
|
|||
}
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -49,5 +49,19 @@ final class PhabricatorStandardCustomFieldRemarkup
|
|||
$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,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -50,4 +50,18 @@ final class PhabricatorStandardCustomFieldText
|
|||
->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,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -44,4 +44,9 @@ final class PhabricatorStandardCustomFieldUsers
|
|||
|
||||
$form->appendChild($control);
|
||||
}
|
||||
|
||||
public function getHeraldFieldValueType($condition) {
|
||||
return HeraldAdapter::VALUE_USER;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue