Page MenuHomePhabricator

D20408.diff
No OneTemporary

D20408.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -10,7 +10,7 @@
'conpherence.pkg.css' => '3c8a0668',
'conpherence.pkg.js' => '020aebcf',
'core.pkg.css' => '294e365c',
- 'core.pkg.js' => '794952ae',
+ 'core.pkg.js' => '69247edd',
'differential.pkg.css' => '8d8360fb',
'differential.pkg.js' => '67e02996',
'diffusion.pkg.css' => '42c75c37',
@@ -371,7 +371,7 @@
'rsrc/js/application/conpherence/behavior-toggle-widget.js' => '8f959ad0',
'rsrc/js/application/countdown/timer.js' => '6a162524',
'rsrc/js/application/daemon/behavior-bulk-job-reload.js' => '3829a3cf',
- 'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '09ecf50c',
+ 'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => 'a871fe00',
'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '076bd092',
'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '1e413dc9',
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => '0116d3e8',
@@ -594,7 +594,7 @@
'javelin-behavior-conpherence-search' => '91befbcc',
'javelin-behavior-countdown-timer' => '6a162524',
'javelin-behavior-dark-console' => 'f39d968b',
- 'javelin-behavior-dashboard-async-panel' => '09ecf50c',
+ 'javelin-behavior-dashboard-async-panel' => 'a871fe00',
'javelin-behavior-dashboard-move-panels' => '076bd092',
'javelin-behavior-dashboard-query-panel-select' => '1e413dc9',
'javelin-behavior-dashboard-tab-panel' => '0116d3e8',
@@ -982,11 +982,6 @@
'herald-rule-editor',
'javelin-behavior',
),
- '09ecf50c' => array(
- 'javelin-behavior',
- 'javelin-dom',
- 'javelin-workflow',
- ),
'0ad8d31f' => array(
'javelin-behavior',
'javelin-stratcom',
@@ -1794,6 +1789,11 @@
'javelin-install',
'javelin-dom',
),
+ 'a871fe00' => array(
+ 'javelin-behavior',
+ 'javelin-dom',
+ 'javelin-workflow',
+ ),
'a9942052' => array(
'javelin-behavior',
'javelin-dom',
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2906,7 +2906,7 @@
'PhabricatorDarkConsoleTabSetting' => 'applications/settings/setting/PhabricatorDarkConsoleTabSetting.php',
'PhabricatorDarkConsoleVisibleSetting' => 'applications/settings/setting/PhabricatorDarkConsoleVisibleSetting.php',
'PhabricatorDashboard' => 'applications/dashboard/storage/PhabricatorDashboard.php',
- 'PhabricatorDashboardAddPanelController' => 'applications/dashboard/controller/PhabricatorDashboardAddPanelController.php',
+ 'PhabricatorDashboardAdjustController' => 'applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php',
'PhabricatorDashboardApplication' => 'applications/dashboard/application/PhabricatorDashboardApplication.php',
'PhabricatorDashboardApplicationInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardApplicationInstallWorkflow.php',
'PhabricatorDashboardArchiveController' => 'applications/dashboard/controller/dashboard/PhabricatorDashboardArchiveController.php',
@@ -2927,7 +2927,6 @@
'PhabricatorDashboardInstall' => 'applications/dashboard/storage/PhabricatorDashboardInstall.php',
'PhabricatorDashboardInstallController' => 'applications/dashboard/controller/dashboard/PhabricatorDashboardInstallController.php',
'PhabricatorDashboardInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardInstallWorkflow.php',
- 'PhabricatorDashboardLayoutConfig' => 'applications/dashboard/layoutconfig/PhabricatorDashboardLayoutConfig.php',
'PhabricatorDashboardLayoutMode' => 'applications/dashboard/layoutconfig/PhabricatorDashboardLayoutMode.php',
'PhabricatorDashboardLayoutTransaction' => 'applications/dashboard/xaction/dashboard/PhabricatorDashboardLayoutTransaction.php',
'PhabricatorDashboardListController' => 'applications/dashboard/controller/PhabricatorDashboardListController.php',
@@ -2964,6 +2963,7 @@
'PhabricatorDashboardPanelType' => 'applications/dashboard/paneltype/PhabricatorDashboardPanelType.php',
'PhabricatorDashboardPanelUsedByObjectEdgeType' => 'applications/search/edge/PhabricatorDashboardPanelUsedByObjectEdgeType.php',
'PhabricatorDashboardPanelViewController' => 'applications/dashboard/controller/panel/PhabricatorDashboardPanelViewController.php',
+ 'PhabricatorDashboardPanelsTransaction' => 'applications/dashboard/xaction/dashboard/PhabricatorDashboardPanelsTransaction.php',
'PhabricatorDashboardPortal' => 'applications/dashboard/storage/PhabricatorDashboardPortal.php',
'PhabricatorDashboardPortalController' => 'applications/dashboard/controller/portal/PhabricatorDashboardPortalController.php',
'PhabricatorDashboardPortalDatasource' => 'applications/dashboard/typeahead/PhabricatorDashboardPortalDatasource.php',
@@ -2998,7 +2998,6 @@
'PhabricatorDashboardQueryPanelQueryTransaction' => 'applications/dashboard/xaction/panel/PhabricatorDashboardQueryPanelQueryTransaction.php',
'PhabricatorDashboardQueryPanelType' => 'applications/dashboard/paneltype/PhabricatorDashboardQueryPanelType.php',
'PhabricatorDashboardRemarkupRule' => 'applications/dashboard/remarkup/PhabricatorDashboardRemarkupRule.php',
- 'PhabricatorDashboardRemovePanelController' => 'applications/dashboard/controller/PhabricatorDashboardRemovePanelController.php',
'PhabricatorDashboardRenderingEngine' => 'applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php',
'PhabricatorDashboardSchemaSpec' => 'applications/dashboard/storage/PhabricatorDashboardSchemaSpec.php',
'PhabricatorDashboardSearchEngine' => 'applications/dashboard/query/PhabricatorDashboardSearchEngine.php',
@@ -8906,7 +8905,7 @@
'PhabricatorNgramsInterface',
'PhabricatorDashboardPanelContainerInterface',
),
- 'PhabricatorDashboardAddPanelController' => 'PhabricatorDashboardController',
+ 'PhabricatorDashboardAdjustController' => 'PhabricatorDashboardController',
'PhabricatorDashboardApplication' => 'PhabricatorApplication',
'PhabricatorDashboardApplicationInstallWorkflow' => 'PhabricatorDashboardInstallWorkflow',
'PhabricatorDashboardArchiveController' => 'PhabricatorDashboardController',
@@ -8927,7 +8926,6 @@
'PhabricatorDashboardInstall' => 'PhabricatorDashboardDAO',
'PhabricatorDashboardInstallController' => 'PhabricatorDashboardController',
'PhabricatorDashboardInstallWorkflow' => 'Phobject',
- 'PhabricatorDashboardLayoutConfig' => 'Phobject',
'PhabricatorDashboardLayoutMode' => 'Phobject',
'PhabricatorDashboardLayoutTransaction' => 'PhabricatorDashboardTransactionType',
'PhabricatorDashboardListController' => 'PhabricatorDashboardController',
@@ -8971,6 +8969,7 @@
'PhabricatorDashboardPanelType' => 'Phobject',
'PhabricatorDashboardPanelUsedByObjectEdgeType' => 'PhabricatorEdgeType',
'PhabricatorDashboardPanelViewController' => 'PhabricatorDashboardController',
+ 'PhabricatorDashboardPanelsTransaction' => 'PhabricatorDashboardTransactionType',
'PhabricatorDashboardPortal' => array(
'PhabricatorDashboardDAO',
'PhabricatorApplicationTransactionInterface',
@@ -9013,7 +9012,6 @@
'PhabricatorDashboardQueryPanelQueryTransaction' => 'PhabricatorDashboardPanelPropertyTransaction',
'PhabricatorDashboardQueryPanelType' => 'PhabricatorDashboardPanelType',
'PhabricatorDashboardRemarkupRule' => 'PhabricatorObjectRemarkupRule',
- 'PhabricatorDashboardRemovePanelController' => 'PhabricatorDashboardController',
'PhabricatorDashboardRenderingEngine' => 'Phobject',
'PhabricatorDashboardSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'PhabricatorDashboardSearchEngine' => 'PhabricatorApplicationSearchEngine',
diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php
--- a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php
+++ b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php
@@ -389,7 +389,7 @@
* appropriate for one-time checks.
*
* @param PhabricatorUser User whose session needs to be in high security.
- * @param AphrontReqeust Current request.
+ * @param AphrontRequest Current request.
* @param string URI to return the user to if they cancel.
* @return PhabricatorAuthHighSecurityToken Security token.
* @task hisec
@@ -421,7 +421,7 @@
* use @{method:requireHighSecurityToken}.
*
* @param PhabricatorUser User whose session needs to be in high security.
- * @param AphrontReqeust Current request.
+ * @param AphrontRequest Current request.
* @param string URI to return the user to if they cancel.
* @param bool True to jump partial sessions directly into high
* security instead of just upgrading them to full
diff --git a/src/applications/dashboard/application/PhabricatorDashboardApplication.php b/src/applications/dashboard/application/PhabricatorDashboardApplication.php
--- a/src/applications/dashboard/application/PhabricatorDashboardApplication.php
+++ b/src/applications/dashboard/application/PhabricatorDashboardApplication.php
@@ -48,10 +48,9 @@
'(?:(?P<modeKey>[^/]+)/)?)?' =>
'PhabricatorDashboardInstallController',
'console/' => 'PhabricatorDashboardConsoleController',
- 'addpanel/(?P<id>\d+)/' => 'PhabricatorDashboardAddPanelController',
'movepanel/(?P<id>\d+)/' => 'PhabricatorDashboardMovePanelController',
- 'removepanel/(?P<id>\d+)/'
- => 'PhabricatorDashboardRemovePanelController',
+ 'adjust/(?P<op>remove|add)/'
+ => 'PhabricatorDashboardAdjustController',
'panel/' => array(
'install/(?P<engineKey>[^/]+)/(?:(?P<queryKey>[^/]+)/)?' =>
'PhabricatorDashboardQueryPanelInstallController',
diff --git a/src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php b/src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php
deleted file mode 100644
--- a/src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php
+++ /dev/null
@@ -1,103 +0,0 @@
-<?php
-
-final class PhabricatorDashboardAddPanelController
- extends PhabricatorDashboardController {
-
- public function handleRequest(AphrontRequest $request) {
- $viewer = $request->getViewer();
- $id = $request->getURIData('id');
-
- $dashboard = id(new PhabricatorDashboardQuery())
- ->setViewer($viewer)
- ->withIDs(array($id))
- ->requireCapabilities(
- array(
- PhabricatorPolicyCapability::CAN_VIEW,
- PhabricatorPolicyCapability::CAN_EDIT,
- ))
- ->executeOne();
- if (!$dashboard) {
- return new Aphront404Response();
- }
-
- $redirect_uri = $this->getApplicationURI(
- 'arrange/'.$dashboard->getID().'/');
-
- $v_panel = head($request->getArr('panel'));
- $e_panel = true;
- $errors = array();
- if ($request->isFormPost()) {
- if (strlen($v_panel)) {
- $panel = id(new PhabricatorDashboardPanelQuery())
- ->setViewer($viewer)
- ->withIDs(array($v_panel))
- ->executeOne();
- if (!$panel) {
- $errors[] = pht('Not a valid panel.');
- $e_panel = pht('Invalid');
- }
-
- $on_dashboard = $dashboard->getPanels();
- $on_ids = mpull($on_dashboard, null, 'getID');
- if (array_key_exists($v_panel, $on_ids)) {
- $p_name = $panel->getName();
- $errors[] = pht('Panel "%s" already exists on dashboard.', $p_name);
- $e_panel = pht('Invalid');
- }
-
- } else {
- $errors[] = pht('Select a panel to add.');
- $e_panel = pht('Required');
- }
-
- if (!$errors) {
- PhabricatorDashboardTransactionEditor::addPanelToDashboard(
- $viewer,
- PhabricatorContentSource::newFromRequest($request),
- $panel,
- $dashboard,
- $request->getInt('column', 0));
-
- return id(new AphrontRedirectResponse())->setURI($redirect_uri);
- }
- }
-
- $panels = id(new PhabricatorDashboardPanelQuery())
- ->setViewer($viewer)
- ->withArchived(false)
- ->execute();
-
- if (!$panels) {
- return $this->newDialog()
- ->setTitle(pht('No Panels Exist Yet'))
- ->appendParagraph(
- pht(
- 'You have not created any dashboard panels yet, so you can not '.
- 'add an existing panel.'))
- ->appendParagraph(
- pht('Instead, add a new panel.'))
- ->addCancelButton($redirect_uri);
- }
-
- $form = id(new AphrontFormView())
- ->setUser($viewer)
- ->addHiddenInput('column', $request->getInt('column'))
- ->appendRemarkupInstructions(
- pht('Choose a panel to add to this dashboard:'))
- ->appendChild(
- id(new AphrontFormTokenizerControl())
- ->setUser($this->getViewer())
- ->setDatasource(new PhabricatorDashboardPanelDatasource())
- ->setLimit(1)
- ->setName('panel')
- ->setLabel(pht('Panel')));
-
- return $this->newDialog()
- ->setTitle(pht('Add Panel'))
- ->setErrors($errors)
- ->appendChild($form->buildLayoutView())
- ->addCancelButton($redirect_uri)
- ->addSubmitButton(pht('Add Panel'));
- }
-
-}
diff --git a/src/applications/dashboard/controller/PhabricatorDashboardRemovePanelController.php b/src/applications/dashboard/controller/PhabricatorDashboardRemovePanelController.php
deleted file mode 100644
--- a/src/applications/dashboard/controller/PhabricatorDashboardRemovePanelController.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-final class PhabricatorDashboardRemovePanelController
- extends PhabricatorDashboardController {
-
- public function handleRequest(AphrontRequest $request) {
- $viewer = $request->getViewer();
- $id = $request->getURIData('id');
-
- $dashboard = id(new PhabricatorDashboardQuery())
- ->setViewer($viewer)
- ->withIDs(array($id))
- ->requireCapabilities(
- array(
- PhabricatorPolicyCapability::CAN_VIEW,
- PhabricatorPolicyCapability::CAN_EDIT,
- ))
- ->executeOne();
- if (!$dashboard) {
- return new Aphront404Response();
- }
-
- // NOTE: If you can edit a dashboard, you can remove panels from it even
- // if you don't have permission to see them or they aren't valid. We only
- // require that the panel be present on the dashboard.
-
- $v_panel = $request->getStr('panelPHID');
-
- $panel_on_dashboard = false;
- $layout = $dashboard->getLayoutConfigObject();
- $columns = $layout->getPanelLocations();
- foreach ($columns as $column) {
- foreach ($column as $column_panel_phid) {
- if ($column_panel_phid == $v_panel) {
- $panel_on_dashboard = true;
- break;
- }
- }
- }
-
- if (!$panel_on_dashboard) {
- return new Aphront404Response();
- }
-
- $redirect_uri = $dashboard->getURI();
- $layout_config = $dashboard->getLayoutConfigObject();
-
- if ($request->isFormPost()) {
- $xactions = array();
-
- $layout_config->removePanel($v_panel);
- $dashboard->setLayoutConfigFromObject($layout_config);
-
- $editor = id(new PhabricatorDashboardTransactionEditor())
- ->setActor($viewer)
- ->setContentSourceFromRequest($request)
- ->setContinueOnMissingFields(true)
- ->setContinueOnNoEffect(true)
- ->applyTransactions($dashboard, $xactions);
-
- return id(new AphrontRedirectResponse())->setURI($redirect_uri);
- }
-
- $form = id(new AphrontFormView())
- ->setUser($viewer)
- ->addHiddenInput('confirm', true)
- ->addHiddenInput('panelPHID', $v_panel)
- ->appendChild(pht('Are you sure you want to remove this panel?'));
-
- return $this->newDialog()
- ->setTitle(pht('Remove Panel'))
- ->appendChild($form->buildLayoutView())
- ->addCancelButton($redirect_uri)
- ->addSubmitButton(pht('Remove Panel'));
- }
-
-}
diff --git a/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php b/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php
@@ -0,0 +1,202 @@
+<?php
+
+final class PhabricatorDashboardAdjustController
+ extends PhabricatorDashboardController {
+
+ private $contextPHID;
+ private $panelKey;
+ private $columnKey;
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $this->getViewer();
+
+ $context_phid = $request->getStr('contextPHID');
+
+ $dashboard = id(new PhabricatorDashboardQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($context_phid))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$dashboard) {
+ return new Aphront404Response();
+ }
+
+ $this->contextPHID = $context_phid;
+
+ $done_uri = $dashboard->getURI();
+ $ref_list = $dashboard->getPanelRefList();
+
+ $panel_ref = null;
+ $panel_key = $request->getStr('panelKey');
+ if (strlen($panel_key)) {
+ $panel_ref = $ref_list->getPanelRef($panel_key);
+ if (!$panel_ref) {
+ return new Aphront404Response();
+ }
+
+ $this->panelKey = $panel_key;
+ } else {
+ $panel_ref = null;
+ }
+
+ $column_key = $request->getStr('columnKey');
+ if (strlen($column_key)) {
+ $columns = $ref_list->getColumns();
+ if (!isset($columns[$column_key])) {
+ return new Aphront404Response();
+ }
+ $this->columnKey = $column_key;
+ }
+
+ switch ($request->getURIData('op')) {
+ case 'add':
+ return $this->handleAddRequest($dashboard, $done_uri);
+ case 'remove':
+ if (!$panel_ref) {
+ return new Aphront404Response();
+ }
+ return $this->handleRemoveRequest($dashboard, $panel_ref, $done_uri);
+ }
+ }
+
+ private function handleAddRequest(
+ PhabricatorDashboard $dashboard,
+ $done_uri) {
+ $request = $this->getRequest();
+ $viewer = $this->getViewer();
+
+ $errors = array();
+
+ $panel_phid = null;
+ $e_panel = true;
+ if ($request->isFormPost()) {
+ $panel_phid = head($request->getArr('panelPHIDs'));
+
+ if (!$panel_phid) {
+ $errors[] = pht('You must choose a panel to add to the dashboard.');
+ $e_panel = pht('Required');
+ } else {
+ $panel = id(new PhabricatorDashboardPanelQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($panel_phid))
+ ->executeOne();
+ if (!$panel) {
+ $errors[] = pht('You must choose a valid panel.');
+ $e_panel = pht('Invalid');
+ }
+ }
+
+ if (!$errors) {
+ $xactions = array();
+
+ $ref_list = clone $dashboard->getPanelRefList();
+ $ref_list->newPanelRef($panel, $this->columnKey);
+ $new_panels = $ref_list->toDictionary();
+
+ $xactions[] = $dashboard->getApplicationTransactionTemplate()
+ ->setTransactionType(
+ PhabricatorDashboardPanelsTransaction::TRANSACTIONTYPE)
+ ->setNewValue($new_panels);
+
+ $editor = $dashboard->getApplicationTransactionEditor()
+ ->setActor($viewer)
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnNoEffect(true)
+ ->setContinueOnMissingFields(true);
+
+ $editor->applyTransactions($dashboard, $xactions);
+
+ return id(new AphrontRedirectResponse())->setURI($done_uri);
+ }
+ }
+
+ if ($panel_phid) {
+ $panel_phids = array($panel_phid);
+ } else {
+ $panel_phids = array();
+ }
+
+ $form = id(new AphrontFormView())
+ ->setViewer($viewer)
+ ->appendRemarkupInstructions(
+ pht('Choose a panel to add to this dashboard:'))
+ ->appendControl(
+ id(new AphrontFormTokenizerControl())
+ ->setDatasource(new PhabricatorDashboardPanelDatasource())
+ ->setLimit(1)
+ ->setName('panelPHIDs')
+ ->setLabel(pht('Panel'))
+ ->setError($e_panel)
+ ->setValue($panel_phids));
+
+ return $this->newEditDialog()
+ ->setTitle(pht('Add Panel'))
+ ->setWidth(AphrontDialogView::WIDTH_FORM)
+ ->setErrors($errors)
+ ->appendForm($form)
+ ->addCancelButton($done_uri)
+ ->addSubmitButton(pht('Add Panel'));
+ }
+
+ private function handleRemoveRequest(
+ PhabricatorDashboard $dashboard,
+ PhabricatorDashboardPanelRef $panel_ref,
+ $done_uri) {
+ $request = $this->getRequest();
+ $viewer = $this->getViewer();
+
+ // NOTE: If you can edit a dashboard, you can remove panels from it even
+ // if you don't have permission to see them or they aren't valid. We only
+ // require that the panel be present on the dashboard.
+
+ if ($request->isFormPost()) {
+ $xactions = array();
+
+ $ref_list = clone $dashboard->getPanelRefList();
+ $ref_list->removePanelRef($panel_ref);
+ $new_panels = $ref_list->toDictionary();
+
+ $xactions[] = $dashboard->getApplicationTransactionTemplate()
+ ->setTransactionType(
+ PhabricatorDashboardPanelsTransaction::TRANSACTIONTYPE)
+ ->setNewValue($new_panels);
+
+ $editor = $dashboard->getApplicationTransactionEditor()
+ ->setActor($viewer)
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnNoEffect(true)
+ ->setContinueOnMissingFields(true);
+
+ $editor->applyTransactions($dashboard, $xactions);
+
+ return id(new AphrontRedirectResponse())->setURI($done_uri);
+ }
+
+ $panel_phid = $panel_ref->getPanelPHID();
+ $handles = $viewer->loadHandles(array($panel_phid));
+ $handle = $handles[$panel_phid];
+
+ $message = pht(
+ 'Remove panel %s from dashboard %s?',
+ phutil_tag('strong', array(), $handle->getFullName()),
+ phutil_tag('strong', array(), $dashboard->getName()));
+
+ return $this->newEditDialog()
+ ->setTitle(pht('Remove Dashboard Panel'))
+ ->appendParagraph($message)
+ ->addCancelButton($done_uri)
+ ->addSubmitButton(pht('Remove Panel'));
+ }
+
+ private function newEditDialog() {
+ return $this->newDialog()
+ ->addHiddenInput('contextPHID', $this->contextPHID)
+ ->addHiddenInput('panelKey', $this->panelKey)
+ ->addHiddenInput('columnKey', $this->columnKey);
+ }
+
+}
diff --git a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelEditController.php b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelEditController.php
--- a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelEditController.php
+++ b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelEditController.php
@@ -9,37 +9,43 @@
$engine = id(new PhabricatorDashboardPanelEditEngine())
->setController($this);
- // We can create or edit a panel in the context of a dashboard. If we
- // started on a dashboard, we want to return to that dashboard when we're
- // done editing.
- $dashboard_id = $request->getStr('dashboardID');
- if (strlen($dashboard_id)) {
- $dashboard = id(new PhabricatorDashboardQuery())
+ // We can create or edit a panel in the context of a dashboard or
+ // container panel, like a tab panel. If we started this flow on some
+ // container object, we want to return to that container when we're done
+ // editing.
+
+ $context_phid = $request->getStr('contextPHID');
+ if (strlen($context_phid)) {
+ $context = id(new PhabricatorObjectQuery())
->setViewer($viewer)
- ->withIDs(array($dashboard_id))
+ ->withPHIDs(array($context_phid))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
- if (!$dashboard) {
+ if (!$context) {
+ return new Aphront404Response();
+ }
+
+ if (!($context instanceof PhabricatorDashboardPanelContainerInterface)) {
return new Aphront404Response();
}
$engine
- ->setDashboard($dashboard)
- ->addContextParameter('dashboardID', $dashboard_id);
+ ->setContextObject($context)
+ ->addContextParameter('contextPHID', $context_phid);
} else {
- $dashboard = null;
+ $context = null;
}
$id = $request->getURIData('id');
if (!$id) {
- $column_id = $request->getStr('columnID');
+ $column_key = $request->getStr('columnKey');
- if ($dashboard) {
- $cancel_uri = $dashboard->getURI();
+ if ($context) {
+ $cancel_uri = $context->getURI();
} else {
$cancel_uri = $this->getApplicationURI('panel/');
}
@@ -52,9 +58,9 @@
$engine
->addContextParameter('panelType', $panel_type)
- ->addContextParameter('columnID', $column_id)
+ ->addContextParameter('columnKey', $column_key)
->setPanelType($panel_type)
- ->setColumnID($column_id);
+ ->setColumnKey($column_key);
}
return $engine->buildResponse();
diff --git a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelRenderController.php b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelRenderController.php
--- a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelRenderController.php
+++ b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelRenderController.php
@@ -31,14 +31,27 @@
$parent_phids = array();
}
- $rendered_panel = id(new PhabricatorDashboardPanelRenderingEngine())
+ $engine = id(new PhabricatorDashboardPanelRenderingEngine())
->setViewer($viewer)
->setPanel($panel)
->setPanelPHID($panel->getPHID())
->setParentPanelPHIDs($parent_phids)
->setHeaderMode($request->getStr('headerMode'))
- ->setDashboardID($request->getInt('dashboardID'))
- ->renderPanel();
+ ->setPanelKey($request->getStr('panelKey'));
+
+ $context_phid = $request->getStr('contextPHID');
+ if ($context_phid) {
+ $context = id(new PhabricatorObjectQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($context_phid))
+ ->executeOne();
+ if (!$context) {
+ return new Aphront404Response();
+ }
+ $engine->setContextObject($context);
+ }
+
+ $rendered_panel = $engine->renderPanel();
if ($request->isAjax()) {
return id(new AphrontAjaxResponse())
diff --git a/src/applications/dashboard/editor/PhabricatorDashboardPanelEditEngine.php b/src/applications/dashboard/editor/PhabricatorDashboardPanelEditEngine.php
--- a/src/applications/dashboard/editor/PhabricatorDashboardPanelEditEngine.php
+++ b/src/applications/dashboard/editor/PhabricatorDashboardPanelEditEngine.php
@@ -6,8 +6,8 @@
const ENGINECONST = 'dashboard.panel';
private $panelType;
- private $dashboard;
- private $columnID;
+ private $contextObject;
+ private $columnKey;
public function setPanelType($panel_type) {
$this->panelType = $panel_type;
@@ -18,22 +18,22 @@
return $this->panelType;
}
- public function setDashboard(PhabricatorDashboard $dashboard) {
- $this->dashboard = $dashboard;
+ public function setContextObject($context) {
+ $this->contextObject = $context;
return $this;
}
- public function getDashboard() {
- return $this->dashboard;
+ public function getContextObject() {
+ return $this->contextObject;
}
- public function setColumnID($column_id) {
- $this->columnID = $column_id;
+ public function setColumnKey($column_key) {
+ $this->columnKey = $column_key;
return $this;
}
- public function getColumnID() {
- return $this->columnID;
+ public function getColumnKey() {
+ return $this->columnKey;
}
public function isEngineConfigurable() {
@@ -84,27 +84,27 @@
}
protected function getObjectCreateCancelURI($object) {
- $dashboard = $this->getDashboard();
- if ($dashboard) {
- return $dashboard->getURI();
+ $context = $this->getContextObject();
+ if ($context) {
+ return $context->getURI();
}
return parent::getObjectCreateCancelURI($object);
}
public function getEffectiveObjectEditDoneURI($object) {
- $dashboard = $this->getDashboard();
- if ($dashboard) {
- return $dashboard->getURI();
+ $context = $this->getContextObject();
+ if ($context) {
+ return $context->getURI();
}
return parent::getEffectiveObjectEditDoneURI($object);
}
protected function getObjectEditCancelURI($object) {
- $dashboard = $this->getDashboard();
- if ($dashboard) {
- return $dashboard->getURI();
+ $context = $this->getContextObject();
+ if ($context) {
+ return $context->getURI();
}
return parent::getObjectEditCancelURI($object);
@@ -131,18 +131,34 @@
}
protected function didApplyTransactions($object, array $xactions) {
- $dashboard = $this->getDashboard();
- if ($dashboard) {
+ $context = $this->getContextObject();
+
+ if ($context instanceof PhabricatorDashboard) {
$viewer = $this->getViewer();
$controller = $this->getController();
$request = $controller->getRequest();
- PhabricatorDashboardTransactionEditor::addPanelToDashboard(
- $viewer,
- PhabricatorContentSource::newFromRequest($request),
- $object,
- $dashboard,
- (int)$this->getColumnID());
+ $dashboard = $context;
+
+ $xactions = array();
+
+ $ref_list = clone $dashboard->getPanelRefList();
+
+ $ref_list->newPanelRef($object, $this->getColumnKey());
+ $new_panels = $ref_list->toDictionary();
+
+ $xactions[] = $dashboard->getApplicationTransactionTemplate()
+ ->setTransactionType(
+ PhabricatorDashboardPanelsTransaction::TRANSACTIONTYPE)
+ ->setNewValue($new_panels);
+
+ $editor = $dashboard->getApplicationTransactionEditor()
+ ->setActor($viewer)
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnNoEffect(true)
+ ->setContinueOnMissingFields(true);
+
+ $editor->applyTransactions($dashboard, $xactions);
}
}
diff --git a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php
--- a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php
+++ b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php
@@ -12,11 +12,11 @@
private $enableAsyncRendering;
private $parentPanelPHIDs;
private $headerMode = self::HEADER_MODE_NORMAL;
- private $dashboardID;
private $movable = true;
private $panelHandle;
private $editMode;
private $contextObject;
+ private $panelKey;
public function setContextObject($object) {
$this->contextObject = $object;
@@ -27,13 +27,13 @@
return $this->contextObject;
}
- public function setDashboardID($id) {
- $this->dashboardID = $id;
+ public function setPanelKey($panel_key) {
+ $this->panelKey = $panel_key;
return $this;
}
- public function getDashboardID() {
- return $this->dashboardID;
+ public function getPanelKey() {
+ return $this->panelKey;
}
public function setHeaderMode($header_mode) {
@@ -182,10 +182,10 @@
private function renderAsyncPanel() {
+ $context_phid = $this->getContextPHID();
$panel = $this->getPanel();
$panel_id = celerity_generate_unique_node_id();
- $dashboard_id = $this->getDashboardID();
Javelin::initBehavior(
'dashboard-async-panel',
@@ -193,7 +193,8 @@
'panelID' => $panel_id,
'parentPanelPHIDs' => $this->getParentPanelPHIDs(),
'headerMode' => $this->getHeaderMode(),
- 'dashboardID' => $dashboard_id,
+ 'contextPHID' => $context_phid,
+ 'panelKey' => $this->getPanelKey(),
'uri' => '/dashboard/panel/render/'.$panel->getID().'/',
));
@@ -322,7 +323,7 @@
$viewer = $this->getViewer();
$panel = $this->getPanel();
- $dashboard_id = $this->getDashboardID();
+ $context_phid = $this->getContextPHID();
$actions = array();
@@ -330,15 +331,15 @@
$panel_id = $panel->getID();
$edit_uri = "/dashboard/panel/edit/{$panel_id}/";
- $edit_uri = new PhutilURI($edit_uri);
- if ($dashboard_id) {
- $edit_uri->replaceQueryParam('dashboardID', $dashboard_id);
- }
+ $params = array(
+ 'contextPHID' => $context_phid,
+ );
+ $edit_uri = new PhutilURI($edit_uri, $params);
$actions[] = id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Panel'))
- ->setHref((string)$edit_uri);
+ ->setHref($edit_uri);
$actions[] = id(new PhabricatorActionView())
->setIcon('fa-window-maximize')
@@ -346,16 +347,19 @@
->setHref($panel->getURI());
}
- if ($dashboard_id) {
+ if ($context_phid) {
$panel_phid = $this->getPanelPHID();
- $remove_uri = "/dashboard/removepanel/{$dashboard_id}/";
- $remove_uri = id(new PhutilURI($remove_uri))
- ->replaceQueryParam('panelPHID', $panel_phid);
+ $remove_uri = urisprintf('/dashboard/adjust/remove/');
+ $params = array(
+ 'contextPHID' => $context_phid,
+ 'panelKey' => $this->getPanelKey(),
+ );
+ $remove_uri = new PhutilURI($remove_uri, $params);
$actions[] = id(new PhabricatorActionView())
->setIcon('fa-times')
- ->setHref((string)$remove_uri)
+ ->setHref($remove_uri)
->setName(pht('Remove Panel'))
->setWorkflow(true);
}
@@ -415,5 +419,14 @@
}
}
+ private function getContextPHID() {
+ $context = $this->getContextObject();
+
+ if ($context) {
+ return $context->getPHID();
+ }
+
+ return null;
+ }
}
diff --git a/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php
--- a/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php
+++ b/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php
@@ -73,9 +73,9 @@
$panel_engine = id(new PhabricatorDashboardPanelRenderingEngine())
->setViewer($viewer)
- ->setDashboardID($dashboard->getID())
->setEnableAsyncRendering(true)
->setContextObject($dashboard)
+ ->setPanelKey($panel_ref->getPanelKey())
->setPanelPHID($panel_phid)
->setParentPanelPHIDs(array())
->setHeaderMode($h_mode)
@@ -94,14 +94,20 @@
if ($is_editable) {
$column_views[] = $this->renderAddPanelPlaceHolder();
- $column_views[] = $this->renderAddPanelUI($column->getColumnKey());
+ $column_views[] = $this->renderAddPanelUI($column);
}
+ $sigil = 'dashboard-column';
+
+ $metadata = array(
+ 'columnKey' => $column->getColumnKey(),
+ );
+
$result->addColumn(
$column_views,
implode(' ', $column_classes),
- $sigil = 'dashboard-column',
- $metadata = array('columnID' => $column));
+ $sigil,
+ $metadata);
}
if ($is_editable) {
@@ -133,15 +139,17 @@
pht('This column does not have any panels yet.'));
}
- private function renderAddPanelUI($column) {
- $dashboard_id = $this->dashboard->getID();
+ private function renderAddPanelUI(PhabricatorDashboardColumn $column) {
+ $dashboard = $this->getDashboard();
+ $column_key = $column->getColumnKey();
$create_uri = id(new PhutilURI('/dashboard/panel/edit/'))
- ->replaceQueryParam('dashboardID', $dashboard_id)
- ->replaceQueryParam('columnID', $column);
+ ->replaceQueryParam('contextPHID', $dashboard->getPHID())
+ ->replaceQueryParam('columnKey', $column_key);
- $add_uri = id(new PhutilURI('/dashboard/addpanel/'.$dashboard_id.'/'))
- ->replaceQueryParam('columnID', $column);
+ $add_uri = id(new PhutilURI('/dashboard/adjust/add/'))
+ ->replaceQueryParam('contextPHID', $dashboard->getPHID())
+ ->replaceQueryParam('columnKey', $column_key);
$create_button = id(new PHUIButtonView())
->setTag('a')
diff --git a/src/applications/dashboard/layoutconfig/PhabricatorDashboardLayoutConfig.php b/src/applications/dashboard/layoutconfig/PhabricatorDashboardLayoutConfig.php
deleted file mode 100644
--- a/src/applications/dashboard/layoutconfig/PhabricatorDashboardLayoutConfig.php
+++ /dev/null
@@ -1,128 +0,0 @@
-<?php
-
-final class PhabricatorDashboardLayoutConfig extends Phobject {
-
- const MODE_FULL = 'layout-mode-full';
- const MODE_HALF_AND_HALF = 'layout-mode-half-and-half';
- const MODE_THIRD_AND_THIRDS = 'layout-mode-third-and-thirds';
- const MODE_THIRDS_AND_THIRD = 'layout-mode-thirds-and-third';
-
- private $layoutMode = self::MODE_FULL;
- private $panelLocations = array();
-
- public function setLayoutMode($mode) {
- $this->layoutMode = $mode;
- return $this;
- }
- public function getLayoutMode() {
- return $this->layoutMode;
- }
-
- public function setPanelLocation($which_column, $panel_phid) {
- $this->panelLocations[$which_column][] = $panel_phid;
- return $this;
- }
-
- public function setPanelLocations(array $locations) {
- $this->panelLocations = $locations;
- return $this;
- }
-
- public function getPanelLocations() {
- return $this->panelLocations;
- }
-
- public function replacePanel($old_phid, $new_phid) {
- $locations = $this->getPanelLocations();
- foreach ($locations as $column => $panel_phids) {
- foreach ($panel_phids as $key => $panel_phid) {
- if ($panel_phid == $old_phid) {
- $locations[$column][$key] = $new_phid;
- }
- }
- }
- return $this->setPanelLocations($locations);
- }
-
- public function removePanel($panel_phid) {
- $panel_location_grid = $this->getPanelLocations();
- foreach ($panel_location_grid as $column => $panel_columns) {
- $found_old_column = array_search($panel_phid, $panel_columns);
- if ($found_old_column !== false) {
- $new_panel_columns = $panel_columns;
- array_splice(
- $new_panel_columns,
- $found_old_column,
- 1,
- array());
- $panel_location_grid[$column] = $new_panel_columns;
- break;
- }
- }
- $this->setPanelLocations($panel_location_grid);
- }
-
- public function getDefaultPanelLocations() {
- switch ($this->getLayoutMode()) {
- case self::MODE_HALF_AND_HALF:
- case self::MODE_THIRD_AND_THIRDS:
- case self::MODE_THIRDS_AND_THIRD:
- $locations = array(array(), array());
- break;
- case self::MODE_FULL:
- default:
- $locations = array(array());
- break;
- }
- return $locations;
- }
-
- public function getColumnClass($column_index, $grippable = false) {
- switch ($this->getLayoutMode()) {
- case self::MODE_HALF_AND_HALF:
- $class = 'half';
- break;
- case self::MODE_THIRD_AND_THIRDS:
- if ($column_index) {
- $class = 'thirds';
- } else {
- $class = 'third';
- }
- break;
- case self::MODE_THIRDS_AND_THIRD:
- if ($column_index) {
- $class = 'third';
- } else {
- $class = 'thirds';
- }
- break;
- case self::MODE_FULL:
- default:
- $class = null;
- break;
- }
- if ($grippable) {
- $class .= ' grippable';
- }
- return $class;
- }
-
- public static function newFromDictionary(array $dict) {
- $layout_config = id(new PhabricatorDashboardLayoutConfig())
- ->setLayoutMode(idx($dict, 'layoutMode', self::MODE_FULL));
- $layout_config->setPanelLocations(idx(
- $dict,
- 'panelLocations',
- $layout_config->getDefaultPanelLocations()));
-
- return $layout_config;
- }
-
- public function toDictionary() {
- return array(
- 'layoutMode' => $this->getLayoutMode(),
- 'panelLocations' => $this->getPanelLocations(),
- );
- }
-
-}
diff --git a/src/applications/dashboard/layoutconfig/PhabricatorDashboardPanelRef.php b/src/applications/dashboard/layoutconfig/PhabricatorDashboardPanelRef.php
--- a/src/applications/dashboard/layoutconfig/PhabricatorDashboardPanelRef.php
+++ b/src/applications/dashboard/layoutconfig/PhabricatorDashboardPanelRef.php
@@ -34,4 +34,12 @@
return $this->panelKey;
}
+ public function toDictionary() {
+ return array(
+ 'panelKey' => $this->getPanelKey(),
+ 'panelPHID' => $this->getPanelPHID(),
+ 'columnKey' => $this->getColumnKey(),
+ );
+ }
+
}
diff --git a/src/applications/dashboard/layoutconfig/PhabricatorDashboardPanelRefList.php b/src/applications/dashboard/layoutconfig/PhabricatorDashboardPanelRefList.php
--- a/src/applications/dashboard/layoutconfig/PhabricatorDashboardPanelRefList.php
+++ b/src/applications/dashboard/layoutconfig/PhabricatorDashboardPanelRefList.php
@@ -73,4 +73,47 @@
return $this->refs;
}
+ public function getPanelRef($panel_key) {
+ foreach ($this->getPanelRefs() as $ref) {
+ if ($ref->getPanelKey() === $panel_key) {
+ return $ref;
+ }
+ }
+
+ return null;
+ }
+
+ public function toDictionary() {
+ return array_values(mpull($this->getPanelRefs(), 'toDictionary'));
+ }
+
+ public function newPanelRef(PhabricatorDashboardPanel $panel, $column_key) {
+ $ref = id(new PhabricatorDashboardPanelRef())
+ ->setPanelKey($this->newPanelKey())
+ ->setPanelPHID($panel->getPHID())
+ ->setColumnKey($column_key);
+
+ $this->refs[] = $ref;
+
+ return $ref;
+ }
+
+ public function removePanelRef(PhabricatorDashboardPanelRef $target) {
+ foreach ($this->refs as $key => $ref) {
+ if ($ref->getPanelKey() !== $target->getPanelKey()) {
+ continue;
+ }
+
+ unset($this->refs[$key]);
+ return $ref;
+ }
+
+ return null;
+ }
+
+ private function newPanelKey() {
+ return Filesystem::readRandomCharacters(8);
+ }
+
+
}
diff --git a/src/applications/dashboard/storage/PhabricatorDashboard.php b/src/applications/dashboard/storage/PhabricatorDashboard.php
--- a/src/applications/dashboard/storage/PhabricatorDashboard.php
+++ b/src/applications/dashboard/storage/PhabricatorDashboard.php
@@ -24,9 +24,6 @@
const STATUS_ACTIVE = 'active';
const STATUS_ARCHIVED = 'archived';
- private $panels = self::ATTACHABLE;
- private $edgeProjectPHIDs = self::ATTACHABLE;
-
private $panelRefList;
public static function initializeNewDashboard(PhabricatorUser $actor) {
@@ -36,8 +33,7 @@
->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
->setEditPolicy($actor->getPHID())
->setStatus(self::STATUS_ACTIVE)
- ->setAuthorPHID($actor->getPHID())
- ->attachPanels(array());
+ ->setAuthorPHID($actor->getPHID());
}
public static function getStatusNameMap() {
@@ -62,9 +58,8 @@
) + parent::getConfiguration();
}
- public function generatePHID() {
- return PhabricatorPHID::generateNewPHID(
- PhabricatorDashboardDashboardPHIDType::TYPECONST);
+ public function getPHIDType() {
+ return PhabricatorDashboardDashboardPHIDType::TYPECONST;
}
public function getRawLayoutMode() {
@@ -75,11 +70,18 @@
public function setRawLayoutMode($mode) {
$config = $this->getRawLayoutConfig();
$config['layoutMode'] = $mode;
+ return $this->setRawLayoutConfig($config);
+ }
- // If a cached panel ref list exists, clear it.
- $this->panelRefList = null;
+ public function getRawPanels() {
+ $config = $this->getRawLayoutConfig();
+ return idx($config, 'panels');
+ }
- return $this->setLayoutConfig($config);
+ public function setRawPanels(array $panels) {
+ $config = $this->getRawLayoutConfig();
+ $config['panels'] = $panels;
+ return $this->setRawLayoutConfig($config);
}
private function getRawLayoutConfig() {
@@ -92,6 +94,13 @@
return $config;
}
+ private function setRawLayoutConfig(array $config) {
+ // If a cached panel ref list exists, clear it.
+ $this->panelRefList = null;
+
+ return $this->setLayoutConfig($config);
+ }
+
public function isArchived() {
return ($this->getStatus() == self::STATUS_ARCHIVED);
}
diff --git a/src/applications/dashboard/xaction/dashboard/PhabricatorDashboardPanelsTransaction.php b/src/applications/dashboard/xaction/dashboard/PhabricatorDashboardPanelsTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/dashboard/xaction/dashboard/PhabricatorDashboardPanelsTransaction.php
@@ -0,0 +1,153 @@
+<?php
+
+final class PhabricatorDashboardPanelsTransaction
+ extends PhabricatorDashboardTransactionType {
+
+ const TRANSACTIONTYPE = 'panels';
+
+ public function generateOldValue($object) {
+ return $object->getRawPanels();
+ }
+
+ public function applyInternalEffects($object, $value) {
+ $object->setRawPanels($value);
+ }
+
+ public function getTitle() {
+ return pht(
+ '%s changed the panels on this dashboard.',
+ $this->renderAuthor());
+ }
+
+ public function validateTransactions($object, array $xactions) {
+ $actor = $this->getActor();
+ $errors = array();
+
+ $ref_list = $object->getPanelRefList();
+ $columns = $ref_list->getColumns();
+
+ $old_phids = $object->getPanelPHIDs();
+ $old_phids = array_fuse($old_phids);
+
+ foreach ($xactions as $xaction) {
+ $new_value = $xaction->getNewValue();
+ if (!is_array($new_value)) {
+ $errors[] = $this->newInvalidError(
+ pht('Panels must be a list of panel specifications.'),
+ $xaction);
+ continue;
+ }
+
+ if (!phutil_is_natural_list($new_value)) {
+ $errors[] = $this->newInvalidError(
+ pht('Panels must be a list, not a map.'),
+ $xaction);
+ continue;
+ }
+
+ $new_phids = array();
+ $seen_keys = array();
+ foreach ($new_value as $idx => $spec) {
+ if (!is_array($spec)) {
+ $errors[] = $this->newInvalidError(
+ pht(
+ 'Each panel specification must be a map of panel attributes. '.
+ 'Panel specification at index "%s" is "%s".',
+ $idx,
+ phutil_describe_type($spec)),
+ $xaction);
+ continue;
+ }
+
+ try {
+ PhutilTypeSpec::checkMap(
+ $spec,
+ array(
+ 'panelPHID' => 'string',
+ 'columnKey' => 'string',
+ 'panelKey' => 'string',
+ ));
+ } catch (PhutilTypeCheckException $ex) {
+ $errors[] = $this->newInvalidError(
+ pht(
+ 'Panel specification at index "%s" is invalid: %s',
+ $idx,
+ $ex->getMessage()),
+ $xaction);
+ continue;
+ }
+
+ $panel_key = $spec['panelKey'];
+
+ if (!strlen($panel_key)) {
+ $errors[] = $this->newInvalidError(
+ pht(
+ 'Panel specification at index "%s" has bad panel key "%s". '.
+ 'Panel keys must be nonempty.',
+ $idx,
+ $panel_key),
+ $xaction);
+ continue;
+ }
+
+ if (isset($seen_keys[$panel_key])) {
+ $errors[] = $this->newInvalidError(
+ pht(
+ 'Panel specification at index "%s" has duplicate panel key '.
+ '"%s". Each panel must have a unique panel key.',
+ $idx,
+ $panel_key),
+ $xaction);
+ continue;
+ }
+
+ $seen_keys[$panel_key] = true;
+
+ $panel_phid = $spec['panelPHID'];
+ $new_phids[] = $panel_phid;
+
+ $column_key = $spec['columnKey'];
+
+ if (!isset($columns[$column_key])) {
+ $errors[] = $this->newInvalidError(
+ pht(
+ 'Panel specification at index "%s" has bad column key "%s", '.
+ 'valid column keys are: %s.',
+ $idx,
+ $column_key,
+ implode(', ', array_keys($columns))),
+ $xaction);
+ continue;
+ }
+ }
+
+ $new_phids = array_fuse($new_phids);
+ $add_phids = array_diff_key($new_phids, $old_phids);
+
+ if ($add_phids) {
+ $panels = id(new PhabricatorDashboardPanelQuery())
+ ->setViewer($actor)
+ ->withPHIDs($add_phids)
+ ->execute();
+ $panels = mpull($panels, null, 'getPHID');
+
+ foreach ($add_phids as $add_phid) {
+ $panel = idx($panels, $add_phid);
+
+ if (!$panel) {
+ $errors[] = $this->newInvalidError(
+ pht(
+ 'Panel specification adds panel "%s", but this is not a '.
+ 'valid panel or not a visible panel. You can only add '.
+ 'valid panels which you have permission to see to a dashboard.',
+ $add_phid));
+ continue;
+ }
+ }
+ }
+ }
+
+ return $errors;
+ }
+
+}
diff --git a/webroot/rsrc/js/application/dashboard/behavior-dashboard-async-panel.js b/webroot/rsrc/js/application/dashboard/behavior-dashboard-async-panel.js
--- a/webroot/rsrc/js/application/dashboard/behavior-dashboard-async-panel.js
+++ b/webroot/rsrc/js/application/dashboard/behavior-dashboard-async-panel.js
@@ -12,7 +12,8 @@
var data = {
parentPanelPHIDs: config.parentPanelPHIDs.join(','),
headerMode: config.headerMode,
- dashboardID: config.dashboardID
+ contextPHID: config.contextPHID,
+ panelKey: config.panelKey
};
new JX.Workflow(config.uri)

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 23, 7:51 PM (15 h, 42 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6779462
Default Alt Text
D20408.diff (49 KB)

Event Timeline