3 final class PhabricatorDashboardPanelTabsController
4 extends PhabricatorDashboardController
{
6 private $contextObject;
8 private function setContextObject($context_object) {
9 $this->contextObject
= $context_object;
13 private function getContextObject() {
14 return $this->contextObject
;
17 public function handleRequest(AphrontRequest
$request) {
18 $viewer = $this->getViewer();
20 $panel = id(new PhabricatorDashboardPanelQuery())
22 ->withIDs(array($request->getURIData('id')))
23 ->requireCapabilities(
25 PhabricatorPolicyCapability
::CAN_VIEW
,
26 PhabricatorPolicyCapability
::CAN_EDIT
,
30 return new Aphront404Response();
33 $tabs_type = id(new PhabricatorDashboardTabsPanelType())
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)) {
48 $target = $request->getStr('target');
49 if (!phutil_nonempty_string($target)) {
53 $impl = $panel->getImplementation();
54 $config = $impl->getPanelConfiguration($panel);
56 $cancel_uri = $panel->getURI();
58 if ($after !== null) {
60 foreach ($config as $key => $spec) {
61 if ((string)$key === $after) {
68 return $this->newDialog()
69 ->setTitle(pht('Adjacent Tab Not Found'))
72 'Adjacent tab ("%s") was not found on this panel. It may have '.
75 ->addCancelButton($cancel_uri);
79 if ($target !== null) {
81 foreach ($config as $key => $spec) {
82 if ((string)$key === $target) {
89 return $this->newDialog()
90 ->setTitle(pht('Target Tab Not Found'))
93 'Target tab ("%s") was not found on this panel. It may have '.
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');
106 if (phutil_nonempty_string($context_phid)) {
107 $context = id(new PhabricatorObjectQuery())
109 ->withPHIDs(array($context_phid))
112 return new Aphront404Response();
115 switch (phid_get_type($context_phid)) {
116 case PhabricatorDashboardDashboardPHIDType
::TYPECONST
:
117 $cancel_uri = $context->getURI();
119 case PhabricatorDashboardPanelPHIDType
::TYPECONST
:
120 $cancel_uri = $context->getURI();
123 return $this->newDialog()
124 ->setTitle(pht('Context Object Unsupported'))
127 'Context object ("%s") has unsupported type. Panels should '.
128 'be rendered from the context of a dashboard or another '.
131 ->addCancelButton($cancel_uri);
134 $this->setContextObject($context);
139 return $this->handleAddOperation($panel, $after, $cancel_uri);
141 return $this->handleRemoveOperation($panel, $target, $cancel_uri);
143 return $this->handleMoveOperation($panel, $target, $after, $cancel_uri);
145 return $this->handleRenameOperation($panel, $target, $cancel_uri);
149 private function handleAddOperation(
150 PhabricatorDashboardPanel
$panel,
153 $request = $this->getRequest();
154 $viewer = $this->getViewer();
158 if ($request->isFormPost()) {
159 $panel_phid = $request->getArr('panelPHID');
160 $panel_phid = head($panel_phid);
162 $add_panel = id(new PhabricatorDashboardPanelQuery())
164 ->withPHIDs(array($panel_phid))
167 $errors[] = pht('You must select a valid panel.');
171 $add_panel_config = array(
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;
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;
194 $xactions[] = $panel->getApplicationTransactionTemplate()
195 ->setTransactionType(
196 PhabricatorDashboardTabsPanelTabsTransaction
::TRANSACTIONTYPE
)
197 ->setNewValue($new_config);
199 $editor = id(new PhabricatorDashboardPanelTransactionEditor())
200 ->setContentSourceFromRequest($request)
202 ->setContinueOnNoEffect(true)
203 ->setContinueOnMissingFields(true);
205 $editor->applyTransactions($panel, $xactions);
207 return id(new AphrontRedirectResponse())->setURI($cancel_uri);
212 $v_panel = array($panel_phid);
217 $form = id(new AphrontFormView())
220 id(new AphrontFormTokenizerControl())
221 ->setDatasource(new PhabricatorDashboardPanelDatasource())
223 ->setName('panelPHID')
224 ->setLabel(pht('Panel'))
225 ->setValue($v_panel));
227 return $this->newEditDialog()
228 ->setTitle(pht('Choose Dashboard Panel'))
230 ->addHiddenInput('after', $after)
232 ->addCancelButton($cancel_uri)
233 ->addSubmitButton(pht('Add Panel'));
236 private function handleRemoveOperation(
237 PhabricatorDashboardPanel
$panel,
240 $request = $this->getRequest();
241 $viewer = $this->getViewer();
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,
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())
288 id(new AphrontFormTextControl())
291 ->setLabel(pht('Tab Name')));
293 return $this->newEditDialog()
294 ->setTitle(pht('Rename Panel'))
295 ->addHiddenInput('target', $target)
297 ->addCancelButton($cancel_uri)
298 ->addSubmitButton(pht('Rename Tab'));
301 private function handleMoveOperation(
302 PhabricatorDashboardPanel
$panel,
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!'))
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!'))
327 'This is already the last tab. It can not move any farther to '.
329 ->addCancelButton($cancel_uri);
330 } else if ((string)head_key($old_config) === $target) {
331 return $this->newDialog()
332 ->setTitle(pht('Impossible!'))
335 'This is already the first tab. It can not move any farther to '.
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);
348 if ($old_key === $target) {
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);
370 $prompt = pht('Move this tab to the right?');
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,
388 $request = $this->getRequest();
389 $viewer = $this->getViewer();
393 $xactions[] = $panel->getApplicationTransactionTemplate()
394 ->setTransactionType(
395 PhabricatorDashboardTabsPanelTabsTransaction
::TRANSACTIONTYPE
)
396 ->setNewValue($config);
398 $editor = id(new PhabricatorDashboardPanelTransactionEditor())
399 ->setContentSourceFromRequest($request)
401 ->setContinueOnNoEffect(true)
402 ->setContinueOnMissingFields(true);
404 return $editor->applyTransactions($panel, $xactions);
407 private function removePanel(array $config, $target) {
410 foreach ($config as $key => $panel_spec) {
411 if ((string)$key === $target) {
414 $result[$key] = $panel_spec;
420 private function renamePanel(array $config, $target, $name) {
421 $config[$target]['name'] = $name;
425 protected function newEditDialog() {
426 $dialog = $this->newDialog()
427 ->setWidth(AphrontDialogView
::WIDTH_FORM
);
429 $context = $this->getContextObject();
431 $dialog->addHiddenInput('contextPHID', $context->getPHID());