Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/herald/storage/HeraldRule.php
12256 views
1
<?php
2
3
final class HeraldRule extends HeraldDAO
4
implements
5
PhabricatorApplicationTransactionInterface,
6
PhabricatorFlaggableInterface,
7
PhabricatorPolicyInterface,
8
PhabricatorDestructibleInterface,
9
PhabricatorIndexableInterface,
10
PhabricatorSubscribableInterface {
11
12
const TABLE_RULE_APPLIED = 'herald_ruleapplied';
13
14
protected $name;
15
protected $authorPHID;
16
17
protected $contentType;
18
protected $mustMatchAll;
19
protected $repetitionPolicy;
20
protected $ruleType;
21
protected $isDisabled = 0;
22
protected $triggerObjectPHID;
23
24
protected $configVersion = 38;
25
26
// PHIDs for which this rule has been applied
27
private $ruleApplied = self::ATTACHABLE;
28
private $validAuthor = self::ATTACHABLE;
29
private $author = self::ATTACHABLE;
30
private $conditions;
31
private $actions;
32
private $triggerObject = self::ATTACHABLE;
33
34
const REPEAT_EVERY = 'every';
35
const REPEAT_FIRST = 'first';
36
const REPEAT_CHANGE = 'change';
37
38
protected function getConfiguration() {
39
return array(
40
self::CONFIG_AUX_PHID => true,
41
self::CONFIG_COLUMN_SCHEMA => array(
42
'name' => 'sort255',
43
'contentType' => 'text255',
44
'mustMatchAll' => 'bool',
45
'configVersion' => 'uint32',
46
'repetitionPolicy' => 'text32',
47
'ruleType' => 'text32',
48
'isDisabled' => 'uint32',
49
'triggerObjectPHID' => 'phid?',
50
),
51
self::CONFIG_KEY_SCHEMA => array(
52
'key_name' => array(
53
'columns' => array('name(128)'),
54
),
55
'key_author' => array(
56
'columns' => array('authorPHID'),
57
),
58
'key_ruletype' => array(
59
'columns' => array('ruleType'),
60
),
61
'key_trigger' => array(
62
'columns' => array('triggerObjectPHID'),
63
),
64
),
65
) + parent::getConfiguration();
66
}
67
68
public function generatePHID() {
69
return PhabricatorPHID::generateNewPHID(HeraldRulePHIDType::TYPECONST);
70
}
71
72
public function getRuleApplied($phid) {
73
return $this->assertAttachedKey($this->ruleApplied, $phid);
74
}
75
76
public function setRuleApplied($phid, $applied) {
77
if ($this->ruleApplied === self::ATTACHABLE) {
78
$this->ruleApplied = array();
79
}
80
$this->ruleApplied[$phid] = $applied;
81
return $this;
82
}
83
84
public function loadConditions() {
85
if (!$this->getID()) {
86
return array();
87
}
88
return id(new HeraldCondition())->loadAllWhere(
89
'ruleID = %d',
90
$this->getID());
91
}
92
93
public function attachConditions(array $conditions) {
94
assert_instances_of($conditions, 'HeraldCondition');
95
$this->conditions = $conditions;
96
return $this;
97
}
98
99
public function getConditions() {
100
// TODO: validate conditions have been attached.
101
return $this->conditions;
102
}
103
104
public function loadActions() {
105
if (!$this->getID()) {
106
return array();
107
}
108
return id(new HeraldActionRecord())->loadAllWhere(
109
'ruleID = %d',
110
$this->getID());
111
}
112
113
public function attachActions(array $actions) {
114
// TODO: validate actions have been attached.
115
assert_instances_of($actions, 'HeraldActionRecord');
116
$this->actions = $actions;
117
return $this;
118
}
119
120
public function getActions() {
121
return $this->actions;
122
}
123
124
public function saveConditions(array $conditions) {
125
assert_instances_of($conditions, 'HeraldCondition');
126
return $this->saveChildren(
127
id(new HeraldCondition())->getTableName(),
128
$conditions);
129
}
130
131
public function saveActions(array $actions) {
132
assert_instances_of($actions, 'HeraldActionRecord');
133
return $this->saveChildren(
134
id(new HeraldActionRecord())->getTableName(),
135
$actions);
136
}
137
138
protected function saveChildren($table_name, array $children) {
139
assert_instances_of($children, 'HeraldDAO');
140
141
if (!$this->getID()) {
142
throw new PhutilInvalidStateException('save');
143
}
144
145
foreach ($children as $child) {
146
$child->setRuleID($this->getID());
147
}
148
149
$this->openTransaction();
150
queryfx(
151
$this->establishConnection('w'),
152
'DELETE FROM %T WHERE ruleID = %d',
153
$table_name,
154
$this->getID());
155
foreach ($children as $child) {
156
$child->save();
157
}
158
$this->saveTransaction();
159
}
160
161
public function delete() {
162
$this->openTransaction();
163
queryfx(
164
$this->establishConnection('w'),
165
'DELETE FROM %T WHERE ruleID = %d',
166
id(new HeraldCondition())->getTableName(),
167
$this->getID());
168
queryfx(
169
$this->establishConnection('w'),
170
'DELETE FROM %T WHERE ruleID = %d',
171
id(new HeraldActionRecord())->getTableName(),
172
$this->getID());
173
$result = parent::delete();
174
$this->saveTransaction();
175
176
return $result;
177
}
178
179
public function hasValidAuthor() {
180
return $this->assertAttached($this->validAuthor);
181
}
182
183
public function attachValidAuthor($valid) {
184
$this->validAuthor = $valid;
185
return $this;
186
}
187
188
public function getAuthor() {
189
return $this->assertAttached($this->author);
190
}
191
192
public function attachAuthor(PhabricatorUser $user) {
193
$this->author = $user;
194
return $this;
195
}
196
197
public function isGlobalRule() {
198
return ($this->getRuleType() === HeraldRuleTypeConfig::RULE_TYPE_GLOBAL);
199
}
200
201
public function isPersonalRule() {
202
return ($this->getRuleType() === HeraldRuleTypeConfig::RULE_TYPE_PERSONAL);
203
}
204
205
public function isObjectRule() {
206
return ($this->getRuleType() == HeraldRuleTypeConfig::RULE_TYPE_OBJECT);
207
}
208
209
public function attachTriggerObject($trigger_object) {
210
$this->triggerObject = $trigger_object;
211
return $this;
212
}
213
214
public function getTriggerObject() {
215
return $this->assertAttached($this->triggerObject);
216
}
217
218
/**
219
* Get a sortable key for rule execution order.
220
*
221
* Rules execute in a well-defined order: personal rules first, then object
222
* rules, then global rules. Within each rule type, rules execute from lowest
223
* ID to highest ID.
224
*
225
* This ordering allows more powerful rules (like global rules) to override
226
* weaker rules (like personal rules) when multiple rules exist which try to
227
* affect the same field. Executing from low IDs to high IDs makes
228
* interactions easier to understand when adding new rules, because the newest
229
* rules always happen last.
230
*
231
* @return string A sortable key for this rule.
232
*/
233
public function getRuleExecutionOrderSortKey() {
234
235
$rule_type = $this->getRuleType();
236
237
switch ($rule_type) {
238
case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL:
239
$type_order = 1;
240
break;
241
case HeraldRuleTypeConfig::RULE_TYPE_OBJECT:
242
$type_order = 2;
243
break;
244
case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL:
245
$type_order = 3;
246
break;
247
default:
248
throw new Exception(pht('Unknown rule type "%s"!', $rule_type));
249
}
250
251
return sprintf('~%d%010d', $type_order, $this->getID());
252
}
253
254
public function getMonogram() {
255
return 'H'.$this->getID();
256
}
257
258
public function getURI() {
259
return '/'.$this->getMonogram();
260
}
261
262
public function getEditorSortVector() {
263
return id(new PhutilSortVector())
264
->addInt($this->getIsDisabled() ? 1 : 0)
265
->addString($this->getName());
266
}
267
268
public function getEditorDisplayName() {
269
$name = pht('%s %s', $this->getMonogram(), $this->getName());
270
271
if ($this->getIsDisabled()) {
272
$name = pht('%s (Disabled)', $name);
273
}
274
275
return $name;
276
}
277
278
279
/* -( Repetition Policies )------------------------------------------------ */
280
281
282
public function getRepetitionPolicyStringConstant() {
283
return $this->getRepetitionPolicy();
284
}
285
286
public function setRepetitionPolicyStringConstant($value) {
287
$map = self::getRepetitionPolicyMap();
288
289
if (!isset($map[$value])) {
290
throw new Exception(
291
pht(
292
'Rule repetition string constant "%s" is unknown.',
293
$value));
294
}
295
296
return $this->setRepetitionPolicy($value);
297
}
298
299
public function isRepeatEvery() {
300
return ($this->getRepetitionPolicyStringConstant() === self::REPEAT_EVERY);
301
}
302
303
public function isRepeatFirst() {
304
return ($this->getRepetitionPolicyStringConstant() === self::REPEAT_FIRST);
305
}
306
307
public function isRepeatOnChange() {
308
return ($this->getRepetitionPolicyStringConstant() === self::REPEAT_CHANGE);
309
}
310
311
public static function getRepetitionPolicySelectOptionMap() {
312
$map = self::getRepetitionPolicyMap();
313
return ipull($map, 'select');
314
}
315
316
private static function getRepetitionPolicyMap() {
317
return array(
318
self::REPEAT_EVERY => array(
319
'select' => pht('every time this rule matches:'),
320
),
321
self::REPEAT_FIRST => array(
322
'select' => pht('only the first time this rule matches:'),
323
),
324
self::REPEAT_CHANGE => array(
325
'select' => pht('if this rule did not match the last time:'),
326
),
327
);
328
}
329
330
331
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
332
333
334
public function getApplicationTransactionEditor() {
335
return new HeraldRuleEditor();
336
}
337
338
public function getApplicationTransactionTemplate() {
339
return new HeraldRuleTransaction();
340
}
341
342
343
/* -( PhabricatorPolicyInterface )----------------------------------------- */
344
345
346
public function getCapabilities() {
347
return array(
348
PhabricatorPolicyCapability::CAN_VIEW,
349
PhabricatorPolicyCapability::CAN_EDIT,
350
);
351
}
352
353
public function getPolicy($capability) {
354
if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
355
return PhabricatorPolicies::getMostOpenPolicy();
356
}
357
358
if ($this->isGlobalRule()) {
359
$app = 'PhabricatorHeraldApplication';
360
$herald = PhabricatorApplication::getByClass($app);
361
$global = HeraldManageGlobalRulesCapability::CAPABILITY;
362
return $herald->getPolicy($global);
363
} else if ($this->isObjectRule()) {
364
return $this->getTriggerObject()->getPolicy($capability);
365
} else {
366
return $this->getAuthorPHID();
367
}
368
}
369
370
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
371
return false;
372
}
373
374
public function describeAutomaticCapability($capability) {
375
if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
376
return null;
377
}
378
379
if ($this->isGlobalRule()) {
380
return pht(
381
'Global Herald rules can be edited by users with the "Can Manage '.
382
'Global Rules" Herald application permission.');
383
} else if ($this->isObjectRule()) {
384
return pht('Object rules inherit the edit policies of their objects.');
385
} else {
386
return pht('A personal rule can only be edited by its owner.');
387
}
388
}
389
390
391
/* -( PhabricatorSubscribableInterface )----------------------------------- */
392
393
394
public function isAutomaticallySubscribed($phid) {
395
return $this->isPersonalRule() && $phid == $this->getAuthorPHID();
396
}
397
398
399
/* -( PhabricatorDestructibleInterface )----------------------------------- */
400
401
402
public function destroyObjectPermanently(
403
PhabricatorDestructionEngine $engine) {
404
405
$this->openTransaction();
406
$this->delete();
407
$this->saveTransaction();
408
}
409
410
}
411
412