@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3/**
4 * @extends PhabricatorCursorPagedPolicyAwareQuery<HeraldRule>
5 */
6final class HeraldRuleQuery extends PhabricatorCursorPagedPolicyAwareQuery {
7
8 private $ids;
9 private $phids;
10 private $authorPHIDs;
11 private $ruleTypes;
12 private $contentTypes;
13 private $disabled;
14 private $active;
15 private $datasourceQuery;
16 private $triggerObjectPHIDs;
17 private $affectedObjectPHIDs;
18
19 private $needConditionsAndActions;
20 private $needAppliedToPHIDs;
21 private $needValidateAuthors;
22
23 public function withIDs(array $ids) {
24 $this->ids = $ids;
25 return $this;
26 }
27
28 public function withPHIDs(array $phids) {
29 $this->phids = $phids;
30 return $this;
31 }
32
33 public function withAuthorPHIDs(array $author_phids) {
34 $this->authorPHIDs = $author_phids;
35 return $this;
36 }
37
38 public function withRuleTypes(array $types) {
39 $this->ruleTypes = $types;
40 return $this;
41 }
42
43 public function withContentTypes(array $types) {
44 $this->contentTypes = $types;
45 return $this;
46 }
47
48 public function withDisabled($disabled) {
49 $this->disabled = $disabled;
50 return $this;
51 }
52
53 public function withActive($active) {
54 $this->active = $active;
55 return $this;
56 }
57
58 public function withDatasourceQuery($query) {
59 $this->datasourceQuery = $query;
60 return $this;
61 }
62
63 public function withTriggerObjectPHIDs(array $phids) {
64 $this->triggerObjectPHIDs = $phids;
65 return $this;
66 }
67
68 public function withAffectedObjectPHIDs(array $phids) {
69 $this->affectedObjectPHIDs = $phids;
70 return $this;
71 }
72
73 public function needConditionsAndActions($need) {
74 $this->needConditionsAndActions = $need;
75 return $this;
76 }
77
78 public function needAppliedToPHIDs(array $phids) {
79 $this->needAppliedToPHIDs = $phids;
80 return $this;
81 }
82
83 public function needValidateAuthors($need) {
84 $this->needValidateAuthors = $need;
85 return $this;
86 }
87
88 public function newResultObject() {
89 return new HeraldRule();
90 }
91
92 protected function willFilterPage(array $rules) {
93 $rule_ids = mpull($rules, 'getID');
94
95 // Filter out any rules that have invalid adapters, or have adapters the
96 // viewer isn't permitted to see or use (for example, Differential rules
97 // if the user can't use Differential or Differential is disabled).
98 $types = HeraldAdapter::getEnabledAdapterMap($this->getViewer());
99 foreach ($rules as $key => $rule) {
100 if (empty($types[$rule->getContentType()])) {
101 $this->didRejectResult($rule);
102 unset($rules[$key]);
103 }
104 }
105
106 if ($this->needValidateAuthors || ($this->active !== null)) {
107 $this->validateRuleAuthors($rules);
108 }
109
110 if ($this->active !== null) {
111 $need_active = (bool)$this->active;
112 foreach ($rules as $key => $rule) {
113 if ($rule->getIsDisabled()) {
114 $is_active = false;
115 } else if (!$rule->hasValidAuthor()) {
116 $is_active = false;
117 } else {
118 $is_active = true;
119 }
120
121 if ($is_active != $need_active) {
122 unset($rules[$key]);
123 }
124 }
125 }
126
127 if (!$rules) {
128 return array();
129 }
130
131 if ($this->needConditionsAndActions) {
132 $conditions = id(new HeraldCondition())->loadAllWhere(
133 'ruleID IN (%Ld)',
134 $rule_ids);
135 $conditions = mgroup($conditions, 'getRuleID');
136
137 $actions = id(new HeraldActionRecord())->loadAllWhere(
138 'ruleID IN (%Ld)',
139 $rule_ids);
140 $actions = mgroup($actions, 'getRuleID');
141
142 foreach ($rules as $rule) {
143 $rule->attachActions(idx($actions, $rule->getID(), array()));
144 $rule->attachConditions(idx($conditions, $rule->getID(), array()));
145 }
146 }
147
148 if ($this->needAppliedToPHIDs) {
149 $conn_r = id(new HeraldRule())->establishConnection('r');
150 $applied = queryfx_all(
151 $conn_r,
152 'SELECT * FROM %T WHERE ruleID IN (%Ld) AND phid IN (%Ls)',
153 HeraldRule::TABLE_RULE_APPLIED,
154 $rule_ids,
155 $this->needAppliedToPHIDs);
156
157 $map = array();
158 foreach ($applied as $row) {
159 $map[$row['ruleID']][$row['phid']] = true;
160 }
161
162 foreach ($rules as $rule) {
163 foreach ($this->needAppliedToPHIDs as $phid) {
164 $rule->setRuleApplied(
165 $phid,
166 isset($map[$rule->getID()][$phid]));
167 }
168 }
169 }
170
171 $object_phids = array();
172 foreach ($rules as $rule) {
173 if ($rule->isObjectRule()) {
174 $object_phids[] = $rule->getTriggerObjectPHID();
175 }
176 }
177
178 if ($object_phids) {
179 $objects = id(new PhabricatorObjectQuery())
180 ->setParentQuery($this)
181 ->setViewer($this->getViewer())
182 ->withPHIDs($object_phids)
183 ->execute();
184 $objects = mpull($objects, null, 'getPHID');
185 } else {
186 $objects = array();
187 }
188
189 foreach ($rules as $key => $rule) {
190 if ($rule->isObjectRule()) {
191 $object = idx($objects, $rule->getTriggerObjectPHID());
192 if (!$object) {
193 unset($rules[$key]);
194 continue;
195 }
196 $rule->attachTriggerObject($object);
197 }
198 }
199
200 return $rules;
201 }
202
203 protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
204 $where = parent::buildWhereClauseParts($conn);
205
206 if ($this->ids !== null) {
207 $where[] = qsprintf(
208 $conn,
209 'rule.id IN (%Ld)',
210 $this->ids);
211 }
212
213 if ($this->phids !== null) {
214 $where[] = qsprintf(
215 $conn,
216 'rule.phid IN (%Ls)',
217 $this->phids);
218 }
219
220 if ($this->authorPHIDs !== null) {
221 $where[] = qsprintf(
222 $conn,
223 'rule.authorPHID IN (%Ls)',
224 $this->authorPHIDs);
225 }
226
227 if ($this->ruleTypes !== null) {
228 $where[] = qsprintf(
229 $conn,
230 'rule.ruleType IN (%Ls)',
231 $this->ruleTypes);
232 }
233
234 if ($this->contentTypes !== null) {
235 $where[] = qsprintf(
236 $conn,
237 'rule.contentType IN (%Ls)',
238 $this->contentTypes);
239 }
240
241 if ($this->disabled !== null) {
242 $where[] = qsprintf(
243 $conn,
244 'rule.isDisabled = %d',
245 (int)$this->disabled);
246 }
247
248 if ($this->active !== null) {
249 $where[] = qsprintf(
250 $conn,
251 'rule.isDisabled = %d',
252 (int)(!$this->active));
253 }
254
255 if ($this->datasourceQuery !== null) {
256 $where[] = qsprintf(
257 $conn,
258 'rule.name LIKE %>',
259 $this->datasourceQuery);
260 }
261
262 if ($this->triggerObjectPHIDs !== null) {
263 $where[] = qsprintf(
264 $conn,
265 'rule.triggerObjectPHID IN (%Ls)',
266 $this->triggerObjectPHIDs);
267 }
268
269 if ($this->affectedObjectPHIDs !== null) {
270 $where[] = qsprintf(
271 $conn,
272 'edge_affects.dst IN (%Ls)',
273 $this->affectedObjectPHIDs);
274 }
275
276 return $where;
277 }
278
279 protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
280 $joins = parent::buildJoinClauseParts($conn);
281
282 if ($this->affectedObjectPHIDs !== null) {
283 $joins[] = qsprintf(
284 $conn,
285 'JOIN %T edge_affects ON rule.phid = edge_affects.src
286 AND edge_affects.type = %d',
287 PhabricatorEdgeConfig::TABLE_NAME_EDGE,
288 HeraldRuleActionAffectsObjectEdgeType::EDGECONST);
289 }
290
291 return $joins;
292 }
293
294 private function validateRuleAuthors(array $rules) {
295 // "Global" and "Object" rules always have valid authors.
296 foreach ($rules as $key => $rule) {
297 if ($rule->isGlobalRule() || $rule->isObjectRule()) {
298 $rule->attachValidAuthor(true);
299 unset($rules[$key]);
300 continue;
301 }
302 }
303
304 if (!$rules) {
305 return;
306 }
307
308 // For personal rules, the author needs to exist and not be disabled.
309 $user_phids = mpull($rules, 'getAuthorPHID');
310 $users = id(new PhabricatorPeopleQuery())
311 ->setViewer($this->getViewer())
312 ->withPHIDs($user_phids)
313 ->execute();
314 $users = mpull($users, null, 'getPHID');
315
316 foreach ($rules as $key => $rule) {
317 $author_phid = $rule->getAuthorPHID();
318 if (empty($users[$author_phid])) {
319 $rule->attachValidAuthor(false);
320 continue;
321 }
322 if (!$users[$author_phid]->isUserActivated()) {
323 $rule->attachValidAuthor(false);
324 continue;
325 }
326
327 $rule->attachValidAuthor(true);
328 $rule->attachAuthor($users[$author_phid]);
329 }
330 }
331
332 public function getQueryApplicationClass() {
333 return PhabricatorHeraldApplication::class;
334 }
335
336 protected function getPrimaryTableAlias() {
337 return 'rule';
338 }
339
340}