@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 * Common code for standard field types which store lists of PHIDs.
5 */
6abstract class PhabricatorStandardCustomFieldPHIDs
7 extends PhabricatorStandardCustomField {
8
9 public function buildFieldIndexes() {
10 $indexes = array();
11
12 $value = $this->getFieldValue();
13 if (is_array($value)) {
14 foreach ($value as $phid) {
15 $indexes[] = $this->newStringIndex($phid);
16 }
17 }
18
19 return $indexes;
20 }
21
22 public function readValueFromRequest(AphrontRequest $request) {
23 $value = $request->getArr($this->getFieldKey());
24 $this->setFieldValue($value);
25 }
26
27 public function getValueForStorage() {
28 $value = $this->getFieldValue();
29 if (!$value) {
30 return null;
31 }
32
33 return json_encode(array_values($value));
34 }
35
36 public function setValueFromStorage($value) {
37 // NOTE: We're accepting either a JSON string (a real storage value) or
38 // an array (from HTTP parameter prefilling). This is a little hacky, but
39 // should hold until this can get cleaned up more thoroughly.
40 // TODO: Clean this up.
41
42 if (is_string($value) && phutil_nonempty_string($value)) {
43 $value = json_decode($value, true);
44 }
45 $this->setFieldValue($value);
46
47 return $this;
48 }
49
50 public function readApplicationSearchValueFromRequest(
51 PhabricatorApplicationSearchEngine $engine,
52 AphrontRequest $request) {
53 return $request->getArr($this->getFieldKey());
54 }
55
56 public function applyApplicationSearchConstraintToQuery(
57 PhabricatorApplicationSearchEngine $engine,
58 PhabricatorCursorPagedPolicyAwareQuery $query,
59 $value) {
60 if ($value) {
61 $query->withApplicationSearchContainsConstraint(
62 $this->newStringIndex(null),
63 $value);
64 }
65 }
66
67 public function getRequiredHandlePHIDsForPropertyView() {
68 $value = $this->getFieldValue();
69 if ($value) {
70 return $value;
71 }
72 return array();
73 }
74
75 protected function renderValue() {
76 $value = $this->getFieldValue();
77 if (!$value) {
78 return null;
79 }
80
81 return $this->getViewer()->renderHandleList($value)
82 ->setAsInline(true);
83 }
84
85 public function getRequiredHandlePHIDsForEdit() {
86 $value = $this->getFieldValue();
87 if ($value) {
88 return $value;
89 } else {
90 return array();
91 }
92 }
93
94 public function getApplicationTransactionRequiredHandlePHIDs(
95 PhabricatorApplicationTransaction $xaction) {
96
97 $old = $this->decodeValue($xaction->getOldValue());
98 $new = $this->decodeValue($xaction->getNewValue());
99
100 $add = array_diff($new, $old);
101 $rem = array_diff($old, $new);
102
103 return array_merge($add, $rem);
104 }
105
106 public function getApplicationTransactionTitle(
107 PhabricatorApplicationTransaction $xaction) {
108 $author_phid = $xaction->getAuthorPHID();
109
110 $old = $this->decodeValue($xaction->getOldValue());
111 $new = $this->decodeValue($xaction->getNewValue());
112
113 $add = array_diff($new, $old);
114 $rem = array_diff($old, $new);
115
116 if ($add && !$rem) {
117 return pht(
118 '%s updated %s, added %s: %s.',
119 $xaction->renderHandleLink($author_phid),
120 $this->getFieldName(),
121 phutil_count($add),
122 $xaction->renderHandleList($add));
123 } else if ($rem && !$add) {
124 return pht(
125 '%s updated %s, removed %s: %s.',
126 $xaction->renderHandleLink($author_phid),
127 $this->getFieldName(),
128 phutil_count($rem),
129 $xaction->renderHandleList($rem));
130 } else {
131 return pht(
132 '%s updated %s, added %s: %s; removed %s: %s.',
133 $xaction->renderHandleLink($author_phid),
134 $this->getFieldName(),
135 phutil_count($add),
136 $xaction->renderHandleList($add),
137 phutil_count($rem),
138 $xaction->renderHandleList($rem));
139 }
140 }
141
142 public function getApplicationTransactionTitleForFeed(
143 PhabricatorApplicationTransaction $xaction) {
144 $author_phid = $xaction->getAuthorPHID();
145 $object_phid = $xaction->getObjectPHID();
146
147 $old = $this->decodeValue($xaction->getOldValue());
148 $new = $this->decodeValue($xaction->getNewValue());
149
150 $add = array_diff($new, $old);
151 $rem = array_diff($old, $new);
152
153 if ($add && !$rem) {
154 return pht(
155 '%s updated %s for %s, added %s: %s.',
156 $xaction->renderHandleLink($author_phid),
157 $this->getFieldName(),
158 $xaction->renderHandleLink($object_phid),
159 phutil_count($add),
160 $xaction->renderHandleList($add));
161 } else if ($rem && !$add) {
162 return pht(
163 '%s updated %s for %s, removed %s: %s.',
164 $xaction->renderHandleLink($author_phid),
165 $this->getFieldName(),
166 $xaction->renderHandleLink($object_phid),
167 phutil_count($rem),
168 $xaction->renderHandleList($rem));
169 } else {
170 return pht(
171 '%s updated %s for %s, added %s: %s; removed %s: %s.',
172 $xaction->renderHandleLink($author_phid),
173 $this->getFieldName(),
174 $xaction->renderHandleLink($object_phid),
175 phutil_count($add),
176 $xaction->renderHandleList($add),
177 phutil_count($rem),
178 $xaction->renderHandleList($rem));
179 }
180 }
181
182 public function validateApplicationTransactions(
183 PhabricatorApplicationTransactionEditor $editor,
184 $type,
185 array $xactions) {
186
187 $errors = parent::validateApplicationTransactions(
188 $editor,
189 $type,
190 $xactions);
191
192 // If the user is adding PHIDs, make sure the new PHIDs are valid and
193 // visible to the actor. It's OK for a user to edit a field which includes
194 // some invalid or restricted values, but they can't add new ones.
195
196 foreach ($xactions as $xaction) {
197 $old = $this->decodeValue($xaction->getOldValue());
198 $new = $this->decodeValue($xaction->getNewValue());
199
200 $add = array_diff($new, $old);
201
202 $invalid = PhabricatorObjectQuery::loadInvalidPHIDsForViewer(
203 $editor->getActor(),
204 $add);
205
206 if ($invalid) {
207 $error = new PhabricatorApplicationTransactionValidationError(
208 $type,
209 pht('Invalid'),
210 pht(
211 'Some of the selected PHIDs in field "%s" are invalid or '.
212 'restricted: %s.',
213 $this->getFieldName(),
214 implode(', ', $invalid)),
215 $xaction);
216 $errors[] = $error;
217 $this->setFieldError(pht('Invalid'));
218 }
219 }
220
221 return $errors;
222 }
223
224 public function shouldAppearInHerald() {
225 return true;
226 }
227
228 public function getHeraldFieldConditions() {
229 return array(
230 HeraldAdapter::CONDITION_INCLUDE_ALL,
231 HeraldAdapter::CONDITION_INCLUDE_ANY,
232 HeraldAdapter::CONDITION_INCLUDE_NONE,
233 HeraldAdapter::CONDITION_EXISTS,
234 HeraldAdapter::CONDITION_NOT_EXISTS,
235 );
236 }
237
238 public function getHeraldFieldStandardType() {
239 return HeraldField::STANDARD_PHID_NULLABLE;
240 }
241
242 public function getHeraldFieldValue() {
243 // If the field has a `null` value, make sure we hand an `array()` to
244 // Herald.
245 $value = parent::getHeraldFieldValue();
246 if ($value) {
247 return $value;
248 }
249 return array();
250 }
251
252 protected function decodeValue($value) {
253 if ($value === null) {
254 return array();
255 }
256
257 $value = json_decode($value);
258 if (!is_array($value)) {
259 $value = array();
260 }
261
262 return $value;
263 }
264
265 protected function getHTTPParameterType() {
266 return new AphrontPHIDListHTTPParameterType();
267 }
268
269}