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:
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 {
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.'),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue