@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
fork

Configure Feed

Select the types of activity you want to include in your feed.

When editing a tab panel from a dashboard, redirect back to the dashboard

Summary:
Depends on D20396. Ref T13272. Currently, using the dropdowns to edit a tab panel from a dashboard redirects you to the tab panel page.

Instead, redirect back to the context page (usually, a dashboard -- but theoretically a containing tab panel, since you can put tab panels inside tab panels).

Also, fix some JS issues with non-integer panel keys. I've moved panel keys from "0, 1, 2, ..." to having arbitrary keys to make some operations less flimsy/error-prone, but this needs some JS tweaks.

Test Plan: Edited a tab panel from a dashboard, got sent sensibly back to the dashboard.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13272

Differential Revision: https://secure.phabricator.com/D20397

+122 -38
+8 -8
resources/celerity/map.php
··· 10 10 'conpherence.pkg.css' => '3c8a0668', 11 11 'conpherence.pkg.js' => '020aebcf', 12 12 'core.pkg.css' => '9d654dff', 13 - 'core.pkg.js' => '350acda5', 13 + 'core.pkg.js' => '794952ae', 14 14 'differential.pkg.css' => '8d8360fb', 15 15 'differential.pkg.js' => '67e02996', 16 16 'diffusion.pkg.css' => '42c75c37', ··· 374 374 'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '09ecf50c', 375 375 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '076bd092', 376 376 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '1e413dc9', 377 - 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => '8d4490a2', 377 + 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => '0116d3e8', 378 378 'rsrc/js/application/diff/DiffChangeset.js' => 'd0a85a85', 379 379 'rsrc/js/application/diff/DiffChangesetList.js' => '04023d82', 380 380 'rsrc/js/application/diff/DiffInline.js' => 'a4a14a94', ··· 597 597 'javelin-behavior-dashboard-async-panel' => '09ecf50c', 598 598 'javelin-behavior-dashboard-move-panels' => '076bd092', 599 599 'javelin-behavior-dashboard-query-panel-select' => '1e413dc9', 600 - 'javelin-behavior-dashboard-tab-panel' => '8d4490a2', 600 + 'javelin-behavior-dashboard-tab-panel' => '0116d3e8', 601 601 'javelin-behavior-day-view' => '727a5a61', 602 602 'javelin-behavior-desktop-notifications-control' => '070679fe', 603 603 'javelin-behavior-detect-timezone' => '78bc5d94', ··· 902 902 'unhandled-exception-css' => '9ecfc00d', 903 903 ), 904 904 'requires' => array( 905 + '0116d3e8' => array( 906 + 'javelin-behavior', 907 + 'javelin-dom', 908 + 'javelin-stratcom', 909 + ), 905 910 '01384686' => array( 906 911 'javelin-behavior', 907 912 'javelin-uri', ··· 1643 1648 'phabricator-title', 1644 1649 'phabricator-shaped-request', 1645 1650 'conpherence-thread-manager', 1646 - ), 1647 - '8d4490a2' => array( 1648 - 'javelin-behavior', 1649 - 'javelin-dom', 1650 - 'javelin-stratcom', 1651 1651 ), 1652 1652 '8e0aa661' => array( 1653 1653 'javelin-install',
+63 -4
src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php
··· 3 3 final class PhabricatorDashboardPanelTabsController 4 4 extends PhabricatorDashboardController { 5 5 6 + private $contextObject; 7 + 8 + private function setContextObject($context_object) { 9 + $this->contextObject = $context_object; 10 + return $this; 11 + } 12 + 13 + private function getContextObject() { 14 + return $this->contextObject; 15 + } 16 + 6 17 public function handleRequest(AphrontRequest $request) { 7 18 $viewer = $this->getViewer(); 8 19 ··· 86 97 } 87 98 } 88 99 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. 103 + 104 + $context_phid = $request->getStr('contextPHID'); 105 + $context = null; 106 + if (strlen($context_phid)) { 107 + $context = id(new PhabricatorObjectQuery()) 108 + ->setViewer($viewer) 109 + ->withPHIDs(array($context_phid)) 110 + ->executeOne(); 111 + if (!$context) { 112 + return new Aphront404Response(); 113 + } 114 + 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); 132 + } 133 + 134 + $this->setContextObject($context); 135 + } 136 + 89 137 switch ($op) { 90 138 case 'add': 91 139 return $this->handleAddOperation($panel, $after, $cancel_uri); ··· 176 224 ->setLabel(pht('Panel')) 177 225 ->setValue($v_panel)); 178 226 179 - return $this->newDialog() 227 + return $this->newEditDialog() 180 228 ->setTitle(pht('Choose Dashboard Panel')) 181 229 ->setErrors($errors) 182 - ->setWidth(AphrontDialogView::WIDTH_FORM) 183 230 ->addHiddenInput('after', $after) 184 231 ->appendForm($form) 185 232 ->addCancelButton($cancel_uri) ··· 205 252 return id(new AphrontRedirectResponse())->setURI($cancel_uri); 206 253 } 207 254 208 - return $this->newDialog() 255 + return $this->newEditDialog() 209 256 ->setTitle(pht('Remove tab?')) 210 257 ->addHiddenInput('target', $target) 211 258 ->appendParagraph(pht('Really remove this tab?')) ··· 243 290 ->setName('name') 244 291 ->setLabel(pht('Tab Name'))); 245 292 246 - return $this->newDialog() 293 + return $this->newEditDialog() 247 294 ->setTitle(pht('Rename Panel')) 248 295 ->addHiddenInput('target', $target) 249 296 ->appendForm($form) ··· 290 337 private function renamePanel(array $config, $target, $name) { 291 338 $config[$target]['name'] = $name; 292 339 return $config; 340 + } 341 + 342 + protected function newEditDialog() { 343 + $dialog = $this->newDialog() 344 + ->setWidth(AphrontDialogView::WIDTH_FORM); 345 + 346 + $context = $this->getContextObject(); 347 + if ($context) { 348 + $dialog->addHiddenInput('contextPHID', $context->getPHID()); 349 + } 350 + 351 + return $dialog; 293 352 } 294 353 295 354 }
+2 -8
src/applications/dashboard/controller/panel/PhabricatorDashboardPanelViewController.php
··· 42 42 $rendered_panel = id(new PhabricatorDashboardPanelRenderingEngine()) 43 43 ->setViewer($viewer) 44 44 ->setPanel($panel) 45 + ->setContextObject($panel) 45 46 ->setPanelPHID($panel->getPHID()) 46 47 ->setParentPanelPHIDs(array()) 47 48 ->setEditMode(true) ··· 69 70 $viewer = $this->getViewer(); 70 71 $id = $panel->getID(); 71 72 72 - $button = id(new PHUIButtonView()) 73 - ->setTag('a') 74 - ->setText(pht('View Panel')) 75 - ->setIcon('fa-columns') 76 - ->setHref($this->getApplicationURI("panel/render/{$id}/")); 77 - 78 73 $header = id(new PHUIHeaderView()) 79 74 ->setUser($viewer) 80 75 ->setHeader($panel->getName()) 81 76 ->setPolicyObject($panel) 82 - ->setHeaderIcon('fa-columns') 83 - ->addActionLink($button); 77 + ->setHeaderIcon('fa-window-maximize'); 84 78 85 79 if (!$panel->getIsArchived()) { 86 80 $header->setStatus('fa-check', 'bluegrey', pht('Active'));
+10
src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php
··· 16 16 private $movable = true; 17 17 private $panelHandle; 18 18 private $editMode; 19 + private $contextObject; 20 + 21 + public function setContextObject($object) { 22 + $this->contextObject = $object; 23 + return $this; 24 + } 25 + 26 + public function getContextObject() { 27 + return $this->contextObject; 28 + } 19 29 20 30 public function setDashboardID($id) { 21 31 $this->dashboardID = $id;
+1
src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php
··· 73 73 ->setViewer($viewer) 74 74 ->setDashboardID($dashboard->getID()) 75 75 ->setEnableAsyncRendering(true) 76 + ->setContextObject($dashboard) 76 77 ->setPanelPHID($panel_phid) 77 78 ->setParentPanelPHIDs(array()) 78 79 ->setHeaderMode($h_mode)
+25 -11
src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php
··· 51 51 $is_edit = $engine->isEditMode(); 52 52 $config = $this->getPanelConfiguration($panel); 53 53 54 + $context_object = $engine->getContextObject(); 55 + if (!$context_object) { 56 + $context_object = $panel; 57 + } 58 + 59 + $context_phid = $context_object->getPHID(); 60 + 54 61 $list = id(new PHUIListView()) 55 62 ->setType(PHUIListView::NAVBAR_LIST); 56 63 57 - $node_ids = array(); 58 - foreach ($config as $idx => $tab_spec) { 59 - $node_ids[$idx] = celerity_generate_unique_node_id(); 60 - } 61 - 62 64 $ids = ipull($config, 'panelID'); 63 65 if ($ids) { 64 66 $panels = id(new PhabricatorDashboardPanelQuery()) ··· 72 74 $id = $panel->getID(); 73 75 74 76 $add_uri = urisprintf('/dashboard/panel/tabs/%d/add/', $id); 75 - $add_uri = new PhutilURI($add_uri); 77 + $add_uri = id(new PhutilURI($add_uri)) 78 + ->replaceQueryParam('contextPHID', $context_phid); 76 79 77 80 $remove_uri = urisprintf('/dashboard/panel/tabs/%d/remove/', $id); 78 - $remove_uri = new PhutilURI($remove_uri); 81 + $remove_uri = id(new PhutilURI($remove_uri)) 82 + ->replaceQueryParam('contextPHID', $context_phid); 79 83 80 84 $rename_uri = urisprintf('/dashboard/panel/tabs/%d/rename/', $id); 81 - $rename_uri = new PhutilURI($rename_uri); 85 + $rename_uri = id(new PhutilURI($rename_uri)) 86 + ->replaceQueryParam('contextPHID', $context_phid); 82 87 83 88 $selected = 0; 84 89 ··· 102 107 ->setHref('#') 103 108 ->setSelected($idx == $selected) 104 109 ->addSigil('dashboard-tab-panel-tab') 105 - ->setMetadata(array('idx' => $idx)) 110 + ->setMetadata(array('panelKey' => $idx)) 106 111 ->setName($name); 107 112 108 113 if ($is_edit) { ··· 212 217 // remains selected across page loads. 213 218 214 219 $content = array(); 220 + $panel_list = array(); 215 221 $no_headers = PhabricatorDashboardPanelRenderingEngine::HEADER_MODE_NONE; 216 222 foreach ($config as $idx => $tab_spec) { 217 223 $panel_id = idx($tab_spec, 'panelID'); ··· 221 227 $panel_content = id(new PhabricatorDashboardPanelRenderingEngine()) 222 228 ->setViewer($viewer) 223 229 ->setEnableAsyncRendering(true) 230 + ->setContextObject($context_object) 224 231 ->setParentPanelPHIDs($parent_phids) 225 232 ->setPanel($subpanel) 226 233 ->setPanelPHID($subpanel->getPHID()) ··· 230 237 } else { 231 238 $panel_content = pht('(Invalid Panel)'); 232 239 } 240 + 241 + $content_id = celerity_generate_unique_node_id(); 233 242 234 243 $content[] = phutil_tag( 235 244 'div', 236 245 array( 237 - 'id' => $node_ids[$idx], 246 + 'id' => $content_id, 238 247 'style' => ($idx == $selected) ? null : 'display: none', 239 248 ), 240 249 $panel_content); 250 + 251 + $panel_list[] = array( 252 + 'panelKey' => (string)$idx, 253 + 'panelContentID' => $content_id, 254 + ); 241 255 } 242 256 243 257 if (!$content) { ··· 269 283 array( 270 284 'sigil' => 'dashboard-tab-panel-container', 271 285 'meta' => array( 272 - 'panels' => $node_ids, 286 + 'panels' => $panel_list, 273 287 ), 274 288 ), 275 289 array(
+13 -7
webroot/rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js
··· 18 18 19 19 e.kill(); 20 20 21 - var ii; 22 - var idx = e.getNodeData('dashboard-tab-panel-tab').idx; 21 + var selected_key = e.getNodeData('dashboard-tab-panel-tab').panelKey; 23 22 24 23 var root = e.getNode('dashboard-tab-panel-container'); 25 24 var data = JX.Stratcom.getData(root); 26 25 26 + 27 + var ii; 27 28 // Give the tab the user clicked a selected style, and remove it from 28 29 // the other tabs. 29 30 var tabs = JX.DOM.scry(root, 'li', 'dashboard-tab-panel-tab'); 30 31 for (ii = 0; ii < tabs.length; ii++) { 31 - JX.DOM.alterClass(tabs[ii], 'phui-list-item-selected', (ii == idx)); 32 + var tab = tabs[ii]; 33 + var tab_data = JX.Stratcom.getData(tab); 34 + var is_selected = (tab_data.panelKey === selected_key); 35 + JX.DOM.alterClass(tabs[ii], 'phui-list-item-selected', is_selected); 32 36 } 33 37 34 38 // Switch the visible content to correspond to whatever the user clicked. 35 39 for (ii = 0; ii < data.panels.length; ii++) { 36 - var panel = JX.$(data.panels[ii]); 37 - if (ii == idx) { 38 - JX.DOM.show(panel); 40 + var panel = data.panels[ii]; 41 + var node = JX.$(panel.panelContentID); 42 + 43 + if (panel.panelKey == selected_key) { 44 + JX.DOM.show(node); 39 45 } else { 40 - JX.DOM.hide(panel); 46 + JX.DOM.hide(node); 41 47 } 42 48 } 43 49