1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-28 16:30:59 +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:
epriestley 2013-12-27 13:17:10 -08:00
parent f38a565aa5
commit f5fb3f05dc
9 changed files with 130 additions and 16 deletions

View 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);

View file

@ -3,12 +3,14 @@
final class HeraldRuleTypeConfig {
const RULE_TYPE_GLOBAL = 'global';
const RULE_TYPE_OBJECT = 'object';
const RULE_TYPE_PERSONAL = 'personal';
public static function getRuleTypeMap() {
$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;
}

View file

@ -28,7 +28,7 @@ final class HeraldDisableController extends HeraldController {
return new Aphront404Response();
}
if ($rule->getRuleType() == HeraldRuleTypeConfig::RULE_TYPE_GLOBAL) {
if ($rule->isGlobalRule()) {
$this->requireApplicationCapability(
HeraldCapabilityManageGlobalRules::CAPABILITY);
}

View file

@ -147,13 +147,18 @@ final class HeraldNewController extends HeraldController {
private function renderRuleTypeControl(array $rule_type_map, $e_rule) {
$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(
HeraldRuleTypeConfig::RULE_TYPE_PERSONAL,
HeraldRuleTypeConfig::RULE_TYPE_OBJECT,
HeraldRuleTypeConfig::RULE_TYPE_GLOBAL,
)) + $rule_type_map;
// TODO: Enable this.
unset($rule_type_map[HeraldRuleTypeConfig::RULE_TYPE_OBJECT]);
list($can_global, $global_link) = $this->explainApplicationCapability(
HeraldCapabilityManageGlobalRules::CAPABILITY,
pht('You have permission to create and manage global rules.'),

View file

@ -49,7 +49,7 @@ final class HeraldRuleController extends HeraldController {
$cancel_uri = $this->getApplicationURI();
}
if ($rule->getRuleType() == HeraldRuleTypeConfig::RULE_TYPE_GLOBAL) {
if ($rule->isGlobalRule()) {
$this->requireApplicationCapability(
HeraldCapabilityManageGlobalRules::CAPABILITY);
}
@ -561,7 +561,17 @@ final class HeraldRuleController extends HeraldController {
->withContentTypes(array($rule->getContentType()))
->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.
$all_rules += id(new HeraldRuleQuery())
->setViewer($viewer)

View file

@ -256,6 +256,11 @@ final class HeraldEngine {
"Rule failed automatically because it is a personal rule and its ".
"owner can not see the object.");
$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 {
foreach ($conditions as $condition) {
$match = $this->doesConditionMatch($rule, $condition, $object);
@ -381,8 +386,8 @@ final class HeraldEngine {
HeraldRule $rule,
HeraldAdapter $adapter) {
// Authorship is irrelevant for global rules.
if ($rule->isGlobalRule()) {
// Authorship is irrelevant for global rules and object rules.
if ($rule->isGlobalRule() || $rule->isObjectRule()) {
return true;
}
@ -405,4 +410,25 @@ final class HeraldEngine {
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;
}
}

View file

@ -9,6 +9,7 @@ final class HeraldRuleQuery
private $ruleTypes;
private $contentTypes;
private $disabled;
private $triggerObjectPHIDs;
private $needConditionsAndActions;
private $needAppliedToPHIDs;
@ -49,6 +50,11 @@ final class HeraldRuleQuery
return $this;
}
public function withTriggerObjectPHIDs(array $phids) {
$this->triggerObjectPHIDs = $phids;
return $this;
}
public function needConditionsAndActions($need) {
$this->needConditionsAndActions = $need;
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;
}
@ -185,6 +220,13 @@ final class HeraldRuleQuery
(int)$this->disabled);
}
if ($this->triggerObjectPHIDs) {
$where[] = qsprintf(
$conn_r,
'rule.triggerObjectPHID IN (%Ls)',
$this->triggerObjectPHIDs);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
@ -192,9 +234,9 @@ final class HeraldRuleQuery
private function validateRuleAuthors(array $rules) {
// "Global" rules always have valid authors.
// "Global" and "Object" rules always have valid authors.
foreach ($rules as $key => $rule) {
if ($rule->isGlobalRule()) {
if ($rule->isGlobalRule() || $rule->isObjectRule()) {
$rule->attachValidAuthor(true);
unset($rules[$key]);
continue;

View file

@ -15,8 +15,9 @@ final class HeraldRule extends HeraldDAO
protected $repetitionPolicy;
protected $ruleType;
protected $isDisabled = 0;
protected $triggerObjectPHID;
protected $configVersion = 21;
protected $configVersion = 22;
// phids for which this rule has been applied
private $ruleApplied = self::ATTACHABLE;
@ -24,6 +25,7 @@ final class HeraldRule extends HeraldDAO
private $author = self::ATTACHABLE;
private $conditions;
private $actions;
private $triggerObject = self::ATTACHABLE;
public function getConfiguration() {
return array(
@ -146,9 +148,7 @@ final class HeraldRule extends HeraldDAO
}
public function delete() {
// TODO:
// $this->openTransaction();
$this->openTransaction();
queryfx(
$this->establishConnection('w'),
'DELETE FROM %T WHERE ruleID = %d',
@ -159,8 +159,10 @@ final class HeraldRule extends HeraldDAO
'DELETE FROM %T WHERE ruleID = %d',
id(new HeraldAction())->getTableName(),
$this->getID());
parent::delete();
// $this->saveTransaction();
$result = parent::delete();
$this->saveTransaction();
return $result;
}
public function hasValidAuthor() {
@ -189,6 +191,19 @@ final class HeraldRule extends HeraldDAO
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 )----------------------------------------- */
@ -211,6 +226,8 @@ final class HeraldRule extends HeraldDAO
$global = HeraldCapabilityManageGlobalRules::CAPABILITY;
return $herald->getPolicy($global);
}
} else if ($this->isObjectRule()) {
return $this->getTriggerObject()->getPolicy($capability);
} else {
return PhabricatorPolicies::POLICY_NOONE;
}
@ -227,6 +244,8 @@ final class HeraldRule extends HeraldDAO
public function describeAutomaticCapability($capability) {
if ($this->isPersonalRule()) {
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;

View file

@ -1852,6 +1852,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'type' => 'sql',
'name' => $this->getPatchPath('20131224.harbormanual.sql'),
),
'20131227.heraldobject.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20131227.heraldobject.sql'),
),
);
}
}