Commit 12b92243 authored by epriestley's avatar epriestley
Browse files

Make the "Install Dashboard" flow smoother

Summary:
Depends on D20362. Ref T13272. Currently, Dashboards have an "Install Dashboard" flow which is pretty janky and only allows you to install things to the home page.

Instead, allow users to install things to any valid target (home, favorites, portals, projects). This also provides URIs like `dashboard/install/1/home/personal/` which allow you to link users to an "install a dashboard" page; this may or may not get used.

Test Plan: Installed dashboards on home, favorites, projects, and portals.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13272

Differential Revision: https://secure.phabricator.com/D20364
parent eea093be
......@@ -9,7 +9,7 @@ return array(
'names' => array(
'conpherence.pkg.css' => '3c8a0668',
'conpherence.pkg.js' => '020aebcf',
'core.pkg.css' => 'a1c2d49b',
'core.pkg.css' => '3b565a84',
'core.pkg.js' => 'a568e834',
'differential.pkg.css' => '8d8360fb',
'differential.pkg.js' => '67e02996',
......@@ -128,7 +128,7 @@ return array(
'rsrc/css/phui/calendar/phui-calendar-list.css' => 'ccd7e4e2',
'rsrc/css/phui/calendar/phui-calendar-month.css' => 'cb758c42',
'rsrc/css/phui/calendar/phui-calendar.css' => 'f11073aa',
'rsrc/css/phui/object-item/phui-oi-big-ui.css' => '534f1757',
'rsrc/css/phui/object-item/phui-oi-big-ui.css' => 'fa74cc35',
'rsrc/css/phui/object-item/phui-oi-color.css' => 'b517bfa0',
'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => 'da15d3dc',
'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '490e2e2e',
......@@ -849,7 +849,7 @@ return array(
'phui-lightbox-css' => '4ebf22da',
'phui-list-view-css' => '470b1adb',
'phui-object-box-css' => 'f434b6be',
'phui-oi-big-ui-css' => '534f1757',
'phui-oi-big-ui-css' => 'fa74cc35',
'phui-oi-color-css' => 'b517bfa0',
'phui-oi-drag-ui-css' => 'da15d3dc',
'phui-oi-flush-ui-css' => '490e2e2e',
......@@ -1379,9 +1379,6 @@ return array(
'javelin-dom',
'javelin-fx',
),
'534f1757' => array(
'phui-oi-list-view-css',
),
'541f81c3' => array(
'javelin-install',
),
......@@ -2179,6 +2176,9 @@ return array(
'phabricator-keyboard-shortcut',
'conpherence-thread-manager',
),
'fa74cc35' => array(
'phui-oi-list-view-css',
),
'fdc13e4e' => array(
'javelin-install',
),
......
......@@ -2905,6 +2905,7 @@ phutil_register_library_map(array(
'PhabricatorDashboard' => 'applications/dashboard/storage/PhabricatorDashboard.php',
'PhabricatorDashboardAddPanelController' => 'applications/dashboard/controller/PhabricatorDashboardAddPanelController.php',
'PhabricatorDashboardApplication' => 'applications/dashboard/application/PhabricatorDashboardApplication.php',
'PhabricatorDashboardApplicationInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardApplicationInstallWorkflow.php',
'PhabricatorDashboardArchiveController' => 'applications/dashboard/controller/dashboard/PhabricatorDashboardArchiveController.php',
'PhabricatorDashboardConsoleController' => 'applications/dashboard/controller/PhabricatorDashboardConsoleController.php',
'PhabricatorDashboardController' => 'applications/dashboard/controller/PhabricatorDashboardController.php',
......@@ -2913,13 +2914,17 @@ phutil_register_library_map(array(
'PhabricatorDashboardDashboardPHIDType' => 'applications/dashboard/phid/PhabricatorDashboardDashboardPHIDType.php',
'PhabricatorDashboardDatasource' => 'applications/dashboard/typeahead/PhabricatorDashboardDatasource.php',
'PhabricatorDashboardEditController' => 'applications/dashboard/controller/dashboard/PhabricatorDashboardEditController.php',
'PhabricatorDashboardFavoritesInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardFavoritesInstallWorkflow.php',
'PhabricatorDashboardHomeInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardHomeInstallWorkflow.php',
'PhabricatorDashboardIconSet' => 'applications/dashboard/icon/PhabricatorDashboardIconSet.php',
'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',
'PhabricatorDashboardListController' => 'applications/dashboard/controller/PhabricatorDashboardListController.php',
'PhabricatorDashboardMovePanelController' => 'applications/dashboard/controller/PhabricatorDashboardMovePanelController.php',
'PhabricatorDashboardNgrams' => 'applications/dashboard/storage/PhabricatorDashboardNgrams.php',
'PhabricatorDashboardObjectInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardObjectInstallWorkflow.php',
'PhabricatorDashboardPanel' => 'applications/dashboard/storage/PhabricatorDashboardPanel.php',
'PhabricatorDashboardPanelArchiveController' => 'applications/dashboard/controller/PhabricatorDashboardPanelArchiveController.php',
'PhabricatorDashboardPanelCoreCustomField' => 'applications/dashboard/customfield/PhabricatorDashboardPanelCoreCustomField.php',
......@@ -2947,10 +2952,12 @@ phutil_register_library_map(array(
'PhabricatorDashboardPanelViewController' => 'applications/dashboard/controller/PhabricatorDashboardPanelViewController.php',
'PhabricatorDashboardPortal' => 'applications/dashboard/storage/PhabricatorDashboardPortal.php',
'PhabricatorDashboardPortalController' => 'applications/dashboard/controller/portal/PhabricatorDashboardPortalController.php',
'PhabricatorDashboardPortalDatasource' => 'applications/dashboard/typeahead/PhabricatorDashboardPortalDatasource.php',
'PhabricatorDashboardPortalEditConduitAPIMethod' => 'applications/dashboard/conduit/PhabricatorDashboardPortalEditConduitAPIMethod.php',
'PhabricatorDashboardPortalEditController' => 'applications/dashboard/controller/portal/PhabricatorDashboardPortalEditController.php',
'PhabricatorDashboardPortalEditEngine' => 'applications/dashboard/editor/PhabricatorDashboardPortalEditEngine.php',
'PhabricatorDashboardPortalEditor' => 'applications/dashboard/editor/PhabricatorDashboardPortalEditor.php',
'PhabricatorDashboardPortalInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardPortalInstallWorkflow.php',
'PhabricatorDashboardPortalListController' => 'applications/dashboard/controller/portal/PhabricatorDashboardPortalListController.php',
'PhabricatorDashboardPortalMenuItem' => 'applications/dashboard/menuitem/PhabricatorDashboardPortalMenuItem.php',
'PhabricatorDashboardPortalNameTransaction' => 'applications/dashboard/xaction/portal/PhabricatorDashboardPortalNameTransaction.php',
......@@ -2966,6 +2973,7 @@ phutil_register_library_map(array(
'PhabricatorDashboardPortalViewController' => 'applications/dashboard/controller/portal/PhabricatorDashboardPortalViewController.php',
'PhabricatorDashboardProfileController' => 'applications/dashboard/controller/PhabricatorDashboardProfileController.php',
'PhabricatorDashboardProfileMenuItem' => 'applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php',
'PhabricatorDashboardProjectInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardProjectInstallWorkflow.php',
'PhabricatorDashboardQuery' => 'applications/dashboard/query/PhabricatorDashboardQuery.php',
'PhabricatorDashboardQueryPanelInstallController' => 'applications/dashboard/controller/PhabricatorDashboardQueryPanelInstallController.php',
'PhabricatorDashboardQueryPanelType' => 'applications/dashboard/paneltype/PhabricatorDashboardQueryPanelType.php',
......@@ -8865,6 +8873,7 @@ phutil_register_library_map(array(
),
'PhabricatorDashboardAddPanelController' => 'PhabricatorDashboardController',
'PhabricatorDashboardApplication' => 'PhabricatorApplication',
'PhabricatorDashboardApplicationInstallWorkflow' => 'PhabricatorDashboardInstallWorkflow',
'PhabricatorDashboardArchiveController' => 'PhabricatorDashboardController',
'PhabricatorDashboardConsoleController' => 'PhabricatorDashboardController',
'PhabricatorDashboardController' => 'PhabricatorController',
......@@ -8873,13 +8882,17 @@ phutil_register_library_map(array(
'PhabricatorDashboardDashboardPHIDType' => 'PhabricatorPHIDType',
'PhabricatorDashboardDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorDashboardEditController' => 'PhabricatorDashboardController',
'PhabricatorDashboardFavoritesInstallWorkflow' => 'PhabricatorDashboardApplicationInstallWorkflow',
'PhabricatorDashboardHomeInstallWorkflow' => 'PhabricatorDashboardApplicationInstallWorkflow',
'PhabricatorDashboardIconSet' => 'PhabricatorIconSet',
'PhabricatorDashboardInstall' => 'PhabricatorDashboardDAO',
'PhabricatorDashboardInstallController' => 'PhabricatorDashboardController',
'PhabricatorDashboardInstallWorkflow' => 'Phobject',
'PhabricatorDashboardLayoutConfig' => 'Phobject',
'PhabricatorDashboardListController' => 'PhabricatorDashboardController',
'PhabricatorDashboardMovePanelController' => 'PhabricatorDashboardController',
'PhabricatorDashboardNgrams' => 'PhabricatorSearchNgrams',
'PhabricatorDashboardObjectInstallWorkflow' => 'PhabricatorDashboardInstallWorkflow',
'PhabricatorDashboardPanel' => array(
'PhabricatorDashboardDAO',
'PhabricatorApplicationTransactionInterface',
......@@ -8923,10 +8936,12 @@ phutil_register_library_map(array(
'PhabricatorDestructibleInterface',
),
'PhabricatorDashboardPortalController' => 'PhabricatorDashboardController',
'PhabricatorDashboardPortalDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorDashboardPortalEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
'PhabricatorDashboardPortalEditController' => 'PhabricatorDashboardPortalController',
'PhabricatorDashboardPortalEditEngine' => 'PhabricatorEditEngine',
'PhabricatorDashboardPortalEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorDashboardPortalInstallWorkflow' => 'PhabricatorDashboardObjectInstallWorkflow',
'PhabricatorDashboardPortalListController' => 'PhabricatorDashboardPortalController',
'PhabricatorDashboardPortalMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorDashboardPortalNameTransaction' => 'PhabricatorDashboardPortalTransactionType',
......@@ -8942,6 +8957,7 @@ phutil_register_library_map(array(
'PhabricatorDashboardPortalViewController' => 'PhabricatorDashboardPortalController',
'PhabricatorDashboardProfileController' => 'PhabricatorController',
'PhabricatorDashboardProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorDashboardProjectInstallWorkflow' => 'PhabricatorDashboardObjectInstallWorkflow',
'PhabricatorDashboardQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorDashboardQueryPanelInstallController' => 'PhabricatorDashboardController',
'PhabricatorDashboardQueryPanelType' => 'PhabricatorDashboardPanelType',
......
......@@ -43,7 +43,10 @@ final class PhabricatorDashboardApplication extends PhabricatorApplication {
'archive/(?P<id>\d+)/' => 'PhabricatorDashboardArchiveController',
'create/' => 'PhabricatorDashboardEditController',
'edit/(?:(?P<id>\d+)/)?' => 'PhabricatorDashboardEditController',
'install/(?:(?P<id>\d+)/)?' => 'PhabricatorDashboardInstallController',
'install/(?P<id>\d+)/'.
'(?:(?P<workflowKey>[^/]+)/'.
'(?:(?P<modeKey>[^/]+)/)?)?' =>
'PhabricatorDashboardInstallController',
'console/' => 'PhabricatorDashboardConsoleController',
'addpanel/(?P<id>\d+)/' => 'PhabricatorDashboardAddPanelController',
'movepanel/(?P<id>\d+)/' => 'PhabricatorDashboardMovePanelController',
......
......@@ -3,6 +3,17 @@
final class PhabricatorDashboardInstallController
extends PhabricatorDashboardController {
private $dashboard;
public function setDashboard(PhabricatorDashboard $dashboard) {
$this->dashboard = $dashboard;
return $this;
}
public function getDashboard() {
return $this->dashboard;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
......@@ -15,126 +26,50 @@ final class PhabricatorDashboardInstallController
return new Aphront404Response();
}
$this->setDashboard($dashboard);
$cancel_uri = $dashboard->getURI();
$home_app = new PhabricatorHomeApplication();
$options = array();
$options['home'] = array(
'personal' =>
array(
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
'application' => $home_app,
'name' => pht('Personal Dashboard'),
'value' => 'personal',
'description' => pht('Places this dashboard as a menu item on home '.
'as a personal menu item. It will only be on your personal '.
'home.'),
),
'global' =>
array(
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
'application' => $home_app,
'name' => pht('Global Dashboard'),
'value' => 'global',
'description' => pht('Places this dashboard as a menu item on home '.
'as a global menu item. It will be available to all users.'),
),
);
$errors = array();
$v_name = null;
if ($request->isFormPost()) {
$menuitem = new PhabricatorDashboardProfileMenuItem();
$dashboard_phid = $dashboard->getPHID();
$home = new PhabricatorHomeApplication();
$v_name = $request->getStr('name');
$v_home = $request->getStr('home');
if ($v_home) {
$application = $options['home'][$v_home]['application'];
$capability = $options['home'][$v_home]['capability'];
$can_edit_home = PhabricatorPolicyFilter::hasCapability(
$viewer,
$application,
$capability);
if (!$can_edit_home) {
$errors[] = pht(
'You do not have permission to install a dashboard on home.');
}
} else {
$errors[] = pht(
'You must select a destination to install this dashboard.');
}
$v_phid = $viewer->getPHID();
if ($v_home == 'global') {
$v_phid = null;
}
if (!$errors) {
$install = PhabricatorProfileMenuItemConfiguration::initializeNewItem(
$home,
$menuitem,
$v_phid);
$install->setMenuItemProperty('dashboardPHID', $dashboard_phid);
$install->setMenuItemProperty('name', $v_name);
$install->setMenuItemOrder(1);
$xactions = array();
$editor = id(new PhabricatorProfileMenuEditor())
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->setContentSourceFromRequest($request);
$editor->applyTransactions($install, $xactions);
$view_uri = '/home/menu/view/'.$install->getID().'/';
return id(new AphrontRedirectResponse())->setURI($view_uri);
}
}
$workflow_key = $request->getURIData('workflowKey');
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Menu Label'))
->setName('name')
->setValue($v_name));
$radio = id(new AphrontFormRadioButtonControl())
->setLabel(pht('Home Menu'))
->setName('home');
foreach ($options['home'] as $type => $option) {
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$option['application'],
$option['capability']);
if ($can_edit) {
$radio->addButton(
$option['value'],
$option['name'],
$option['description']);
}
$workflows = PhabricatorDashboardInstallWorkflow::getAllWorkflows();
if (!isset($workflows[$workflow_key])) {
return $this->newWorkflowDialog($dashboard, $workflows);
}
$form->appendChild($radio);
return id(clone $workflows[$workflow_key])
->setRequest($request)
->setViewer($viewer)
->setDashboard($dashboard)
->setMode($request->getURIData('modeKey'))
->handleRequest($request);
}
private function newWorkflowDialog(
PhabricatorDashboard $dashboard,
array $workflows) {
$viewer = $this->getViewer();
$cancel_uri = $dashboard->getURI();
$menu = id(new PHUIObjectItemListView())
->setViewer($viewer)
->setFlush(true)
->setBig(true);
foreach ($workflows as $key => $workflow) {
$item = $workflow->getWorkflowMenuItem();
$item_href = urisprintf('install/%d/%s/', $dashboard->getID(), $key);
$item_href = $this->getApplicationURI($item_href);
$item->setHref($item_href);
$menu->addItem($item);
}
return $this->newDialog()
->setTitle(pht('Install Dashboard'))
->setErrors($errors)
->setTitle(pht('Add Dashboard to Menu'))
->setWidth(AphrontDialogView::WIDTH_FORM)
->appendChild($form->buildLayoutView())
->addCancelButton($cancel_uri)
->addSubmitButton(pht('Install Dashboard'));
->appendChild($menu)
->addCancelButton($cancel_uri);
}
}
......@@ -83,7 +83,7 @@ final class PhabricatorDashboardViewController
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Install Dashboard'))
->setName(pht('Add Dashboard to Menu'))
->setIcon('fa-wrench')
->setHref($this->getApplicationURI("/install/{$id}/"))
->setWorkflow(true));
......
<?php
abstract class PhabricatorDashboardApplicationInstallWorkflow
extends PhabricatorDashboardInstallWorkflow {
abstract protected function newApplication();
protected function canInstallToGlobalMenu() {
return PhabricatorPolicyFilter::hasCapability(
$this->getViewer(),
$this->newApplication(),
PhabricatorPolicyCapability::CAN_EDIT);
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$application = $this->newApplication();
$can_global = $this->canInstallToGlobalMenu();
switch ($this->getMode()) {
case 'global':
if (!$can_global) {
return $this->newGlobalPermissionDialog();
} else if ($request->isFormPost()) {
return $this->installDashboard($application, null);
} else {
return $this->newGlobalConfirmDialog();
}
case 'personal':
if ($request->isFormPost()) {
return $this->installDashboard($application, $viewer->getPHID());
} else {
return $this->newPersonalConfirmDialog();
}
}
$global_item = $this->newGlobalMenuItem()
->setDisabled(!$can_global);
$menu = $this->newMenuFromItemMap(
array(
'personal' => $this->newPersonalMenuItem(),
'global' => $global_item,
));
return $this->newApplicationModeDialog()
->appendChild($menu);
}
abstract protected function newGlobalPermissionDialog();
abstract protected function newGlobalConfirmDialog();
abstract protected function newPersonalConfirmDialog();
abstract protected function newPersonalMenuItem();
abstract protected function newGlobalMenuItem();
abstract protected function newApplicationModeDialog();
}
<?php
final class PhabricatorDashboardFavoritesInstallWorkflow
extends PhabricatorDashboardApplicationInstallWorkflow {
const WORKFLOWKEY = 'favorites';
public function getOrder() {
return 4000;
}
protected function newWorkflowMenuItem() {
return $this->newMenuItem()
->setHeader(pht('Add to Favorites Menu'))
->setImageIcon('fa-bookmark')
->addAttribute(
pht(
'Add this dashboard to the favorites menu in the main '.
'menu bar.'));
}
protected function newProfileEngine() {
return new PhabricatorFavoritesProfileMenuEngine();
}
protected function newApplication() {
return new PhabricatorFavoritesApplication();
}
protected function newApplicationModeDialog() {
return $this->newDialog()
->setTitle(pht('Add Dashboard to Favorites Menu'));
}
protected function newPersonalMenuItem() {
return $this->newMenuItem()
->setHeader(pht('Add to Personal Favorites'))
->setImageIcon('fa-user')
->addAttribute(
pht(
'Add this dashboard to your list of personal favorite menu items, '.
'visible to only you.'));
}
protected function newGlobalMenuItem() {
return $this->newMenuItem()
->setHeader(pht('Add to Global Favorites'))
->setImageIcon('fa-globe')
->addAttribute(
pht(
'Add this dashboard to the global favorites menu, visible to all '.
'users.'));
}
protected function newGlobalPermissionDialog() {
return $this->newDialog()
->setTitle(pht('No Permission'))
->appendParagraph(
pht(
'You do not have permission to install items on the global '.
'favorites menu.'));
}
protected function newGlobalConfirmDialog() {
return $this->newDialog()
->setTitle(pht('Add Dashboard to Global Favorites'))
->appendParagraph(
pht(
'Add dashboard %s as a global menu item in the favorites menu?',
$this->getDashboardDisplayName()))
->addSubmitButton(pht('Add to Favorites'));
}
protected function newPersonalConfirmDialog() {
return $this->newDialog()
->setTitle(pht('Add Dashboard to Personal Favorites'))
->appendParagraph(
pht(
'Add dashboard %s as a personal menu item in the favorites menu?',
$this->getDashboardDisplayName()))
->addSubmitButton(pht('Add to Favorites'));
}
}
<?php
final class PhabricatorDashboardHomeInstallWorkflow
extends PhabricatorDashboardApplicationInstallWorkflow {
const WORKFLOWKEY = 'home';
public function getOrder() {
return 1000;
}
protected function newWorkflowMenuItem() {
return $this->newMenuItem()
->setHeader(pht('Add to Home Page Menu'))
->setImageIcon('fa-home')
->addAttribute(
pht(
'Add this dashboard to the menu on the home page.'));
}
protected function newProfileEngine() {
return new PhabricatorHomeProfileMenuEngine();
}
protected function newApplication() {
return new PhabricatorHomeApplication();
}
protected function newApplicationModeDialog() {
return $this->newDialog()
->setTitle(pht('Add Dashboard to Home Menu'));
}
protected function newPersonalMenuItem() {
return $this->newMenuItem()
->setHeader(pht('Add to Personal Home Menu'))
->setImageIcon('fa-user')
->addAttribute(
pht(
'Add this dashboard to your list of personal home menu items, '.
'visible to only you.'));
}
protected function newGlobalMenuItem() {
return $this->newMenuItem()
->setHeader(pht('Add to Global Home Menu'))
->setImageIcon('fa-globe')
->addAttribute(
pht(
'Add this dashboard to the global home menu, visible to all '.
'users.'));
}
protected function newGlobalPermissionDialog() {
return $this->newDialog()
->setTitle(pht('No Permission'))
->appendParagraph(
pht(
'You do not have permission to install items on the global home '.
'menu.'));
}
protected function newGlobalConfirmDialog() {
return $this->newDialog()
->setTitle(pht('Add Dashboard to Global Home Page'))
->appendParagraph(
pht(
'Add dashboard %s as a global menu item on the home page?',
$this->getDashboardDisplayName()))
->addSubmitButton(pht('Add to Home'));
}
protected function newPersonalConfirmDialog() {
return $this->newDialog()
->setTitle(pht('Add Dashboard to Personal Home Page'))
->appendParagraph(
pht(
'Add dashboard %s as a personal menu item on your home page?',
$this->getDashboardDisplayName()))
->addSubmitButton(pht('Add to Home'));
}
}
<?php
abstract class PhabricatorDashboardInstallWorkflow
extends Phobject {
private $request;
private $viewer;
private $dashboard;
private $mode;