@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
3final class DiffusionCommitAuditorsTransaction
4 extends DiffusionCommitTransactionType {
5
6 const TRANSACTIONTYPE = 'diffusion.commit.auditors';
7
8 public function generateOldValue($object) {
9 $auditors = $object->getAudits();
10 return mpull($auditors, 'getAuditStatus', 'getAuditorPHID');
11 }
12
13 public function generateNewValue($object, $value) {
14 $actor = $this->getActor();
15
16 $auditors = $this->generateOldValue($object);
17 $old_auditors = $auditors;
18
19 $request_status = PhabricatorAuditRequestStatus::AUDIT_REQUESTED;
20
21 $rem = idx($value, '-', array());
22 foreach ($rem as $phid) {
23 unset($auditors[$phid]);
24 }
25
26 $add = idx($value, '+', array());
27 $add_map = array();
28 foreach ($add as $phid) {
29 $add_map[$phid] = $request_status;
30 }
31
32 $set = idx($value, '=', null);
33 if ($set !== null) {
34 foreach ($set as $phid) {
35 $add_map[$phid] = $request_status;
36 }
37
38 $auditors = array();
39 }
40
41 foreach ($add_map as $phid => $new_status) {
42 $old_status = idx($old_auditors, $phid);
43
44 if ($old_status) {
45 $auditors[$phid] = $old_status;
46 continue;
47 }
48
49 $auditors[$phid] = $new_status;
50 }
51
52 return $auditors;
53 }
54
55 public function getTransactionHasEffect($object, $old, $new) {
56 ksort($old);
57 ksort($new);
58 return ($old !== $new);
59 }
60
61 public function mergeTransactions(
62 $object,
63 PhabricatorApplicationTransaction $u,
64 PhabricatorApplicationTransaction $v) {
65
66 $u_new = $u->getNewValue();
67 $v_new = $v->getNewValue();
68
69 $result = $v_new;
70 foreach (array('-', '+') as $key) {
71 $u_value = idx($u_new, $key, array());
72 $v_value = idx($v_new, $key, array());
73
74 $merged = $u_value + $v_value;
75
76 if ($merged) {
77 $result[$key] = $merged;
78 }
79 }
80
81 $u->setNewValue($result);
82
83 return $u;
84 }
85
86 public function applyExternalEffects($object, $value) {
87 $src_phid = $object->getPHID();
88
89 $old = $this->generateOldValue($object);
90 $new = $value;
91
92 $auditors = $object->getAudits();
93 $auditors = mpull($auditors, null, 'getAuditorPHID');
94
95 $rem = array_diff_key($old, $new);
96 foreach ($rem as $phid => $status) {
97 $auditor = idx($auditors, $phid);
98 if ($auditor) {
99 $auditor->delete();
100 }
101 }
102
103 $this->updateAudits($object, $new);
104 }
105
106 public function getTitle() {
107 $old = $this->getOldValue();
108 $new = $this->getNewValue();
109
110 $rem = array_diff_key($old, $new);
111 $add = array_diff_key($new, $old);
112 $rem_phids = array_keys($rem);
113 $add_phids = array_keys($add);
114 $total_count = count($rem) + count($add);
115
116 if ($rem && $add) {
117 return pht(
118 '%s edited %s auditor(s), removed %s: %s; added %s: %s.',
119 $this->renderAuthor(),
120 new PhutilNumber($total_count),
121 phutil_count($rem_phids),
122 $this->renderHandleList($rem_phids),
123 phutil_count($add_phids),
124 $this->renderHandleList($add_phids));
125 } else if ($add) {
126 return pht(
127 '%s added %s auditor(s): %s.',
128 $this->renderAuthor(),
129 phutil_count($add_phids),
130 $this->renderHandleList($add_phids));
131 } else {
132 return pht(
133 '%s removed %s auditor(s): %s.',
134 $this->renderAuthor(),
135 phutil_count($rem_phids),
136 $this->renderHandleList($rem_phids));
137 }
138 }
139
140 public function getTitleForFeed() {
141 $old = $this->getOldValue();
142 $new = $this->getNewValue();
143
144 $rem = array_diff_key($old, $new);
145 $add = array_diff_key($new, $old);
146 $rem_phids = array_keys($rem);
147 $add_phids = array_keys($add);
148 $total_count = count($rem) + count($add);
149
150 if ($rem && $add) {
151 return pht(
152 '%s edited %s auditor(s) for %s, removed %s: %s; added %s: %s.',
153 $this->renderAuthor(),
154 new PhutilNumber($total_count),
155 $this->renderObject(),
156 phutil_count($rem_phids),
157 $this->renderHandleList($rem_phids),
158 phutil_count($add_phids),
159 $this->renderHandleList($add_phids));
160 } else if ($add) {
161 return pht(
162 '%s added %s auditor(s) for %s: %s.',
163 $this->renderAuthor(),
164 phutil_count($add_phids),
165 $this->renderObject(),
166 $this->renderHandleList($add_phids));
167 } else {
168 return pht(
169 '%s removed %s auditor(s) for %s: %s.',
170 $this->renderAuthor(),
171 phutil_count($rem_phids),
172 $this->renderObject(),
173 $this->renderHandleList($rem_phids));
174 }
175 }
176
177 public function validateTransactions($object, array $xactions) {
178 $actor = $this->getActor();
179 $errors = array();
180
181 if (!$xactions) {
182 return $errors;
183 }
184
185 $author_phid = $object->getEffectiveAuthorPHID();
186 $can_author_close_key = 'audit.can-author-close-audit';
187 $can_author_close = PhabricatorEnv::getEnvConfig($can_author_close_key);
188
189 $old = $this->generateOldValue($object);
190 foreach ($xactions as $xaction) {
191 $new = $this->generateNewValue($object, $xaction->getNewValue());
192
193 $add = array_diff_key($new, $old);
194 if (!$add) {
195 continue;
196 }
197
198 $objects = id(new PhabricatorObjectQuery())
199 ->setViewer($actor)
200 ->withPHIDs(array_keys($add))
201 ->execute();
202 $objects = mpull($objects, null, 'getPHID');
203
204 foreach ($add as $phid => $status) {
205 if (!isset($objects[$phid])) {
206 $errors[] = $this->newInvalidError(
207 pht(
208 'Auditor "%s" is not a valid object.',
209 $phid),
210 $xaction);
211 continue;
212 }
213
214 switch (phid_get_type($phid)) {
215 case PhabricatorPeopleUserPHIDType::TYPECONST:
216 case PhabricatorOwnersPackagePHIDType::TYPECONST:
217 case PhabricatorProjectProjectPHIDType::TYPECONST:
218 break;
219 default:
220 $errors[] = $this->newInvalidError(
221 pht(
222 'Auditor "%s" must be a user, a package, or a project.',
223 $phid),
224 $xaction);
225 continue 2;
226 }
227
228 $is_self = ($phid === $author_phid);
229 if ($is_self && !$can_author_close) {
230 $errors[] = $this->newInvalidError(
231 pht('The author of a commit can not be an auditor.'),
232 $xaction);
233 continue;
234 }
235 }
236 }
237
238 return $errors;
239 }
240
241}