Correct Aphlict websocket URI construction after PHP8 compatibility changes
[phabricator.git] / src / applications / dashboard / controller / panel / PhabricatorDashboardPanelTabsController.php
blob2fba97db35a4cdb862d5f4c6df48e1448ce20cc7
1 <?php
3 final class PhabricatorDashboardPanelTabsController
4 extends PhabricatorDashboardController {
6 private $contextObject;
8 private function setContextObject($context_object) {
9 $this->contextObject = $context_object;
10 return $this;
13 private function getContextObject() {
14 return $this->contextObject;
17 public function handleRequest(AphrontRequest $request) {
18 $viewer = $this->getViewer();
20 $panel = id(new PhabricatorDashboardPanelQuery())
21 ->setViewer($viewer)
22 ->withIDs(array($request->getURIData('id')))
23 ->requireCapabilities(
24 array(
25 PhabricatorPolicyCapability::CAN_VIEW,
26 PhabricatorPolicyCapability::CAN_EDIT,
28 ->executeOne();
29 if (!$panel) {
30 return new Aphront404Response();
33 $tabs_type = id(new PhabricatorDashboardTabsPanelType())
34 ->getPanelTypeKey();
36 // This controller may only be used to edit tab panels.
37 $panel_type = $panel->getPanelType();
38 if ($panel_type !== $tabs_type) {
39 return new Aphront404Response();
42 $op = $request->getURIData('op');
43 $after = $request->getStr('after');
44 if (!phutil_nonempty_string($after)) {
45 $after = null;
48 $target = $request->getStr('target');
49 if (!phutil_nonempty_string($target)) {
50 $target = null;
53 $impl = $panel->getImplementation();
54 $config = $impl->getPanelConfiguration($panel);
56 $cancel_uri = $panel->getURI();
58 if ($after !== null) {
59 $found = false;
60 foreach ($config as $key => $spec) {
61 if ((string)$key === $after) {
62 $found = true;
63 break;
67 if (!$found) {
68 return $this->newDialog()
69 ->setTitle(pht('Adjacent Tab Not Found'))
70 ->appendParagraph(
71 pht(
72 'Adjacent tab ("%s") was not found on this panel. It may have '.
73 'been removed.',
74 $after))
75 ->addCancelButton($cancel_uri);
79 if ($target !== null) {
80 $found = false;
81 foreach ($config as $key => $spec) {
82 if ((string)$key === $target) {
83 $found = true;
84 break;
88 if (!$found) {
89 return $this->newDialog()
90 ->setTitle(pht('Target Tab Not Found'))
91 ->appendParagraph(
92 pht(
93 'Target tab ("%s") was not found on this panel. It may have '.
94 'been removed.',
95 $target))
96 ->addCancelButton($cancel_uri);
100 // Tab panels may be edited from the panel page, or from the context of
101 // a dashboard. If we're editing from a dashboard, we want to redirect
102 // back to the dashboard after making changes.
104 $context_phid = $request->getStr('contextPHID');
105 $context = null;
106 if (phutil_nonempty_string($context_phid)) {
107 $context = id(new PhabricatorObjectQuery())
108 ->setViewer($viewer)
109 ->withPHIDs(array($context_phid))
110 ->executeOne();
111 if (!$context) {
112 return new Aphront404Response();
115 switch (phid_get_type($context_phid)) {
116 case PhabricatorDashboardDashboardPHIDType::TYPECONST:
117 $cancel_uri = $context->getURI();
118 break;
119 case PhabricatorDashboardPanelPHIDType::TYPECONST:
120 $cancel_uri = $context->getURI();
121 break;
122 default:
123 return $this->newDialog()
124 ->setTitle(pht('Context Object Unsupported'))
125 ->appendParagraph(
126 pht(
127 'Context object ("%s") has unsupported type. Panels should '.
128 'be rendered from the context of a dashboard or another '.
129 'panel.',
130 $context_phid))
131 ->addCancelButton($cancel_uri);
134 $this->setContextObject($context);
137 switch ($op) {
138 case 'add':
139 return $this->handleAddOperation($panel, $after, $cancel_uri);
140 case 'remove':
141 return $this->handleRemoveOperation($panel, $target, $cancel_uri);
142 case 'move':
143 return $this->handleMoveOperation($panel, $target, $after, $cancel_uri);
144 case 'rename':
145 return $this->handleRenameOperation($panel, $target, $cancel_uri);
149 private function handleAddOperation(
150 PhabricatorDashboardPanel $panel,
151 $after,
152 $cancel_uri) {
153 $request = $this->getRequest();
154 $viewer = $this->getViewer();
156 $panel_phid = null;
157 $errors = array();
158 if ($request->isFormPost()) {
159 $panel_phid = $request->getArr('panelPHID');
160 $panel_phid = head($panel_phid);
162 $add_panel = id(new PhabricatorDashboardPanelQuery())
163 ->setViewer($viewer)
164 ->withPHIDs(array($panel_phid))
165 ->executeOne();
166 if (!$add_panel) {
167 $errors[] = pht('You must select a valid panel.');
170 if (!$errors) {
171 $add_panel_config = array(
172 'name' => null,
173 'panelID' => $add_panel->getID(),
175 $add_panel_key = Filesystem::readRandomCharacters(12);
177 $impl = $panel->getImplementation();
178 $old_config = $impl->getPanelConfiguration($panel);
179 $new_config = array();
180 if ($after === null) {
181 $new_config = $old_config;
182 $new_config[] = $add_panel_config;
183 } else {
184 foreach ($old_config as $key => $value) {
185 $new_config[$key] = $value;
186 if ((string)$key === $after) {
187 $new_config[$add_panel_key] = $add_panel_config;
192 $xactions = array();
194 $xactions[] = $panel->getApplicationTransactionTemplate()
195 ->setTransactionType(
196 PhabricatorDashboardTabsPanelTabsTransaction::TRANSACTIONTYPE)
197 ->setNewValue($new_config);
199 $editor = id(new PhabricatorDashboardPanelTransactionEditor())
200 ->setContentSourceFromRequest($request)
201 ->setActor($viewer)
202 ->setContinueOnNoEffect(true)
203 ->setContinueOnMissingFields(true);
205 $editor->applyTransactions($panel, $xactions);
207 return id(new AphrontRedirectResponse())->setURI($cancel_uri);
211 if ($panel_phid) {
212 $v_panel = array($panel_phid);
213 } else {
214 $v_panel = array();
217 $form = id(new AphrontFormView())
218 ->setViewer($viewer)
219 ->appendControl(
220 id(new AphrontFormTokenizerControl())
221 ->setDatasource(new PhabricatorDashboardPanelDatasource())
222 ->setLimit(1)
223 ->setName('panelPHID')
224 ->setLabel(pht('Panel'))
225 ->setValue($v_panel));
227 return $this->newEditDialog()
228 ->setTitle(pht('Choose Dashboard Panel'))
229 ->setErrors($errors)
230 ->addHiddenInput('after', $after)
231 ->appendForm($form)
232 ->addCancelButton($cancel_uri)
233 ->addSubmitButton(pht('Add Panel'));
236 private function handleRemoveOperation(
237 PhabricatorDashboardPanel $panel,
238 $target,
239 $cancel_uri) {
240 $request = $this->getRequest();
241 $viewer = $this->getViewer();
243 $panel_phid = null;
244 $errors = array();
245 if ($request->isFormPost()) {
246 $impl = $panel->getImplementation();
247 $old_config = $impl->getPanelConfiguration($panel);
249 $new_config = $this->removePanel($old_config, $target);
250 $this->writePanelConfig($panel, $new_config);
252 return id(new AphrontRedirectResponse())->setURI($cancel_uri);
255 return $this->newEditDialog()
256 ->setTitle(pht('Remove tab?'))
257 ->addHiddenInput('target', $target)
258 ->appendParagraph(pht('Really remove this tab?'))
259 ->addCancelButton($cancel_uri)
260 ->addSubmitButton(pht('Remove Tab'));
263 private function handleRenameOperation(
264 PhabricatorDashboardPanel $panel,
265 $target,
266 $cancel_uri) {
267 $request = $this->getRequest();
268 $viewer = $this->getViewer();
270 $impl = $panel->getImplementation();
271 $old_config = $impl->getPanelConfiguration($panel);
273 $spec = $old_config[$target];
274 $name = idx($spec, 'name');
276 if ($request->isFormPost()) {
277 $name = $request->getStr('name');
279 $new_config = $this->renamePanel($old_config, $target, $name);
280 $this->writePanelConfig($panel, $new_config);
282 return id(new AphrontRedirectResponse())->setURI($cancel_uri);
285 $form = id(new AphrontFormView())
286 ->setViewer($viewer)
287 ->appendControl(
288 id(new AphrontFormTextControl())
289 ->setValue($name)
290 ->setName('name')
291 ->setLabel(pht('Tab Name')));
293 return $this->newEditDialog()
294 ->setTitle(pht('Rename Panel'))
295 ->addHiddenInput('target', $target)
296 ->appendForm($form)
297 ->addCancelButton($cancel_uri)
298 ->addSubmitButton(pht('Rename Tab'));
301 private function handleMoveOperation(
302 PhabricatorDashboardPanel $panel,
303 $target,
304 $after,
305 $cancel_uri) {
306 $request = $this->getRequest();
307 $viewer = $this->getViewer();
309 $move = $request->getStr('move');
311 $impl = $panel->getImplementation();
312 $old_config = $impl->getPanelConfiguration($panel);
314 $is_next = ($move === 'next');
315 if ($target === $after) {
316 return $this->newDialog()
317 ->setTitle(pht('Impossible!'))
318 ->appendParagraph(
319 pht(
320 'You can not move a tab relative to itself.'))
321 ->addCancelButton($cancel_uri);
322 } else if ($is_next && ((string)last_key($old_config) === $target)) {
323 return $this->newDialog()
324 ->setTitle(pht('Impossible!'))
325 ->appendParagraph(
326 pht(
327 'This is already the last tab. It can not move any farther to '.
328 'the right.'))
329 ->addCancelButton($cancel_uri);
330 } else if ((string)head_key($old_config) === $target) {
331 return $this->newDialog()
332 ->setTitle(pht('Impossible!'))
333 ->appendParagraph(
334 pht(
335 'This is already the first tab. It can not move any farther to '.
336 'the left.'))
337 ->addCancelButton($cancel_uri);
340 if ($request->hasCSRF()) {
341 $new_config = array();
342 foreach ($old_config as $old_key => $old_spec) {
343 $old_key = (string)$old_key;
345 $is_after = ($old_key === $after);
347 if (!$is_after) {
348 if ($old_key === $target) {
349 continue;
353 if ($is_after && !$is_next) {
354 $new_config[$target] = $old_config[$target];
357 $new_config[$old_key] = $old_spec;
359 if ($is_after && $is_next) {
360 $new_config[$target] = $old_config[$target];
364 $this->writePanelConfig($panel, $new_config);
366 return id(new AphrontRedirectResponse())->setURI($cancel_uri);
369 if ($is_next) {
370 $prompt = pht('Move this tab to the right?');
371 } else {
372 $prompt = pht('Move this tab to the left?');
375 return $this->newEditDialog()
376 ->setTitle(pht('Move Tab'))
377 ->addHiddenInput('target', $target)
378 ->addHiddenInput('after', $after)
379 ->addHiddenInput('move', $move)
380 ->appendParagraph($prompt)
381 ->addCancelButton($cancel_uri)
382 ->addSubmitButton(pht('Move Tab'));
385 private function writePanelConfig(
386 PhabricatorDashboardPanel $panel,
387 array $config) {
388 $request = $this->getRequest();
389 $viewer = $this->getViewer();
391 $xactions = array();
393 $xactions[] = $panel->getApplicationTransactionTemplate()
394 ->setTransactionType(
395 PhabricatorDashboardTabsPanelTabsTransaction::TRANSACTIONTYPE)
396 ->setNewValue($config);
398 $editor = id(new PhabricatorDashboardPanelTransactionEditor())
399 ->setContentSourceFromRequest($request)
400 ->setActor($viewer)
401 ->setContinueOnNoEffect(true)
402 ->setContinueOnMissingFields(true);
404 return $editor->applyTransactions($panel, $xactions);
407 private function removePanel(array $config, $target) {
408 $result = array();
410 foreach ($config as $key => $panel_spec) {
411 if ((string)$key === $target) {
412 continue;
414 $result[$key] = $panel_spec;
417 return $result;
420 private function renamePanel(array $config, $target, $name) {
421 $config[$target]['name'] = $name;
422 return $config;
425 protected function newEditDialog() {
426 $dialog = $this->newDialog()
427 ->setWidth(AphrontDialogView::WIDTH_FORM);
429 $context = $this->getContextObject();
430 if ($context) {
431 $dialog->addHiddenInput('contextPHID', $context->getPHID());
434 return $dialog;