diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index b8135b06ab..85a8c6e13e 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1488,7 +1488,6 @@ phutil_register_library_map(array( 'PhabricatorDashboardPHIDTypePanel' => 'applications/dashboard/phid/PhabricatorDashboardPHIDTypePanel.php', 'PhabricatorDashboardPanel' => 'applications/dashboard/storage/PhabricatorDashboardPanel.php', 'PhabricatorDashboardPanelCoreCustomField' => 'applications/dashboard/customfield/PhabricatorDashboardPanelCoreCustomField.php', - 'PhabricatorDashboardPanelCreateController' => 'applications/dashboard/controller/PhabricatorDashboardPanelCreateController.php', 'PhabricatorDashboardPanelCustomField' => 'applications/dashboard/customfield/PhabricatorDashboardPanelCustomField.php', 'PhabricatorDashboardPanelEditController' => 'applications/dashboard/controller/PhabricatorDashboardPanelEditController.php', 'PhabricatorDashboardPanelListController' => 'applications/dashboard/controller/PhabricatorDashboardPanelListController.php', @@ -4313,7 +4312,6 @@ phutil_register_library_map(array( 0 => 'PhabricatorDashboardPanelCustomField', 1 => 'PhabricatorStandardCustomFieldInterface', ), - 'PhabricatorDashboardPanelCreateController' => 'PhabricatorDashboardController', 'PhabricatorDashboardPanelCustomField' => 'PhabricatorCustomField', 'PhabricatorDashboardPanelEditController' => 'PhabricatorDashboardController', 'PhabricatorDashboardPanelListController' => 'PhabricatorDashboardController', diff --git a/src/applications/dashboard/application/PhabricatorApplicationDashboard.php b/src/applications/dashboard/application/PhabricatorApplicationDashboard.php index a667a4d5bb..220a492714 100644 --- a/src/applications/dashboard/application/PhabricatorApplicationDashboard.php +++ b/src/applications/dashboard/application/PhabricatorApplicationDashboard.php @@ -34,7 +34,7 @@ final class PhabricatorApplicationDashboard extends PhabricatorApplication { 'panel/' => array( '(?:query/(?P[^/]+)/)?' => 'PhabricatorDashboardPanelListController', - 'create/' => 'PhabricatorDashboardPanelCreateController', + 'create/' => 'PhabricatorDashboardPanelEditController', 'edit/(?:(?P\d+)/)?' => 'PhabricatorDashboardPanelEditController', 'render/(?P\d+)/' => 'PhabricatorDashboardPanelRenderController', ), diff --git a/src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php b/src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php index 44c0f3dc80..28c987fa37 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php @@ -26,82 +26,74 @@ final class PhabricatorDashboardAddPanelController return new Aphront404Response(); } - $redirect_uri = $this->getApplicationURI( - 'manage/'.$dashboard->getID().'/'); - $layout_config = $dashboard->getLayoutConfigObject(); + $redirect_uri = $this->getApplicationURI('manage/'.$dashboard->getID().'/'); $v_panel = $request->getStr('panel'); $e_panel = true; $errors = array(); if ($request->isFormPost()) { if (strlen($v_panel)) { - $panel = id(new PhabricatorObjectQuery()) + $panel = id(new PhabricatorDashboardPanelQuery()) ->setViewer($viewer) - ->withNames(array($v_panel)) - ->withTypes(array(PhabricatorDashboardPHIDTypePanel::TYPECONST)) + ->withIDs(array($v_panel)) ->executeOne(); if (!$panel) { $errors[] = pht('No such panel!'); $e_panel = pht('Invalid'); } } else { - $errors[] = pht('Name a panel to add.'); + $errors[] = pht('Select a panel to add.'); $e_panel = pht('Required'); } if (!$errors) { - $xactions = array(); - $xactions[] = id(new PhabricatorDashboardTransaction()) - ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) - ->setMetadataValue( - 'edge:type', - PhabricatorEdgeConfig::TYPE_DASHBOARD_HAS_PANEL) - ->setNewValue( - array( - '+' => array( - $panel->getPHID() => $panel->getPHID(), - ), - )); - - $layout_config->setPanelLocation( - $request->getInt('column', 0), - $panel->getPHID()); - $dashboard->setLayoutConfigFromObject($layout_config); - - $editor = id(new PhabricatorDashboardTransactionEditor()) - ->setActor($viewer) - ->setContentSourceFromRequest($request) - ->setContinueOnMissingFields(true) - ->setContinueOnNoEffect(true) - ->applyTransactions($dashboard, $xactions); + 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) + ->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); + } + + $panel_options = array(); + foreach ($panels as $panel) { + $panel_options[$panel->getID()] = pht( + '%s %s', + $panel->getMonogram(), + $panel->getName()); + } + $form = id(new AphrontFormView()) ->setUser($viewer) - ->addHiddenInput('src', $request->getStr('src', 'edit')) + ->addHiddenInput('column', $request->getInt('column')) ->appendRemarkupInstructions( - pht('Enter a panel monogram like `W123`.')) + pht('Choose a panel to add to this dashboard:')) ->appendChild( - id(new AphrontFormTextControl()) + id(new AphrontFormSelectControl()) ->setName('panel') ->setLabel(pht('Panel')) ->setValue($v_panel) - ->setError($e_panel)); - - if ($layout_config->isMultiColumnLayout()) { - $form - ->appendRemarkupInstructions( - pht('Choose which column the panel should reside in.')) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setName('column') - ->setLabel(pht('Column')) - ->setOptions($layout_config->getColumnSelectOptions()) - ->setValue($request->getInt('column'))); - } + ->setError($e_panel) + ->setOptions($panel_options)); return $this->newDialog() ->setTitle(pht('Add Panel')) diff --git a/src/applications/dashboard/controller/PhabricatorDashboardPanelCreateController.php b/src/applications/dashboard/controller/PhabricatorDashboardPanelCreateController.php deleted file mode 100644 index 344044a88e..0000000000 --- a/src/applications/dashboard/controller/PhabricatorDashboardPanelCreateController.php +++ /dev/null @@ -1,75 +0,0 @@ -getRequest(); - $viewer = $request->getUser(); - - $types = PhabricatorDashboardPanelType::getAllPanelTypes(); - - $v_type = null; - $errors = array(); - if ($request->isFormPost()) { - $v_type = $request->getStr('type'); - if (!isset($types[$v_type])) { - $errors[] = pht('You must select a type of panel to create.'); - } - - if (!$errors) { - return id(new AphrontRedirectResponse())->setURI( - $this->getApplicationURI('panel/edit/?type='.$v_type)); - } - } - - $cancel_uri = $this->getApplicationURI('panel/'); - - if (!$v_type) { - $v_type = key($types); - } - - $panel_types = id(new AphrontFormRadioButtonControl()) - ->setName('type') - ->setValue($v_type); - - foreach ($types as $key => $type) { - $panel_types->addButton( - $key, - $type->getPanelTypeName(), - $type->getPanelTypeDescription()); - } - - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->appendChild($panel_types) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Continue')) - ->addCancelButton($cancel_uri)); - - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb( - pht('Panels'), - $this->getApplicationURI('panel/')); - $crumbs->addTextCrumb(pht('New Panel')); - - $title = pht('Create Dashboard Panel'); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText($title) - ->setFormErrors($errors) - ->setForm($form); - - return $this->buildApplicationPage( - array( - $crumbs, - $box, - ), - array( - 'title' => $title, - 'device' => true, - )); - } - -} diff --git a/src/applications/dashboard/controller/PhabricatorDashboardPanelEditController.php b/src/applications/dashboard/controller/PhabricatorDashboardPanelEditController.php index 2c949564ef..2c2e283862 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardPanelEditController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardPanelEditController.php @@ -13,6 +13,28 @@ final class PhabricatorDashboardPanelEditController $request = $this->getRequest(); $viewer = $request->getUser(); + // If the user is trying to create a panel directly on a dashboard, make + // sure they have permission to see and edit the dashboard. + + $dashboard_id = $request->getInt('dashboardID'); + $dashboard = null; + if ($dashboard_id) { + $dashboard = id(new PhabricatorDashboardQuery()) + ->setViewer($viewer) + ->withIDs(array($dashboard_id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + if (!$dashboard) { + return new Aphront404Response(); + } + + $manage_uri = $this->getApplicationURI('manage/'.$dashboard_id.'/'); + } + if ($this->id) { $is_create = false; @@ -33,11 +55,10 @@ final class PhabricatorDashboardPanelEditController $is_create = true; $panel = PhabricatorDashboardPanel::initializeNewPanel($viewer); - $types = PhabricatorDashboardPanelType::getAllPanelTypes(); $type = $request->getStr('type'); if (empty($types[$type])) { - return new Aphront404Response(); + return $this->processPanelTypeRequest($request); } $panel->setPanelType($type); @@ -47,12 +68,20 @@ final class PhabricatorDashboardPanelEditController $title = pht('New Panel'); $header = pht('Create New Panel'); $button = pht('Create Panel'); - $cancel_uri = $this->getApplicationURI('panel/'); + if ($dashboard) { + $cancel_uri = $manage_uri; + } else { + $cancel_uri = $this->getApplicationURI('panel/'); + } } else { $title = pht('Edit %s', $panel->getMonogram()); $header = pht('Edit %s %s', $panel->getMonogram(), $panel->getName()); $button = pht('Save Panel'); - $cancel_uri = $this->getPanelRedirectURI($panel); + if ($dashboard) { + $cancel_uri = $manage_uri; + } else { + $cancel_uri = '/'.$panel->getMonogram(); + } } $v_name = $panel->getName(); @@ -67,7 +96,10 @@ final class PhabricatorDashboardPanelEditController $validation_exception = null; - if ($request->isFormPost()) { + // NOTE: We require 'edit' to distinguish between the "Choose a Type" + // and "Create a Panel" dialogs. + + if ($request->isFormPost() && $request->getBool('edit')) { $v_name = $request->getStr('name'); $v_view_policy = $request->getStr('viewPolicy'); $v_edit_policy = $request->getStr('editPolicy'); @@ -102,8 +134,23 @@ final class PhabricatorDashboardPanelEditController ->setContentSourceFromRequest($request) ->applyTransactions($panel, $xactions); - return id(new AphrontRedirectResponse()) - ->setURI($this->getPanelRedirectURI($panel)); + // If we're creating a panel directly on a dashboard, add it now. + if ($dashboard) { + PhabricatorDashboardTransactionEditor::addPanelToDashboard( + $viewer, + PhabricatorContentSource::newFromRequest($request), + $panel, + $dashboard, + $request->getInt('column', 0)); + } + + if ($dashboard) { + $done_uri = $manage_uri; + } else { + $done_uri = '/'.$panel->getMonogram(); + } + + return id(new AphrontRedirectResponse())->setURI($done_uri); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; @@ -121,7 +168,9 @@ final class PhabricatorDashboardPanelEditController $form = id(new AphrontFormView()) ->setUser($viewer) + ->addHiddenInput('edit', true) ->addHiddenInput('dashboardID', $request->getInt('dashboardID')) + ->addHiddenInput('column', $request->getInt('column')) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Name')) @@ -189,16 +238,83 @@ final class PhabricatorDashboardPanelEditController )); } - private function getPanelRedirectURI(PhabricatorDashboardPanel $panel) { - $request = $this->getRequest(); - $dashboard_id = $request->getInt('dashboardID'); - if ($dashboard_id) { - $uri = $this->getApplicationURI('manage/'.$dashboard_id.'/'); - } else { - $uri = '/'.$panel->getMonogram(); + private function processPanelTypeRequest(AphrontRequest $request) { + $viewer = $request->getUser(); + + $types = PhabricatorDashboardPanelType::getAllPanelTypes(); + + $v_type = null; + $errors = array(); + if ($request->isFormPost()) { + $v_type = $request->getStr('type'); + if (!isset($types[$v_type])) { + $errors[] = pht('You must select a type of panel to create.'); + } } - return $uri; + $cancel_uri = $this->getApplicationURI('panel/'); + + if (!$v_type) { + $v_type = key($types); + } + + $panel_types = id(new AphrontFormRadioButtonControl()) + ->setName('type') + ->setValue($v_type); + + foreach ($types as $key => $type) { + $panel_types->addButton( + $key, + $type->getPanelTypeName(), + $type->getPanelTypeDescription()); + } + + $form = id(new AphrontFormView()) + ->setUser($viewer) + ->addHiddenInput('dashboardID', $request->getInt('dashboardID')) + ->addHiddenInput('column', $request->getInt('column')) + ->appendRemarkupInstructions( + pht( + 'Choose the type of dashboard panel to create:')) + ->appendChild($panel_types); + + if ($request->isAjax()) { + return $this->newDialog() + ->setTitle(pht('Add New Panel')) + ->setWidth(AphrontDialogView::WIDTH_FORM) + ->setErrors($errors) + ->appendChild($form->buildLayoutView()) + ->addCancelbutton($cancel_uri) + ->addSubmitButton(pht('Continue')); + } else { + $form->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue(pht('Continue')) + ->addCancelButton($cancel_uri)); + } + + $title = pht('Create Dashboard Panel'); + + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addTextCrumb( + pht('Panels'), + $this->getApplicationURI('panel/')); + $crumbs->addTextCrumb(pht('New Panel')); + + $box = id(new PHUIObjectBoxView()) + ->setHeaderText($title) + ->setFormErrors($errors) + ->setForm($form); + + return $this->buildApplicationPage( + array( + $crumbs, + $box, + ), + array( + 'title' => $title, + 'device' => true, + )); } } diff --git a/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php b/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php index fe824727fe..e5618d7c7d 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php @@ -136,9 +136,14 @@ final class PhabricatorDashboardPanelViewController PhabricatorEdgeConfig::TYPE_PANEL_HAS_DASHBOARD); $this->loadHandles($dashboard_phids); + $does_not_appear = pht( + 'This panel does not appear on any dashboards.'); + $properties->addProperty( pht('Appears On'), - $this->renderHandlesForPHIDs($dashboard_phids)); + $dashboard_phids + ? $this->renderHandlesForPHIDs($dashboard_phids) + : phutil_tag('em', array(), $does_not_appear)); return $properties; } diff --git a/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php b/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php index df05e566a8..73e8035e4e 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php @@ -3,6 +3,38 @@ final class PhabricatorDashboardTransactionEditor extends PhabricatorApplicationTransactionEditor { + public static function addPanelToDashboard( + PhabricatorUser $actor, + PhabricatorContentSource $content_source, + PhabricatorDashboardPanel $panel, + PhabricatorDashboard $dashboard, + $column) { + + $xactions = array(); + $xactions[] = id(new PhabricatorDashboardTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) + ->setMetadataValue( + 'edge:type', + PhabricatorEdgeConfig::TYPE_DASHBOARD_HAS_PANEL) + ->setNewValue( + array( + '+' => array( + $panel->getPHID() => $panel->getPHID(), + ), + )); + + $layout_config = $dashboard->getLayoutConfigObject(); + $layout_config->setPanelLocation($column, $panel->getPHID()); + $dashboard->setLayoutConfigFromObject($layout_config); + + $editor = id(new PhabricatorDashboardTransactionEditor()) + ->setActor($actor) + ->setContentSource($content_source) + ->setContinueOnMissingFields(true) + ->setContinueOnNoEffect(true) + ->applyTransactions($dashboard, $xactions); + } + public function getTransactionTypes() { $types = parent::getTransactionTypes(); diff --git a/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php index 8c37e78a0b..8f0c43a914 100644 --- a/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php +++ b/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php @@ -86,45 +86,53 @@ final class PhabricatorDashboardRenderingEngine extends Phobject { } private function renderAddPanelPlaceHolder($column) { - $uri = $this->getAddPanelURI($column); - $dashboard = $this->dashboard; $panels = $dashboard->getPanels(); - $layout_config = $dashboard->getLayoutConfigObject(); - if ($layout_config->isMultiColumnLayout() && count($panels)) { - $text = pht('Drag a panel here or click to add a panel.'); - } else { - $text = pht('Click to add a panel.'); - } + return javelin_tag( - 'a', + 'span', array( 'sigil' => 'workflow', 'class' => 'drag-ghost dashboard-panel-placeholder', - 'href' => (string) $uri), - $text); + ), + pht('This column does not have any panels yet.')); } private function renderAddPanelUI($column) { - $uri = $this->getAddPanelURI($column); + $dashboard_id = $this->dashboard->getID(); - return id(new PHUIButtonView()) + $create_uri = id(new PhutilURI('/dashboard/panel/create/')) + ->setQueryParam('dashboardID', $dashboard_id) + ->setQueryParam('column', $column); + + $add_uri = id(new PhutilURI('/dashboard/addpanel/'.$dashboard_id.'/')) + ->setQueryParam('column', $column); + + $create_button = id(new PHUIButtonView()) ->setTag('a') - ->setHref((string) $uri) + ->setHref($create_uri) ->setWorkflow(true) ->setColor(PHUIButtonView::GREY) - ->setIcon(id(new PHUIIconView()) - ->setIconFont('fa-plus')) - ->setText(pht('Add Panel')) - ->addClass(PHUI::MARGIN_LARGE); - } + ->setText(pht('Create Panel')) + ->addClass(PHUI::MARGIN_MEDIUM); - private function getAddPanelURI($column) { - $dashboard = $this->dashboard; - $uri = id(new PhutilURI('/dashboard/addpanel/'.$dashboard->getID().'/')) - ->setQueryParam('column', $column) - ->setQueryParam('src', 'arrange'); - return $uri; + $add_button = id(new PHUIButtonView()) + ->setTag('a') + ->setHref($add_uri) + ->setWorkflow(true) + ->setColor(PHUIButtonView::GREY) + ->setText(pht('Add Existing Panel')) + ->addClass(PHUI::MARGIN_MEDIUM); + + return phutil_tag( + 'div', + array( + 'style' => 'text-align: center;', + ), + array( + $create_button, + $add_button, + )); } } diff --git a/webroot/rsrc/css/application/dashboard/dashboard.css b/webroot/rsrc/css/application/dashboard/dashboard.css index 11ff9ccbec..219268741e 100644 --- a/webroot/rsrc/css/application/dashboard/dashboard.css +++ b/webroot/rsrc/css/application/dashboard/dashboard.css @@ -50,18 +50,12 @@ .aphront-multi-column-fluid .aphront-multi-column-column.dashboard-column-empty .dashboard-panel-placeholder { - color: {$greytext}; display: block; padding: 24px; margin: 16px 16px 0px 16px; -} - -.aphront-multi-column-fluid -.aphront-multi-column-column.dashboard-column-empty -.dashboard-panel-placeholder:hover { text-decoration: none; border: 1px {$greyborder} dashed; - color: {$darkgreytext}; + color: {$greytext}; } .aphront-multi-column-fluid