@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 DifferentialRevisionEditEngine
4 extends PhabricatorEditEngine {
5
6 private $diff;
7
8 const ENGINECONST = 'differential.revision';
9
10 const ACTIONGROUP_REVIEW = 'review';
11 const ACTIONGROUP_REVISION = 'revision';
12
13 public function getEngineName() {
14 return pht('Revisions');
15 }
16
17 public function getSummaryHeader() {
18 return pht('Configure Revision Forms');
19 }
20
21 public function getSummaryText() {
22 return pht(
23 'Configure creation and editing revision forms in Differential.');
24 }
25
26 public function getEngineApplicationClass() {
27 return PhabricatorDifferentialApplication::class;
28 }
29
30 public function isEngineConfigurable() {
31 return false;
32 }
33
34 protected function newEditableObject() {
35 $viewer = $this->getViewer();
36 return DifferentialRevision::initializeNewRevision($viewer);
37 }
38
39 protected function newObjectQuery() {
40 return id(new DifferentialRevisionQuery())
41 ->needActiveDiffs(true)
42 ->needReviewers(true)
43 ->needReviewerAuthority(true);
44 }
45
46 protected function getObjectCreateTitleText($object) {
47 return pht('Create New Revision');
48 }
49
50 protected function getObjectEditTitleText($object) {
51 $monogram = $object->getMonogram();
52 $title = $object->getTitle();
53
54 $diff = $this->getDiff();
55 if ($diff) {
56 return pht('Update Revision %s: %s', $monogram, $title);
57 } else {
58 return pht('Edit Revision %s: %s', $monogram, $title);
59 }
60 }
61
62 protected function getObjectEditShortText($object) {
63 return $object->getMonogram();
64 }
65
66 public function getCreateURI($form_key) {
67 return '/differential/diff/create/';
68 }
69
70 protected function getObjectCreateShortText() {
71 return pht('Create Revision');
72 }
73
74 protected function getObjectName() {
75 return pht('Revision');
76 }
77
78 protected function getCommentViewButtonText($object) {
79 if ($object->isDraft()) {
80 return pht('Submit Quietly');
81 }
82
83 return parent::getCommentViewButtonText($object);
84 }
85
86 protected function getObjectViewURI($object) {
87 return $object->getURI();
88 }
89
90 protected function getEditorURI() {
91 return $this->getApplication()->getApplicationURI('revision/edit/');
92 }
93
94 public function setDiff(DifferentialDiff $diff) {
95 $this->diff = $diff;
96 return $this;
97 }
98
99 public function getDiff() {
100 return $this->diff;
101 }
102
103 protected function newCommentActionGroups() {
104 return array(
105 id(new PhabricatorEditEngineCommentActionGroup())
106 ->setKey(self::ACTIONGROUP_REVIEW)
107 ->setLabel(pht('Review Actions')),
108 id(new PhabricatorEditEngineCommentActionGroup())
109 ->setKey(self::ACTIONGROUP_REVISION)
110 ->setLabel(pht('Revision Actions')),
111 );
112 }
113
114 protected function buildCustomEditFields($object) {
115 $viewer = $this->getViewer();
116
117 $plan_required = PhabricatorEnv::getEnvConfig(
118 'differential.require-test-plan-field');
119 $plan_enabled = $this->isCustomFieldEnabled(
120 $object,
121 'differential:test-plan');
122
123 $diff = $this->getDiff();
124 if ($diff) {
125 $diff_phid = $diff->getPHID();
126 } else {
127 $diff_phid = null;
128 }
129
130 $is_create = $this->getIsCreate();
131 $is_update = ($diff && !$is_create);
132
133 $fields = array();
134
135 $fields[] = id(new PhabricatorHandlesEditField())
136 ->setKey(DifferentialRevisionUpdateTransaction::EDITKEY)
137 ->setLabel(pht('Update Diff'))
138 ->setDescription(pht('New diff to create or update the revision with.'))
139 ->setConduitDescription(pht('Create or update a revision with a diff.'))
140 ->setConduitTypeDescription(pht('PHID of the diff.'))
141 ->setTransactionType(
142 DifferentialRevisionUpdateTransaction::TRANSACTIONTYPE)
143 ->setHandleParameterType(new AphrontPHIDListHTTPParameterType())
144 ->setSingleValue($diff_phid)
145 ->setIsFormField((bool)$diff)
146 ->setIsReorderable(false)
147 ->setIsDefaultable(false)
148 ->setIsInvisible(true)
149 ->setIsLockable(false);
150
151 if ($is_update) {
152 $fields[] = id(new PhabricatorInstructionsEditField())
153 ->setKey('update.help')
154 ->setValue(pht('Describe the updates you have made to the diff.'));
155 $fields[] = id(new PhabricatorCommentEditField())
156 ->setKey('update.comment')
157 ->setLabel(pht('Comment'))
158 ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
159 ->setIsWebOnly(true)
160 ->setDescription(pht('Comments providing context for the update.'));
161 $fields[] = id(new PhabricatorSubmitEditField())
162 ->setKey('update.submit')
163 ->setValue($this->getObjectEditButtonText($object));
164 $fields[] = id(new PhabricatorDividerEditField())
165 ->setKey('update.note');
166 }
167
168 $fields[] = id(new PhabricatorTextEditField())
169 ->setKey(DifferentialRevisionTitleTransaction::EDITKEY)
170 ->setLabel(pht('Title'))
171 ->setIsRequired(true)
172 ->setTransactionType(
173 DifferentialRevisionTitleTransaction::TRANSACTIONTYPE)
174 ->setDescription(pht('The title of the revision.'))
175 ->setConduitDescription(pht('Retitle the revision.'))
176 ->setConduitTypeDescription(pht('New revision title.'))
177 ->setValue($object->getTitle());
178
179 $author_field = id(new PhabricatorDatasourceEditField())
180 ->setKey(DifferentialRevisionAuthorTransaction::EDITKEY)
181 ->setLabel(pht('Author'))
182 ->setDatasource(new PhabricatorPeopleDatasource())
183 ->setTransactionType(
184 DifferentialRevisionAuthorTransaction::TRANSACTIONTYPE)
185 ->setDescription(pht('Make someone else the author of this revision.'))
186 ->setConduitDescription(
187 pht('Make another user the author of this revision.'))
188 ->setConduitTypeDescription(pht('New author.'))
189 ->setSingleValue($object->getAuthorPHID());
190
191 // Don't show the "Author" field when creating a revision using the web
192 // workflow, since it adds more noise than signal to this workflow.
193 if ($is_create) {
194 $author_field->setIsHidden(true);
195 }
196
197 // Only show the "Change Author" comment action to the current revision
198 // author. Other users can use "Edit Revision", it's just very unlikely
199 // that they're interested in this action.
200 if ($viewer->getPHID() === $object->getAuthorPHID()) {
201 $author_field->setCommentActionLabel(pht('Change Author'));
202 }
203
204 $fields[] = $author_field;
205
206 $fields[] = id(new PhabricatorRemarkupEditField())
207 ->setKey(DifferentialRevisionSummaryTransaction::EDITKEY)
208 ->setLabel(pht('Summary'))
209 ->setTransactionType(
210 DifferentialRevisionSummaryTransaction::TRANSACTIONTYPE)
211 ->setDescription(pht('The summary of the revision.'))
212 ->setConduitDescription(pht('Change the revision summary.'))
213 ->setConduitTypeDescription(pht('New revision summary.'))
214 ->setValue($object->getSummary());
215
216 if ($plan_enabled) {
217 $fields[] = id(new PhabricatorRemarkupEditField())
218 ->setKey(DifferentialRevisionTestPlanTransaction::EDITKEY)
219 ->setLabel(pht('Test Plan'))
220 ->setIsRequired($plan_required)
221 ->setTransactionType(
222 DifferentialRevisionTestPlanTransaction::TRANSACTIONTYPE)
223 ->setDescription(
224 pht('Actions performed to verify the behavior of the change.'))
225 ->setConduitDescription(pht('Update the revision test plan.'))
226 ->setConduitTypeDescription(pht('New test plan.'))
227 ->setValue($object->getTestPlan());
228 }
229
230 $fields[] = id(new PhabricatorDatasourceEditField())
231 ->setKey(DifferentialRevisionReviewersTransaction::EDITKEY)
232 ->setLabel(pht('Reviewers'))
233 ->setDatasource(new DifferentialReviewerDatasource())
234 ->setUseEdgeTransactions(true)
235 ->setTransactionType(
236 DifferentialRevisionReviewersTransaction::TRANSACTIONTYPE)
237 ->setCommentActionLabel(pht('Change Reviewers'))
238 ->setDescription(pht('Reviewers for this revision.'))
239 ->setConduitDescription(pht('Change the reviewers for this revision.'))
240 ->setConduitTypeDescription(pht('New reviewers.'))
241 ->setValue($object->getReviewerPHIDsForEdit());
242
243 // Prefill Repository for example when coming from "Attach To".
244 $repository_phid = $object->getRepositoryPHID();
245 if ($is_create && !$repository_phid && $diff) {
246 $repository_phid = $diff->getRepositoryPHID();
247 }
248
249 $fields[] = id(new PhabricatorDatasourceEditField())
250 ->setKey('repositoryPHID')
251 ->setLabel(pht('Repository'))
252 ->setDatasource(new DiffusionRepositoryDatasource())
253 ->setTransactionType(
254 DifferentialRevisionRepositoryTransaction::TRANSACTIONTYPE)
255 ->setDescription(pht('The repository the revision belongs to.'))
256 ->setConduitDescription(pht('Change the repository for this revision.'))
257 ->setConduitTypeDescription(pht('New repository.'))
258 ->setSingleValue($repository_phid);
259
260 // This is a little flimsy, but allows "Maniphest Tasks: ..." to continue
261 // working properly in commit messages until we fully sort out T5873.
262 $fields[] = id(new PhabricatorHandlesEditField())
263 ->setKey('tasks')
264 ->setUseEdgeTransactions(true)
265 ->setIsFormField(false)
266 ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
267 ->setMetadataValue(
268 'edge:type',
269 DifferentialRevisionHasTaskEdgeType::EDGECONST)
270 ->setDescription(pht('Tasks associated with this revision.'))
271 ->setConduitDescription(pht('Change associated tasks.'))
272 ->setConduitTypeDescription(pht('List of tasks.'))
273 ->setValue(array());
274
275 $fields[] = id(new PhabricatorHandlesEditField())
276 ->setKey('parents')
277 ->setUseEdgeTransactions(true)
278 ->setIsFormField(false)
279 ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
280 ->setMetadataValue(
281 'edge:type',
282 DifferentialRevisionDependsOnRevisionEdgeType::EDGECONST)
283 ->setDescription(pht('Parent revisions of this revision.'))
284 ->setConduitDescription(pht('Change associated parent revisions.'))
285 ->setConduitTypeDescription(pht('List of revisions.'))
286 ->setValue(array());
287
288 $fields[] = id(new PhabricatorHandlesEditField())
289 ->setKey('children')
290 ->setUseEdgeTransactions(true)
291 ->setIsFormField(false)
292 ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
293 ->setMetadataValue(
294 'edge:type',
295 DifferentialRevisionDependedOnByRevisionEdgeType::EDGECONST)
296 ->setDescription(pht('Child revisions of this revision.'))
297 ->setConduitDescription(pht('Change associated child revisions.'))
298 ->setConduitTypeDescription(pht('List of revisions.'))
299 ->setValue(array());
300
301 $actions = DifferentialRevisionActionTransaction::loadAllActions();
302 $actions = msortv($actions, 'getRevisionActionOrderVector');
303
304 foreach ($actions as $key => $action) {
305 $fields[] = $action->newEditField($object, $viewer);
306 }
307
308 $fields[] = id(new PhabricatorBoolEditField())
309 ->setKey('draft')
310 ->setLabel(pht('Hold as Draft'))
311 ->setIsFormField(false)
312 ->setOptions(
313 pht('Autosubmit Once Builds Finish'),
314 pht('Hold as Draft'))
315 ->setTransactionType(
316 DifferentialRevisionHoldDraftTransaction::TRANSACTIONTYPE)
317 ->setDescription(pht('Hold revision as draft.'))
318 ->setConduitDescription(
319 pht(
320 'Change autosubmission from draft state after builds finish.'))
321 ->setConduitTypeDescription(pht('New "Hold as Draft" setting.'))
322 ->setValue($object->getHoldAsDraft());
323
324 return $fields;
325 }
326
327 private function isCustomFieldEnabled(DifferentialRevision $revision, $key) {
328 $field_list = PhabricatorCustomField::getObjectFields(
329 $revision,
330 PhabricatorCustomField::ROLE_VIEW);
331
332 $fields = $field_list->getFields();
333 return isset($fields[$key]);
334 }
335
336 protected function newAutomaticCommentTransactions($object) {
337 $viewer = $this->getViewer();
338
339 $editor = $object->getApplicationTransactionEditor()
340 ->setActor($viewer);
341
342 $xactions = $editor->newAutomaticInlineTransactions(
343 $object,
344 DifferentialTransaction::TYPE_INLINE,
345 new DifferentialDiffInlineCommentQuery());
346
347 return $xactions;
348 }
349
350 protected function newCommentPreviewContent($object, array $xactions) {
351 $viewer = $this->getViewer();
352 $type_inline = DifferentialTransaction::TYPE_INLINE;
353
354 $inlines = array();
355 foreach ($xactions as $xaction) {
356 if ($xaction->getTransactionType() === $type_inline) {
357 $inlines[] = $xaction->getComment();
358 }
359 }
360
361 $content = array();
362
363 if ($inlines) {
364 // Reload inlines to get inline context.
365 $inlines = id(new DifferentialDiffInlineCommentQuery())
366 ->setViewer($viewer)
367 ->withIDs(mpull($inlines, 'getID'))
368 ->needInlineContext(true)
369 ->execute();
370
371 $inline_preview = id(new PHUIDiffInlineCommentPreviewListView())
372 ->setViewer($viewer)
373 ->setInlineComments($inlines);
374
375 $content[] = phutil_tag(
376 'div',
377 array(
378 'id' => 'inline-comment-preview',
379 ),
380 $inline_preview);
381 }
382
383 return $content;
384 }
385
386
387}