mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-29 08:50:58 +01:00
Lay most groundwork for Herald object rules
Summary: Ref T4264. This gets most of the plumbing in for "object" rules, which will bind to a specific object, like a repository or project. It does not yet let you actually create these rules. Test Plan: Ran `storage upgrade`, created/edited rules, browsed Herald. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T4264 Differential Revision: https://secure.phabricator.com/D7847
This commit is contained in:
parent
f38a565aa5
commit
f5fb3f05dc
9 changed files with 130 additions and 16 deletions
6
resources/sql/patches/20131227.heraldobject.sql
Normal file
6
resources/sql/patches/20131227.heraldobject.sql
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_herald.herald_rule
|
||||||
|
ADD triggerObjectPHID VARCHAR(64) COLLATE utf8_bin;
|
||||||
|
|
||||||
|
ALTER TABLE {$NAMESPACE}_herald.herald_rule
|
||||||
|
ADD KEY `key_trigger` (triggerObjectPHID);
|
||||||
|
|
|
@ -3,12 +3,14 @@
|
||||||
final class HeraldRuleTypeConfig {
|
final class HeraldRuleTypeConfig {
|
||||||
|
|
||||||
const RULE_TYPE_GLOBAL = 'global';
|
const RULE_TYPE_GLOBAL = 'global';
|
||||||
|
const RULE_TYPE_OBJECT = 'object';
|
||||||
const RULE_TYPE_PERSONAL = 'personal';
|
const RULE_TYPE_PERSONAL = 'personal';
|
||||||
|
|
||||||
public static function getRuleTypeMap() {
|
public static function getRuleTypeMap() {
|
||||||
$map = array(
|
$map = array(
|
||||||
self::RULE_TYPE_GLOBAL => pht('Global'),
|
self::RULE_TYPE_PERSONAL => pht('Personal'),
|
||||||
self::RULE_TYPE_PERSONAL => pht('Personal'),
|
self::RULE_TYPE_OBJECT => pht('Object'),
|
||||||
|
self::RULE_TYPE_GLOBAL => pht('Global'),
|
||||||
);
|
);
|
||||||
return $map;
|
return $map;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ final class HeraldDisableController extends HeraldController {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($rule->getRuleType() == HeraldRuleTypeConfig::RULE_TYPE_GLOBAL) {
|
if ($rule->isGlobalRule()) {
|
||||||
$this->requireApplicationCapability(
|
$this->requireApplicationCapability(
|
||||||
HeraldCapabilityManageGlobalRules::CAPABILITY);
|
HeraldCapabilityManageGlobalRules::CAPABILITY);
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,13 +147,18 @@ final class HeraldNewController extends HeraldController {
|
||||||
private function renderRuleTypeControl(array $rule_type_map, $e_rule) {
|
private function renderRuleTypeControl(array $rule_type_map, $e_rule) {
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
|
|
||||||
// Reorder array to put "personal" first.
|
// Reorder array to put less powerful rules first.
|
||||||
$rule_type_map = array_select_keys(
|
$rule_type_map = array_select_keys(
|
||||||
$rule_type_map,
|
$rule_type_map,
|
||||||
array(
|
array(
|
||||||
HeraldRuleTypeConfig::RULE_TYPE_PERSONAL,
|
HeraldRuleTypeConfig::RULE_TYPE_PERSONAL,
|
||||||
|
HeraldRuleTypeConfig::RULE_TYPE_OBJECT,
|
||||||
|
HeraldRuleTypeConfig::RULE_TYPE_GLOBAL,
|
||||||
)) + $rule_type_map;
|
)) + $rule_type_map;
|
||||||
|
|
||||||
|
// TODO: Enable this.
|
||||||
|
unset($rule_type_map[HeraldRuleTypeConfig::RULE_TYPE_OBJECT]);
|
||||||
|
|
||||||
list($can_global, $global_link) = $this->explainApplicationCapability(
|
list($can_global, $global_link) = $this->explainApplicationCapability(
|
||||||
HeraldCapabilityManageGlobalRules::CAPABILITY,
|
HeraldCapabilityManageGlobalRules::CAPABILITY,
|
||||||
pht('You have permission to create and manage global rules.'),
|
pht('You have permission to create and manage global rules.'),
|
||||||
|
|
|
@ -49,7 +49,7 @@ final class HeraldRuleController extends HeraldController {
|
||||||
$cancel_uri = $this->getApplicationURI();
|
$cancel_uri = $this->getApplicationURI();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($rule->getRuleType() == HeraldRuleTypeConfig::RULE_TYPE_GLOBAL) {
|
if ($rule->isGlobalRule()) {
|
||||||
$this->requireApplicationCapability(
|
$this->requireApplicationCapability(
|
||||||
HeraldCapabilityManageGlobalRules::CAPABILITY);
|
HeraldCapabilityManageGlobalRules::CAPABILITY);
|
||||||
}
|
}
|
||||||
|
@ -561,7 +561,17 @@ final class HeraldRuleController extends HeraldController {
|
||||||
->withContentTypes(array($rule->getContentType()))
|
->withContentTypes(array($rule->getContentType()))
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
if ($rule->getRuleType() == HeraldRuleTypeConfig::RULE_TYPE_PERSONAL) {
|
if ($rule->isObjectRule()) {
|
||||||
|
// Object rules may depend on other rules for the same object.
|
||||||
|
$all_rules += id(new HeraldRuleQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withRuleTypes(array(HeraldRuleTypeConfig::RULE_TYPE_OBJECT))
|
||||||
|
->withContentTypes(array($rule->getContentType()))
|
||||||
|
->withTriggerObjectPHIDs(array($rule->getTriggerObjectPHID()))
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($rule->isPersonalRule()) {
|
||||||
// Personal rules may depend upon your other personal rules.
|
// Personal rules may depend upon your other personal rules.
|
||||||
$all_rules += id(new HeraldRuleQuery())
|
$all_rules += id(new HeraldRuleQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
|
|
|
@ -256,6 +256,11 @@ final class HeraldEngine {
|
||||||
"Rule failed automatically because it is a personal rule and its ".
|
"Rule failed automatically because it is a personal rule and its ".
|
||||||
"owner can not see the object.");
|
"owner can not see the object.");
|
||||||
$result = false;
|
$result = false;
|
||||||
|
} else if (!$this->canRuleApplyToObject($rule, $object)) {
|
||||||
|
$reason = pht(
|
||||||
|
"Rule failed automatically because it is an object rule which is ".
|
||||||
|
"not relevant for this object.");
|
||||||
|
$result = false;
|
||||||
} else {
|
} else {
|
||||||
foreach ($conditions as $condition) {
|
foreach ($conditions as $condition) {
|
||||||
$match = $this->doesConditionMatch($rule, $condition, $object);
|
$match = $this->doesConditionMatch($rule, $condition, $object);
|
||||||
|
@ -381,8 +386,8 @@ final class HeraldEngine {
|
||||||
HeraldRule $rule,
|
HeraldRule $rule,
|
||||||
HeraldAdapter $adapter) {
|
HeraldAdapter $adapter) {
|
||||||
|
|
||||||
// Authorship is irrelevant for global rules.
|
// Authorship is irrelevant for global rules and object rules.
|
||||||
if ($rule->isGlobalRule()) {
|
if ($rule->isGlobalRule() || $rule->isObjectRule()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,4 +410,25 @@ final class HeraldEngine {
|
||||||
PhabricatorPolicyCapability::CAN_VIEW);
|
PhabricatorPolicyCapability::CAN_VIEW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function canRuleApplyToObject(
|
||||||
|
HeraldRule $rule,
|
||||||
|
HeraldAdapter $adapter) {
|
||||||
|
|
||||||
|
// Rules which are not object rules can apply to anything.
|
||||||
|
if (!$rule->isObjectRule()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$trigger_phid = $rule->getTriggerObjectPHID();
|
||||||
|
$object_phid = $adapter->getPHID();
|
||||||
|
|
||||||
|
if ($trigger_phid == $object_phid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: We should also handle projects.
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ final class HeraldRuleQuery
|
||||||
private $ruleTypes;
|
private $ruleTypes;
|
||||||
private $contentTypes;
|
private $contentTypes;
|
||||||
private $disabled;
|
private $disabled;
|
||||||
|
private $triggerObjectPHIDs;
|
||||||
|
|
||||||
private $needConditionsAndActions;
|
private $needConditionsAndActions;
|
||||||
private $needAppliedToPHIDs;
|
private $needAppliedToPHIDs;
|
||||||
|
@ -49,6 +50,11 @@ final class HeraldRuleQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withTriggerObjectPHIDs(array $phids) {
|
||||||
|
$this->triggerObjectPHIDs = $phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function needConditionsAndActions($need) {
|
public function needConditionsAndActions($need) {
|
||||||
$this->needConditionsAndActions = $need;
|
$this->needConditionsAndActions = $need;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -137,6 +143,35 @@ final class HeraldRuleQuery
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$object_phids = array();
|
||||||
|
foreach ($rules as $rule) {
|
||||||
|
if ($rule->isObjectRule()) {
|
||||||
|
$object_phids[] = $rule->getTriggerObjectPHID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($object_phids) {
|
||||||
|
$objects = id(new PhabricatorObjectQuery())
|
||||||
|
->setParentQuery($this)
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withPHIDs($object_phids)
|
||||||
|
->execute();
|
||||||
|
$objects = mpull($objects, null, 'getPHID');
|
||||||
|
} else {
|
||||||
|
$objects = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($rules as $key => $rule) {
|
||||||
|
if ($rule->isObjectRule()) {
|
||||||
|
$object = idx($objects, $rule->getTriggerObjectPHID());
|
||||||
|
if (!$object) {
|
||||||
|
unset($rules[$key]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$rule->attachTriggerObject($object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,6 +220,13 @@ final class HeraldRuleQuery
|
||||||
(int)$this->disabled);
|
(int)$this->disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->triggerObjectPHIDs) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'rule.triggerObjectPHID IN (%Ls)',
|
||||||
|
$this->triggerObjectPHIDs);
|
||||||
|
}
|
||||||
|
|
||||||
$where[] = $this->buildPagingClause($conn_r);
|
$where[] = $this->buildPagingClause($conn_r);
|
||||||
|
|
||||||
return $this->formatWhereClause($where);
|
return $this->formatWhereClause($where);
|
||||||
|
@ -192,9 +234,9 @@ final class HeraldRuleQuery
|
||||||
|
|
||||||
private function validateRuleAuthors(array $rules) {
|
private function validateRuleAuthors(array $rules) {
|
||||||
|
|
||||||
// "Global" rules always have valid authors.
|
// "Global" and "Object" rules always have valid authors.
|
||||||
foreach ($rules as $key => $rule) {
|
foreach ($rules as $key => $rule) {
|
||||||
if ($rule->isGlobalRule()) {
|
if ($rule->isGlobalRule() || $rule->isObjectRule()) {
|
||||||
$rule->attachValidAuthor(true);
|
$rule->attachValidAuthor(true);
|
||||||
unset($rules[$key]);
|
unset($rules[$key]);
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -15,8 +15,9 @@ final class HeraldRule extends HeraldDAO
|
||||||
protected $repetitionPolicy;
|
protected $repetitionPolicy;
|
||||||
protected $ruleType;
|
protected $ruleType;
|
||||||
protected $isDisabled = 0;
|
protected $isDisabled = 0;
|
||||||
|
protected $triggerObjectPHID;
|
||||||
|
|
||||||
protected $configVersion = 21;
|
protected $configVersion = 22;
|
||||||
|
|
||||||
// phids for which this rule has been applied
|
// phids for which this rule has been applied
|
||||||
private $ruleApplied = self::ATTACHABLE;
|
private $ruleApplied = self::ATTACHABLE;
|
||||||
|
@ -24,6 +25,7 @@ final class HeraldRule extends HeraldDAO
|
||||||
private $author = self::ATTACHABLE;
|
private $author = self::ATTACHABLE;
|
||||||
private $conditions;
|
private $conditions;
|
||||||
private $actions;
|
private $actions;
|
||||||
|
private $triggerObject = self::ATTACHABLE;
|
||||||
|
|
||||||
public function getConfiguration() {
|
public function getConfiguration() {
|
||||||
return array(
|
return array(
|
||||||
|
@ -146,9 +148,7 @@ final class HeraldRule extends HeraldDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete() {
|
public function delete() {
|
||||||
|
$this->openTransaction();
|
||||||
// TODO:
|
|
||||||
// $this->openTransaction();
|
|
||||||
queryfx(
|
queryfx(
|
||||||
$this->establishConnection('w'),
|
$this->establishConnection('w'),
|
||||||
'DELETE FROM %T WHERE ruleID = %d',
|
'DELETE FROM %T WHERE ruleID = %d',
|
||||||
|
@ -159,8 +159,10 @@ final class HeraldRule extends HeraldDAO
|
||||||
'DELETE FROM %T WHERE ruleID = %d',
|
'DELETE FROM %T WHERE ruleID = %d',
|
||||||
id(new HeraldAction())->getTableName(),
|
id(new HeraldAction())->getTableName(),
|
||||||
$this->getID());
|
$this->getID());
|
||||||
parent::delete();
|
$result = parent::delete();
|
||||||
// $this->saveTransaction();
|
$this->saveTransaction();
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasValidAuthor() {
|
public function hasValidAuthor() {
|
||||||
|
@ -189,6 +191,19 @@ final class HeraldRule extends HeraldDAO
|
||||||
return ($this->getRuleType() === HeraldRuleTypeConfig::RULE_TYPE_PERSONAL);
|
return ($this->getRuleType() === HeraldRuleTypeConfig::RULE_TYPE_PERSONAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isObjectRule() {
|
||||||
|
return ($this->getRuleType() == HeraldRuleTypeConfig::RULE_TYPE_OBJECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attachTriggerObject($trigger_object) {
|
||||||
|
$this->triggerObject = $trigger_object;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTriggerObject() {
|
||||||
|
return $this->assertAttached($this->triggerObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
@ -211,6 +226,8 @@ final class HeraldRule extends HeraldDAO
|
||||||
$global = HeraldCapabilityManageGlobalRules::CAPABILITY;
|
$global = HeraldCapabilityManageGlobalRules::CAPABILITY;
|
||||||
return $herald->getPolicy($global);
|
return $herald->getPolicy($global);
|
||||||
}
|
}
|
||||||
|
} else if ($this->isObjectRule()) {
|
||||||
|
return $this->getTriggerObject()->getPolicy($capability);
|
||||||
} else {
|
} else {
|
||||||
return PhabricatorPolicies::POLICY_NOONE;
|
return PhabricatorPolicies::POLICY_NOONE;
|
||||||
}
|
}
|
||||||
|
@ -227,6 +244,8 @@ final class HeraldRule extends HeraldDAO
|
||||||
public function describeAutomaticCapability($capability) {
|
public function describeAutomaticCapability($capability) {
|
||||||
if ($this->isPersonalRule()) {
|
if ($this->isPersonalRule()) {
|
||||||
return pht("A personal rule's owner can always view and edit it.");
|
return pht("A personal rule's owner can always view and edit it.");
|
||||||
|
} else if ($this->isObjectRule()) {
|
||||||
|
return pht("Object rules inherit the policies of their objects.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1852,6 +1852,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
||||||
'type' => 'sql',
|
'type' => 'sql',
|
||||||
'name' => $this->getPatchPath('20131224.harbormanual.sql'),
|
'name' => $this->getPatchPath('20131224.harbormanual.sql'),
|
||||||
),
|
),
|
||||||
|
'20131227.heraldobject.sql' => array(
|
||||||
|
'type' => 'sql',
|
||||||
|
'name' => $this->getPatchPath('20131227.heraldobject.sql'),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue