@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 PholioMockEditController extends PholioController {
4
5 public function handleRequest(AphrontRequest $request) {
6 $viewer = $request->getViewer();
7 $id = $request->getURIData('id');
8
9 if ($id) {
10 $mock = id(new PholioMockQuery())
11 ->setViewer($viewer)
12 ->needImages(true)
13 ->requireCapabilities(
14 array(
15 PhabricatorPolicyCapability::CAN_VIEW,
16 PhabricatorPolicyCapability::CAN_EDIT,
17 ))
18 ->withIDs(array($id))
19 ->executeOne();
20
21 if (!$mock) {
22 return new Aphront404Response();
23 }
24
25 $title = pht('Edit Mock: %s', $mock->getName());
26
27 $is_new = false;
28 $mock_images = $mock->getActiveImages();
29 $files = mpull($mock_images, 'getFile');
30 $mock_images = mpull($mock_images, null, 'getFilePHID');
31 } else {
32 $mock = PholioMock::initializeNewMock($viewer);
33
34 $title = pht('Create Mock');
35
36 $is_new = true;
37 $files = array();
38 $mock_images = array();
39 }
40
41 if ($is_new) {
42 $v_projects = array();
43 } else {
44 $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs(
45 $mock->getPHID(),
46 PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
47 $v_projects = array_reverse($v_projects);
48 }
49
50 $e_name = true;
51 $e_images = count($mock_images) ? null : true;
52 $errors = array();
53 $posted_mock_images = array();
54
55 $v_name = $mock->getName();
56 $v_desc = $mock->getDescription();
57 $v_view = $mock->getViewPolicy();
58 $v_edit = $mock->getEditPolicy();
59 $v_cc = PhabricatorSubscribersQuery::loadSubscribersForPHID(
60 $mock->getPHID());
61 $v_space = $mock->getSpacePHID();
62
63 if ($request->isFormPost()) {
64 $xactions = array();
65
66 $type_name = PholioMockNameTransaction::TRANSACTIONTYPE;
67 $type_desc = PholioMockDescriptionTransaction::TRANSACTIONTYPE;
68 $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
69 $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
70 $type_cc = PhabricatorTransactions::TYPE_SUBSCRIBERS;
71 $type_space = PhabricatorTransactions::TYPE_SPACE;
72
73 $v_name = $request->getStr('name');
74 $v_desc = $request->getStr('description');
75 $v_view = $request->getStr('can_view');
76 $v_edit = $request->getStr('can_edit');
77 $v_cc = $request->getArr('cc');
78 $v_projects = $request->getArr('projects');
79 $v_space = $request->getStr('spacePHID');
80
81 $mock_xactions = array();
82 $mock_xactions[$type_name] = $v_name;
83 $mock_xactions[$type_desc] = $v_desc;
84 $mock_xactions[$type_view] = $v_view;
85 $mock_xactions[$type_edit] = $v_edit;
86 $mock_xactions[$type_cc] = array('=' => $v_cc);
87 $mock_xactions[$type_space] = $v_space;
88
89 $file_phids = $request->getArr('file_phids');
90 if ($file_phids) {
91 $files = id(new PhabricatorFileQuery())
92 ->setViewer($viewer)
93 ->withPHIDs($file_phids)
94 ->execute();
95 $files = mpull($files, null, 'getPHID');
96 $files = array_select_keys($files, $file_phids);
97 } else {
98 $files = array();
99 }
100
101 if (!$files) {
102 $e_images = pht('Required');
103 $errors[] = pht('You must add at least one image to the mock.');
104 } else {
105 $mock->setCoverPHID(head($files)->getPHID());
106 }
107
108 foreach ($mock_xactions as $type => $value) {
109 $xactions[$type] = id(new PholioTransaction())
110 ->setTransactionType($type)
111 ->setIsCreateTransaction($is_new)
112 ->setNewValue($value);
113 }
114
115 $order = $request->getStrList('imageOrder');
116 $sequence_map = array_flip($order);
117 $replaces = $request->getArr('replaces');
118 $replaces_map = array_flip($replaces);
119
120 /**
121 * Foreach file posted, check to see whether we are replacing an image,
122 * adding an image, or simply updating image metadata. Create
123 * transactions for these cases as appropos.
124 */
125 foreach ($files as $file_phid => $file) {
126 $replaces_image_phid = null;
127 if (isset($replaces_map[$file_phid])) {
128 $old_file_phid = $replaces_map[$file_phid];
129 if ($old_file_phid != $file_phid) {
130 $old_image = idx($mock_images, $old_file_phid);
131 if ($old_image) {
132 $replaces_image_phid = $old_image->getPHID();
133 }
134 }
135 }
136
137 $existing_image = idx($mock_images, $file_phid);
138
139 $img_title = (string)$request->getStr('title_'.$file_phid);
140 $description = (string)$request->getStr('description_'.$file_phid);
141 $sequence = $sequence_map[$file_phid];
142
143 if ($replaces_image_phid) {
144 $replace_image = PholioImage::initializeNewImage()
145 ->setAuthorPHID($viewer->getPHID())
146 ->setReplacesImagePHID($replaces_image_phid)
147 ->setFilePHID($file_phid)
148 ->attachFile($file)
149 ->setName(strlen($img_title) ? $img_title : $file->getName())
150 ->setDescription($description)
151 ->setSequence($sequence)
152 ->save();
153
154 $xactions[] = id(new PholioTransaction())
155 ->setTransactionType(PholioImageReplaceTransaction::TRANSACTIONTYPE)
156 ->setNewValue($replace_image->getPHID());
157
158 $posted_mock_images[] = $replace_image;
159 } else if (!$existing_image) { // this is an add
160 $add_image = PholioImage::initializeNewImage()
161 ->setAuthorPHID($viewer->getPHID())
162 ->setFilePHID($file_phid)
163 ->attachFile($file)
164 ->setName(strlen($img_title) ? $img_title : $file->getName())
165 ->setDescription($description)
166 ->setSequence($sequence)
167 ->save();
168
169 $xactions[] = id(new PholioTransaction())
170 ->setTransactionType(PholioImageFileTransaction::TRANSACTIONTYPE)
171 ->setNewValue(
172 array('+' => array($add_image->getPHID())));
173 $posted_mock_images[] = $add_image;
174 } else {
175 $xactions[] = id(new PholioTransaction())
176 ->setTransactionType(PholioImageNameTransaction::TRANSACTIONTYPE)
177 ->setNewValue(
178 array($existing_image->getPHID() => $img_title));
179 $xactions[] = id(new PholioTransaction())
180 ->setTransactionType(
181 PholioImageDescriptionTransaction::TRANSACTIONTYPE)
182 ->setNewValue(
183 array($existing_image->getPHID() => $description));
184 $xactions[] = id(new PholioTransaction())
185 ->setTransactionType(
186 PholioImageSequenceTransaction::TRANSACTIONTYPE)
187 ->setNewValue(
188 array($existing_image->getPHID() => $sequence));
189
190 $posted_mock_images[] = $existing_image;
191 }
192 }
193 foreach ($mock_images as $file_phid => $mock_image) {
194 if (!isset($files[$file_phid]) && !isset($replaces[$file_phid])) {
195 // this is an outright delete
196 $xactions[] = id(new PholioTransaction())
197 ->setTransactionType(PholioImageFileTransaction::TRANSACTIONTYPE)
198 ->setNewValue(
199 array('-' => array($mock_image->getPHID())));
200 }
201 }
202
203 if (!$errors) {
204 $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
205 $xactions[] = id(new PholioTransaction())
206 ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
207 ->setMetadataValue('edge:type', $proj_edge_type)
208 ->setNewValue(array('=' => array_fuse($v_projects)));
209
210 $editor = id(new PholioMockEditor())
211 ->setContentSourceFromRequest($request)
212 ->setContinueOnNoEffect(true)
213 ->setActor($viewer);
214
215 try {
216 $xactions = $editor->applyTransactions($mock, $xactions);
217 return id(new AphrontRedirectResponse())
218 ->setURI('/M'.$mock->getID());
219 } catch (PhabricatorApplicationTransactionValidationException $ex) {
220 $errors = mpull($ex->getErrors(), 'getMessage');
221 }
222 }
223 }
224
225 if ($id) {
226 $submit = id(new AphrontFormSubmitControl())
227 ->addCancelButton('/M'.$id)
228 ->setValue(pht('Save'));
229 } else {
230 $submit = id(new AphrontFormSubmitControl())
231 ->addCancelButton($this->getApplicationURI())
232 ->setValue(pht('Create'));
233 }
234
235 $policies = id(new PhabricatorPolicyQuery())
236 ->setViewer($viewer)
237 ->setObject($mock)
238 ->execute();
239
240 // NOTE: Make this show up correctly on the rendered form.
241 $mock->setViewPolicy($v_view);
242 $mock->setEditPolicy($v_edit);
243
244 $image_elements = array();
245 if ($posted_mock_images) {
246 $display_mock_images = $posted_mock_images;
247 } else {
248 $display_mock_images = $mock_images;
249 }
250 foreach ($display_mock_images as $mock_image) {
251 $image_elements[] = id(new PholioUploadedImageView())
252 ->setViewer($viewer)
253 ->setImage($mock_image)
254 ->setReplacesPHID($mock_image->getFilePHID());
255 }
256
257 $list_id = celerity_generate_unique_node_id();
258 $drop_id = celerity_generate_unique_node_id();
259 $order_id = celerity_generate_unique_node_id();
260
261 $list_control = phutil_tag(
262 'div',
263 array(
264 'id' => $list_id,
265 'class' => 'pholio-edit-list',
266 ),
267 $image_elements);
268
269 $drop_control = phutil_tag(
270 'a',
271 array(
272 'id' => $drop_id,
273 'class' => 'pholio-edit-drop',
274 ),
275 pht('Click here, or drag and drop images to add them to the mock.'));
276
277 $order_control = phutil_tag(
278 'input',
279 array(
280 'type' => 'hidden',
281 'name' => 'imageOrder',
282 'id' => $order_id,
283 ));
284
285 Javelin::initBehavior(
286 'pholio-mock-edit',
287 array(
288 'listID' => $list_id,
289 'dropID' => $drop_id,
290 'orderID' => $order_id,
291 'uploadURI' => '/file/dropupload/',
292 'renderURI' => $this->getApplicationURI('image/upload/'),
293 'pht' => array(
294 'uploading' => pht('Uploading Image...'),
295 'uploaded' => pht('Upload Complete...'),
296 'undo' => pht('Undo'),
297 'removed' => pht('This image will be removed from the mock.'),
298 ),
299 ));
300
301 require_celerity_resource('pholio-edit-css');
302 $form = id(new AphrontFormView())
303 ->setViewer($viewer)
304 ->appendChild($order_control)
305 ->appendChild(
306 id(new AphrontFormTextControl())
307 ->setName('name')
308 ->setValue($v_name)
309 ->setLabel(pht('Name'))
310 ->setError($e_name))
311 ->appendChild(
312 id(new PhabricatorRemarkupControl())
313 ->setName('description')
314 ->setValue($v_desc)
315 ->setLabel(pht('Description'))
316 ->setViewer($viewer))
317 ->appendControl(
318 id(new AphrontFormTokenizerControl())
319 ->setLabel(pht('Project Tags'))
320 ->setName('projects')
321 ->setValue($v_projects)
322 ->setDatasource(new PhabricatorProjectDatasource()))
323 ->appendControl(
324 id(new AphrontFormTokenizerControl())
325 ->setLabel(pht('Subscribers'))
326 ->setName('cc')
327 ->setValue($v_cc)
328 ->setViewer($viewer)
329 ->setDatasource(new PhabricatorMetaMTAMailableDatasource()))
330 ->appendChild(
331 id(new AphrontFormPolicyControl())
332 ->setViewer($viewer)
333 ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
334 ->setPolicyObject($mock)
335 ->setPolicies($policies)
336 ->setSpacePHID($v_space)
337 ->setName('can_view'))
338 ->appendChild(
339 id(new AphrontFormPolicyControl())
340 ->setViewer($viewer)
341 ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
342 ->setPolicyObject($mock)
343 ->setPolicies($policies)
344 ->setName('can_edit'))
345 ->appendChild(
346 id(new AphrontFormMarkupControl())
347 ->setValue($list_control))
348 ->appendChild(
349 id(new AphrontFormMarkupControl())
350 ->setValue($drop_control)
351 ->setError($e_images))
352 ->appendChild($submit);
353
354 $form_box = id(new PHUIObjectBoxView())
355 ->setHeaderText($title)
356 ->setFormErrors($errors)
357 ->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
358 ->setForm($form);
359
360 $crumbs = $this->buildApplicationCrumbs();
361 if (!$is_new) {
362 $crumbs->addTextCrumb($mock->getMonogram(), '/'.$mock->getMonogram());
363 }
364 $crumbs->addTextCrumb($title);
365 $crumbs->setBorder(true);
366
367 $view = id(new PHUITwoColumnView())
368 ->setFooter($form_box);
369
370 return $this->newPage()
371 ->setTitle($title)
372 ->setCrumbs($crumbs)
373 ->addQuicksandConfig(
374 array('mockEditConfig' => true))
375 ->appendChild($view);
376 }
377
378}