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

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

+728 -124
+6 -6
resources/celerity/map.php
··· 9 9 'names' => array( 10 10 'conpherence.pkg.css' => '3c8a0668', 11 11 'conpherence.pkg.js' => '020aebcf', 12 - 'core.pkg.css' => 'a1c2d49b', 12 + 'core.pkg.css' => '3b565a84', 13 13 'core.pkg.js' => 'a568e834', 14 14 'differential.pkg.css' => '8d8360fb', 15 15 'differential.pkg.js' => '67e02996', ··· 128 128 'rsrc/css/phui/calendar/phui-calendar-list.css' => 'ccd7e4e2', 129 129 'rsrc/css/phui/calendar/phui-calendar-month.css' => 'cb758c42', 130 130 'rsrc/css/phui/calendar/phui-calendar.css' => 'f11073aa', 131 - 'rsrc/css/phui/object-item/phui-oi-big-ui.css' => '534f1757', 131 + 'rsrc/css/phui/object-item/phui-oi-big-ui.css' => 'fa74cc35', 132 132 'rsrc/css/phui/object-item/phui-oi-color.css' => 'b517bfa0', 133 133 'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => 'da15d3dc', 134 134 'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '490e2e2e', ··· 849 849 'phui-lightbox-css' => '4ebf22da', 850 850 'phui-list-view-css' => '470b1adb', 851 851 'phui-object-box-css' => 'f434b6be', 852 - 'phui-oi-big-ui-css' => '534f1757', 852 + 'phui-oi-big-ui-css' => 'fa74cc35', 853 853 'phui-oi-color-css' => 'b517bfa0', 854 854 'phui-oi-drag-ui-css' => 'da15d3dc', 855 855 'phui-oi-flush-ui-css' => '490e2e2e', ··· 1379 1379 'javelin-dom', 1380 1380 'javelin-fx', 1381 1381 ), 1382 - '534f1757' => array( 1383 - 'phui-oi-list-view-css', 1384 - ), 1385 1382 '541f81c3' => array( 1386 1383 'javelin-install', 1387 1384 ), ··· 2178 2175 'javelin-quicksand', 2179 2176 'phabricator-keyboard-shortcut', 2180 2177 'conpherence-thread-manager', 2178 + ), 2179 + 'fa74cc35' => array( 2180 + 'phui-oi-list-view-css', 2181 2181 ), 2182 2182 'fdc13e4e' => array( 2183 2183 'javelin-install',
+16
src/__phutil_library_map__.php
··· 2905 2905 'PhabricatorDashboard' => 'applications/dashboard/storage/PhabricatorDashboard.php', 2906 2906 'PhabricatorDashboardAddPanelController' => 'applications/dashboard/controller/PhabricatorDashboardAddPanelController.php', 2907 2907 'PhabricatorDashboardApplication' => 'applications/dashboard/application/PhabricatorDashboardApplication.php', 2908 + 'PhabricatorDashboardApplicationInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardApplicationInstallWorkflow.php', 2908 2909 'PhabricatorDashboardArchiveController' => 'applications/dashboard/controller/dashboard/PhabricatorDashboardArchiveController.php', 2909 2910 'PhabricatorDashboardConsoleController' => 'applications/dashboard/controller/PhabricatorDashboardConsoleController.php', 2910 2911 'PhabricatorDashboardController' => 'applications/dashboard/controller/PhabricatorDashboardController.php', ··· 2913 2914 'PhabricatorDashboardDashboardPHIDType' => 'applications/dashboard/phid/PhabricatorDashboardDashboardPHIDType.php', 2914 2915 'PhabricatorDashboardDatasource' => 'applications/dashboard/typeahead/PhabricatorDashboardDatasource.php', 2915 2916 'PhabricatorDashboardEditController' => 'applications/dashboard/controller/dashboard/PhabricatorDashboardEditController.php', 2917 + 'PhabricatorDashboardFavoritesInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardFavoritesInstallWorkflow.php', 2918 + 'PhabricatorDashboardHomeInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardHomeInstallWorkflow.php', 2916 2919 'PhabricatorDashboardIconSet' => 'applications/dashboard/icon/PhabricatorDashboardIconSet.php', 2917 2920 'PhabricatorDashboardInstall' => 'applications/dashboard/storage/PhabricatorDashboardInstall.php', 2918 2921 'PhabricatorDashboardInstallController' => 'applications/dashboard/controller/dashboard/PhabricatorDashboardInstallController.php', 2922 + 'PhabricatorDashboardInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardInstallWorkflow.php', 2919 2923 'PhabricatorDashboardLayoutConfig' => 'applications/dashboard/layoutconfig/PhabricatorDashboardLayoutConfig.php', 2920 2924 'PhabricatorDashboardListController' => 'applications/dashboard/controller/PhabricatorDashboardListController.php', 2921 2925 'PhabricatorDashboardMovePanelController' => 'applications/dashboard/controller/PhabricatorDashboardMovePanelController.php', 2922 2926 'PhabricatorDashboardNgrams' => 'applications/dashboard/storage/PhabricatorDashboardNgrams.php', 2927 + 'PhabricatorDashboardObjectInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardObjectInstallWorkflow.php', 2923 2928 'PhabricatorDashboardPanel' => 'applications/dashboard/storage/PhabricatorDashboardPanel.php', 2924 2929 'PhabricatorDashboardPanelArchiveController' => 'applications/dashboard/controller/PhabricatorDashboardPanelArchiveController.php', 2925 2930 'PhabricatorDashboardPanelCoreCustomField' => 'applications/dashboard/customfield/PhabricatorDashboardPanelCoreCustomField.php', ··· 2947 2952 'PhabricatorDashboardPanelViewController' => 'applications/dashboard/controller/PhabricatorDashboardPanelViewController.php', 2948 2953 'PhabricatorDashboardPortal' => 'applications/dashboard/storage/PhabricatorDashboardPortal.php', 2949 2954 'PhabricatorDashboardPortalController' => 'applications/dashboard/controller/portal/PhabricatorDashboardPortalController.php', 2955 + 'PhabricatorDashboardPortalDatasource' => 'applications/dashboard/typeahead/PhabricatorDashboardPortalDatasource.php', 2950 2956 'PhabricatorDashboardPortalEditConduitAPIMethod' => 'applications/dashboard/conduit/PhabricatorDashboardPortalEditConduitAPIMethod.php', 2951 2957 'PhabricatorDashboardPortalEditController' => 'applications/dashboard/controller/portal/PhabricatorDashboardPortalEditController.php', 2952 2958 'PhabricatorDashboardPortalEditEngine' => 'applications/dashboard/editor/PhabricatorDashboardPortalEditEngine.php', 2953 2959 'PhabricatorDashboardPortalEditor' => 'applications/dashboard/editor/PhabricatorDashboardPortalEditor.php', 2960 + 'PhabricatorDashboardPortalInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardPortalInstallWorkflow.php', 2954 2961 'PhabricatorDashboardPortalListController' => 'applications/dashboard/controller/portal/PhabricatorDashboardPortalListController.php', 2955 2962 'PhabricatorDashboardPortalMenuItem' => 'applications/dashboard/menuitem/PhabricatorDashboardPortalMenuItem.php', 2956 2963 'PhabricatorDashboardPortalNameTransaction' => 'applications/dashboard/xaction/portal/PhabricatorDashboardPortalNameTransaction.php', ··· 2966 2973 'PhabricatorDashboardPortalViewController' => 'applications/dashboard/controller/portal/PhabricatorDashboardPortalViewController.php', 2967 2974 'PhabricatorDashboardProfileController' => 'applications/dashboard/controller/PhabricatorDashboardProfileController.php', 2968 2975 'PhabricatorDashboardProfileMenuItem' => 'applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php', 2976 + 'PhabricatorDashboardProjectInstallWorkflow' => 'applications/dashboard/install/PhabricatorDashboardProjectInstallWorkflow.php', 2969 2977 'PhabricatorDashboardQuery' => 'applications/dashboard/query/PhabricatorDashboardQuery.php', 2970 2978 'PhabricatorDashboardQueryPanelInstallController' => 'applications/dashboard/controller/PhabricatorDashboardQueryPanelInstallController.php', 2971 2979 'PhabricatorDashboardQueryPanelType' => 'applications/dashboard/paneltype/PhabricatorDashboardQueryPanelType.php', ··· 8865 8873 ), 8866 8874 'PhabricatorDashboardAddPanelController' => 'PhabricatorDashboardController', 8867 8875 'PhabricatorDashboardApplication' => 'PhabricatorApplication', 8876 + 'PhabricatorDashboardApplicationInstallWorkflow' => 'PhabricatorDashboardInstallWorkflow', 8868 8877 'PhabricatorDashboardArchiveController' => 'PhabricatorDashboardController', 8869 8878 'PhabricatorDashboardConsoleController' => 'PhabricatorDashboardController', 8870 8879 'PhabricatorDashboardController' => 'PhabricatorController', ··· 8873 8882 'PhabricatorDashboardDashboardPHIDType' => 'PhabricatorPHIDType', 8874 8883 'PhabricatorDashboardDatasource' => 'PhabricatorTypeaheadDatasource', 8875 8884 'PhabricatorDashboardEditController' => 'PhabricatorDashboardController', 8885 + 'PhabricatorDashboardFavoritesInstallWorkflow' => 'PhabricatorDashboardApplicationInstallWorkflow', 8886 + 'PhabricatorDashboardHomeInstallWorkflow' => 'PhabricatorDashboardApplicationInstallWorkflow', 8876 8887 'PhabricatorDashboardIconSet' => 'PhabricatorIconSet', 8877 8888 'PhabricatorDashboardInstall' => 'PhabricatorDashboardDAO', 8878 8889 'PhabricatorDashboardInstallController' => 'PhabricatorDashboardController', 8890 + 'PhabricatorDashboardInstallWorkflow' => 'Phobject', 8879 8891 'PhabricatorDashboardLayoutConfig' => 'Phobject', 8880 8892 'PhabricatorDashboardListController' => 'PhabricatorDashboardController', 8881 8893 'PhabricatorDashboardMovePanelController' => 'PhabricatorDashboardController', 8882 8894 'PhabricatorDashboardNgrams' => 'PhabricatorSearchNgrams', 8895 + 'PhabricatorDashboardObjectInstallWorkflow' => 'PhabricatorDashboardInstallWorkflow', 8883 8896 'PhabricatorDashboardPanel' => array( 8884 8897 'PhabricatorDashboardDAO', 8885 8898 'PhabricatorApplicationTransactionInterface', ··· 8923 8936 'PhabricatorDestructibleInterface', 8924 8937 ), 8925 8938 'PhabricatorDashboardPortalController' => 'PhabricatorDashboardController', 8939 + 'PhabricatorDashboardPortalDatasource' => 'PhabricatorTypeaheadDatasource', 8926 8940 'PhabricatorDashboardPortalEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 8927 8941 'PhabricatorDashboardPortalEditController' => 'PhabricatorDashboardPortalController', 8928 8942 'PhabricatorDashboardPortalEditEngine' => 'PhabricatorEditEngine', 8929 8943 'PhabricatorDashboardPortalEditor' => 'PhabricatorApplicationTransactionEditor', 8944 + 'PhabricatorDashboardPortalInstallWorkflow' => 'PhabricatorDashboardObjectInstallWorkflow', 8930 8945 'PhabricatorDashboardPortalListController' => 'PhabricatorDashboardPortalController', 8931 8946 'PhabricatorDashboardPortalMenuItem' => 'PhabricatorProfileMenuItem', 8932 8947 'PhabricatorDashboardPortalNameTransaction' => 'PhabricatorDashboardPortalTransactionType', ··· 8942 8957 'PhabricatorDashboardPortalViewController' => 'PhabricatorDashboardPortalController', 8943 8958 'PhabricatorDashboardProfileController' => 'PhabricatorController', 8944 8959 'PhabricatorDashboardProfileMenuItem' => 'PhabricatorProfileMenuItem', 8960 + 'PhabricatorDashboardProjectInstallWorkflow' => 'PhabricatorDashboardObjectInstallWorkflow', 8945 8961 'PhabricatorDashboardQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 8946 8962 'PhabricatorDashboardQueryPanelInstallController' => 'PhabricatorDashboardController', 8947 8963 'PhabricatorDashboardQueryPanelType' => 'PhabricatorDashboardPanelType',
+4 -1
src/applications/dashboard/application/PhabricatorDashboardApplication.php
··· 43 43 'archive/(?P<id>\d+)/' => 'PhabricatorDashboardArchiveController', 44 44 'create/' => 'PhabricatorDashboardEditController', 45 45 'edit/(?:(?P<id>\d+)/)?' => 'PhabricatorDashboardEditController', 46 - 'install/(?:(?P<id>\d+)/)?' => 'PhabricatorDashboardInstallController', 46 + 'install/(?P<id>\d+)/'. 47 + '(?:(?P<workflowKey>[^/]+)/'. 48 + '(?:(?P<modeKey>[^/]+)/)?)?' => 49 + 'PhabricatorDashboardInstallController', 47 50 'console/' => 'PhabricatorDashboardConsoleController', 48 51 'addpanel/(?P<id>\d+)/' => 'PhabricatorDashboardAddPanelController', 49 52 'movepanel/(?P<id>\d+)/' => 'PhabricatorDashboardMovePanelController',
+42 -107
src/applications/dashboard/controller/dashboard/PhabricatorDashboardInstallController.php
··· 3 3 final class PhabricatorDashboardInstallController 4 4 extends PhabricatorDashboardController { 5 5 6 + private $dashboard; 7 + 8 + public function setDashboard(PhabricatorDashboard $dashboard) { 9 + $this->dashboard = $dashboard; 10 + return $this; 11 + } 12 + 13 + public function getDashboard() { 14 + return $this->dashboard; 15 + } 16 + 6 17 public function handleRequest(AphrontRequest $request) { 7 18 $viewer = $request->getViewer(); 8 19 $id = $request->getURIData('id'); ··· 15 26 return new Aphront404Response(); 16 27 } 17 28 29 + $this->setDashboard($dashboard); 18 30 $cancel_uri = $dashboard->getURI(); 19 31 20 - $home_app = new PhabricatorHomeApplication(); 32 + $workflow_key = $request->getURIData('workflowKey'); 21 33 22 - $options = array(); 23 - $options['home'] = array( 24 - 'personal' => 25 - array( 26 - 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 27 - 'application' => $home_app, 28 - 'name' => pht('Personal Dashboard'), 29 - 'value' => 'personal', 30 - 'description' => pht('Places this dashboard as a menu item on home '. 31 - 'as a personal menu item. It will only be on your personal '. 32 - 'home.'), 33 - ), 34 - 'global' => 35 - array( 36 - 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 37 - 'application' => $home_app, 38 - 'name' => pht('Global Dashboard'), 39 - 'value' => 'global', 40 - 'description' => pht('Places this dashboard as a menu item on home '. 41 - 'as a global menu item. It will be available to all users.'), 42 - ), 43 - ); 34 + $workflows = PhabricatorDashboardInstallWorkflow::getAllWorkflows(); 35 + if (!isset($workflows[$workflow_key])) { 36 + return $this->newWorkflowDialog($dashboard, $workflows); 37 + } 44 38 39 + return id(clone $workflows[$workflow_key]) 40 + ->setRequest($request) 41 + ->setViewer($viewer) 42 + ->setDashboard($dashboard) 43 + ->setMode($request->getURIData('modeKey')) 44 + ->handleRequest($request); 45 + } 45 46 46 - $errors = array(); 47 - $v_name = null; 48 - if ($request->isFormPost()) { 49 - $menuitem = new PhabricatorDashboardProfileMenuItem(); 50 - $dashboard_phid = $dashboard->getPHID(); 51 - $home = new PhabricatorHomeApplication(); 52 - $v_name = $request->getStr('name'); 53 - $v_home = $request->getStr('home'); 47 + private function newWorkflowDialog( 48 + PhabricatorDashboard $dashboard, 49 + array $workflows) { 50 + $viewer = $this->getViewer(); 51 + $cancel_uri = $dashboard->getURI(); 54 52 55 - if ($v_home) { 56 - $application = $options['home'][$v_home]['application']; 57 - $capability = $options['home'][$v_home]['capability']; 53 + $menu = id(new PHUIObjectItemListView()) 54 + ->setViewer($viewer) 55 + ->setFlush(true) 56 + ->setBig(true); 58 57 59 - $can_edit_home = PhabricatorPolicyFilter::hasCapability( 60 - $viewer, 61 - $application, 62 - $capability); 58 + foreach ($workflows as $key => $workflow) { 59 + $item = $workflow->getWorkflowMenuItem(); 63 60 64 - if (!$can_edit_home) { 65 - $errors[] = pht( 66 - 'You do not have permission to install a dashboard on home.'); 67 - } 68 - } else { 69 - $errors[] = pht( 70 - 'You must select a destination to install this dashboard.'); 71 - } 72 - 73 - $v_phid = $viewer->getPHID(); 74 - if ($v_home == 'global') { 75 - $v_phid = null; 76 - } 77 - 78 - if (!$errors) { 79 - $install = PhabricatorProfileMenuItemConfiguration::initializeNewItem( 80 - $home, 81 - $menuitem, 82 - $v_phid); 83 - 84 - $install->setMenuItemProperty('dashboardPHID', $dashboard_phid); 85 - $install->setMenuItemProperty('name', $v_name); 86 - $install->setMenuItemOrder(1); 87 - 88 - $xactions = array(); 89 - 90 - $editor = id(new PhabricatorProfileMenuEditor()) 91 - ->setActor($viewer) 92 - ->setContinueOnNoEffect(true) 93 - ->setContinueOnMissingFields(true) 94 - ->setContentSourceFromRequest($request); 95 - 96 - $editor->applyTransactions($install, $xactions); 97 - 98 - $view_uri = '/home/menu/view/'.$install->getID().'/'; 99 - 100 - return id(new AphrontRedirectResponse())->setURI($view_uri); 101 - } 102 - } 61 + $item_href = urisprintf('install/%d/%s/', $dashboard->getID(), $key); 62 + $item_href = $this->getApplicationURI($item_href); 63 + $item->setHref($item_href); 103 64 104 - $form = id(new AphrontFormView()) 105 - ->setUser($viewer) 106 - ->appendChild( 107 - id(new AphrontFormTextControl()) 108 - ->setLabel(pht('Menu Label')) 109 - ->setName('name') 110 - ->setValue($v_name)); 111 - 112 - $radio = id(new AphrontFormRadioButtonControl()) 113 - ->setLabel(pht('Home Menu')) 114 - ->setName('home'); 115 - 116 - foreach ($options['home'] as $type => $option) { 117 - $can_edit = PhabricatorPolicyFilter::hasCapability( 118 - $viewer, 119 - $option['application'], 120 - $option['capability']); 121 - if ($can_edit) { 122 - $radio->addButton( 123 - $option['value'], 124 - $option['name'], 125 - $option['description']); 126 - } 65 + $menu->addItem($item); 127 66 } 128 67 129 - $form->appendChild($radio); 130 - 131 68 return $this->newDialog() 132 - ->setTitle(pht('Install Dashboard')) 133 - ->setErrors($errors) 69 + ->setTitle(pht('Add Dashboard to Menu')) 134 70 ->setWidth(AphrontDialogView::WIDTH_FORM) 135 - ->appendChild($form->buildLayoutView()) 136 - ->addCancelButton($cancel_uri) 137 - ->addSubmitButton(pht('Install Dashboard')); 71 + ->appendChild($menu) 72 + ->addCancelButton($cancel_uri); 138 73 } 139 74 140 75 }
+1 -1
src/applications/dashboard/controller/dashboard/PhabricatorDashboardViewController.php
··· 83 83 84 84 $curtain->addAction( 85 85 id(new PhabricatorActionView()) 86 - ->setName(pht('Install Dashboard')) 86 + ->setName(pht('Add Dashboard to Menu')) 87 87 ->setIcon('fa-wrench') 88 88 ->setHref($this->getApplicationURI("/install/{$id}/")) 89 89 ->setWorkflow(true));
+58
src/applications/dashboard/install/PhabricatorDashboardApplicationInstallWorkflow.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorDashboardApplicationInstallWorkflow 4 + extends PhabricatorDashboardInstallWorkflow { 5 + 6 + abstract protected function newApplication(); 7 + 8 + protected function canInstallToGlobalMenu() { 9 + return PhabricatorPolicyFilter::hasCapability( 10 + $this->getViewer(), 11 + $this->newApplication(), 12 + PhabricatorPolicyCapability::CAN_EDIT); 13 + } 14 + 15 + public function handleRequest(AphrontRequest $request) { 16 + $viewer = $this->getViewer(); 17 + $application = $this->newApplication(); 18 + $can_global = $this->canInstallToGlobalMenu(); 19 + 20 + switch ($this->getMode()) { 21 + case 'global': 22 + if (!$can_global) { 23 + return $this->newGlobalPermissionDialog(); 24 + } else if ($request->isFormPost()) { 25 + return $this->installDashboard($application, null); 26 + } else { 27 + return $this->newGlobalConfirmDialog(); 28 + } 29 + case 'personal': 30 + if ($request->isFormPost()) { 31 + return $this->installDashboard($application, $viewer->getPHID()); 32 + } else { 33 + return $this->newPersonalConfirmDialog(); 34 + } 35 + } 36 + 37 + $global_item = $this->newGlobalMenuItem() 38 + ->setDisabled(!$can_global); 39 + 40 + $menu = $this->newMenuFromItemMap( 41 + array( 42 + 'personal' => $this->newPersonalMenuItem(), 43 + 'global' => $global_item, 44 + )); 45 + 46 + return $this->newApplicationModeDialog() 47 + ->appendChild($menu); 48 + } 49 + 50 + abstract protected function newGlobalPermissionDialog(); 51 + abstract protected function newGlobalConfirmDialog(); 52 + abstract protected function newPersonalConfirmDialog(); 53 + 54 + abstract protected function newPersonalMenuItem(); 55 + abstract protected function newGlobalMenuItem(); 56 + abstract protected function newApplicationModeDialog(); 57 + 58 + }
+85
src/applications/dashboard/install/PhabricatorDashboardFavoritesInstallWorkflow.php
··· 1 + <?php 2 + 3 + final class PhabricatorDashboardFavoritesInstallWorkflow 4 + extends PhabricatorDashboardApplicationInstallWorkflow { 5 + 6 + const WORKFLOWKEY = 'favorites'; 7 + 8 + public function getOrder() { 9 + return 4000; 10 + } 11 + 12 + protected function newWorkflowMenuItem() { 13 + return $this->newMenuItem() 14 + ->setHeader(pht('Add to Favorites Menu')) 15 + ->setImageIcon('fa-bookmark') 16 + ->addAttribute( 17 + pht( 18 + 'Add this dashboard to the favorites menu in the main '. 19 + 'menu bar.')); 20 + } 21 + 22 + protected function newProfileEngine() { 23 + return new PhabricatorFavoritesProfileMenuEngine(); 24 + } 25 + 26 + protected function newApplication() { 27 + return new PhabricatorFavoritesApplication(); 28 + } 29 + 30 + protected function newApplicationModeDialog() { 31 + return $this->newDialog() 32 + ->setTitle(pht('Add Dashboard to Favorites Menu')); 33 + } 34 + 35 + protected function newPersonalMenuItem() { 36 + return $this->newMenuItem() 37 + ->setHeader(pht('Add to Personal Favorites')) 38 + ->setImageIcon('fa-user') 39 + ->addAttribute( 40 + pht( 41 + 'Add this dashboard to your list of personal favorite menu items, '. 42 + 'visible to only you.')); 43 + } 44 + 45 + protected function newGlobalMenuItem() { 46 + return $this->newMenuItem() 47 + ->setHeader(pht('Add to Global Favorites')) 48 + ->setImageIcon('fa-globe') 49 + ->addAttribute( 50 + pht( 51 + 'Add this dashboard to the global favorites menu, visible to all '. 52 + 'users.')); 53 + } 54 + 55 + protected function newGlobalPermissionDialog() { 56 + return $this->newDialog() 57 + ->setTitle(pht('No Permission')) 58 + ->appendParagraph( 59 + pht( 60 + 'You do not have permission to install items on the global '. 61 + 'favorites menu.')); 62 + } 63 + 64 + protected function newGlobalConfirmDialog() { 65 + return $this->newDialog() 66 + ->setTitle(pht('Add Dashboard to Global Favorites')) 67 + ->appendParagraph( 68 + pht( 69 + 'Add dashboard %s as a global menu item in the favorites menu?', 70 + $this->getDashboardDisplayName())) 71 + ->addSubmitButton(pht('Add to Favorites')); 72 + } 73 + 74 + protected function newPersonalConfirmDialog() { 75 + return $this->newDialog() 76 + ->setTitle(pht('Add Dashboard to Personal Favorites')) 77 + ->appendParagraph( 78 + pht( 79 + 'Add dashboard %s as a personal menu item in the favorites menu?', 80 + $this->getDashboardDisplayName())) 81 + ->addSubmitButton(pht('Add to Favorites')); 82 + } 83 + 84 + 85 + }
+83
src/applications/dashboard/install/PhabricatorDashboardHomeInstallWorkflow.php
··· 1 + <?php 2 + 3 + final class PhabricatorDashboardHomeInstallWorkflow 4 + extends PhabricatorDashboardApplicationInstallWorkflow { 5 + 6 + const WORKFLOWKEY = 'home'; 7 + 8 + public function getOrder() { 9 + return 1000; 10 + } 11 + 12 + protected function newWorkflowMenuItem() { 13 + return $this->newMenuItem() 14 + ->setHeader(pht('Add to Home Page Menu')) 15 + ->setImageIcon('fa-home') 16 + ->addAttribute( 17 + pht( 18 + 'Add this dashboard to the menu on the home page.')); 19 + } 20 + 21 + protected function newProfileEngine() { 22 + return new PhabricatorHomeProfileMenuEngine(); 23 + } 24 + 25 + protected function newApplication() { 26 + return new PhabricatorHomeApplication(); 27 + } 28 + 29 + protected function newApplicationModeDialog() { 30 + return $this->newDialog() 31 + ->setTitle(pht('Add Dashboard to Home Menu')); 32 + } 33 + 34 + protected function newPersonalMenuItem() { 35 + return $this->newMenuItem() 36 + ->setHeader(pht('Add to Personal Home Menu')) 37 + ->setImageIcon('fa-user') 38 + ->addAttribute( 39 + pht( 40 + 'Add this dashboard to your list of personal home menu items, '. 41 + 'visible to only you.')); 42 + } 43 + 44 + protected function newGlobalMenuItem() { 45 + return $this->newMenuItem() 46 + ->setHeader(pht('Add to Global Home Menu')) 47 + ->setImageIcon('fa-globe') 48 + ->addAttribute( 49 + pht( 50 + 'Add this dashboard to the global home menu, visible to all '. 51 + 'users.')); 52 + } 53 + 54 + protected function newGlobalPermissionDialog() { 55 + return $this->newDialog() 56 + ->setTitle(pht('No Permission')) 57 + ->appendParagraph( 58 + pht( 59 + 'You do not have permission to install items on the global home '. 60 + 'menu.')); 61 + } 62 + 63 + protected function newGlobalConfirmDialog() { 64 + return $this->newDialog() 65 + ->setTitle(pht('Add Dashboard to Global Home Page')) 66 + ->appendParagraph( 67 + pht( 68 + 'Add dashboard %s as a global menu item on the home page?', 69 + $this->getDashboardDisplayName())) 70 + ->addSubmitButton(pht('Add to Home')); 71 + } 72 + 73 + protected function newPersonalConfirmDialog() { 74 + return $this->newDialog() 75 + ->setTitle(pht('Add Dashboard to Personal Home Page')) 76 + ->appendParagraph( 77 + pht( 78 + 'Add dashboard %s as a personal menu item on your home page?', 79 + $this->getDashboardDisplayName())) 80 + ->addSubmitButton(pht('Add to Home')); 81 + } 82 + 83 + }
+143
src/applications/dashboard/install/PhabricatorDashboardInstallWorkflow.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorDashboardInstallWorkflow 4 + extends Phobject { 5 + 6 + private $request; 7 + private $viewer; 8 + private $dashboard; 9 + private $mode; 10 + 11 + final public function setViewer(PhabricatorUser $viewer) { 12 + $this->viewer = $viewer; 13 + return $this; 14 + } 15 + 16 + final public function getViewer() { 17 + return $this->viewer; 18 + } 19 + 20 + final public function setDashboard(PhabricatorDashboard $dashboard) { 21 + $this->dashboard = $dashboard; 22 + return $this; 23 + } 24 + 25 + final public function getDashboard() { 26 + return $this->dashboard; 27 + } 28 + 29 + final public function setMode($mode) { 30 + $this->mode = $mode; 31 + return $this; 32 + } 33 + 34 + final public function getMode() { 35 + return $this->mode; 36 + } 37 + 38 + final public function setRequest(AphrontRequest $request) { 39 + $this->request = $request; 40 + return $this; 41 + } 42 + 43 + final public function getRequest() { 44 + return $this->request; 45 + } 46 + 47 + final public function getWorkflowKey() { 48 + return $this->getPhobjectClassConstant('WORKFLOWKEY', 32); 49 + } 50 + 51 + final public static function getAllWorkflows() { 52 + return id(new PhutilClassMapQuery()) 53 + ->setAncestorClass(__CLASS__) 54 + ->setUniqueMethod('getWorkflowKey') 55 + ->setSortMethod('getOrder') 56 + ->execute(); 57 + } 58 + 59 + final public function getWorkflowMenuItem() { 60 + return $this->newWorkflowMenuItem(); 61 + } 62 + 63 + abstract public function getOrder(); 64 + abstract protected function newWorkflowMenuItem(); 65 + 66 + final protected function newMenuItem() { 67 + return id(new PHUIObjectItemView()) 68 + ->setClickable(true); 69 + } 70 + 71 + abstract public function handleRequest(AphrontRequest $request); 72 + 73 + final protected function newDialog() { 74 + $dashboard = $this->getDashboard(); 75 + 76 + return id(new AphrontDialogView()) 77 + ->setViewer($this->getViewer()) 78 + ->setWidth(AphrontDialogView::WIDTH_FORM) 79 + ->addCancelButton($dashboard->getURI()); 80 + } 81 + 82 + final protected function newMenuFromItemMap(array $map) { 83 + $viewer = $this->getViewer(); 84 + $dashboard = $this->getDashboard(); 85 + 86 + $menu = id(new PHUIObjectItemListView()) 87 + ->setViewer($viewer) 88 + ->setFlush(true) 89 + ->setBig(true); 90 + 91 + foreach ($map as $key => $item) { 92 + $item->setHref( 93 + urisprintf( 94 + '/dashboard/install/%d/%s/%s/', 95 + $dashboard->getID(), 96 + $this->getWorkflowKey(), 97 + $key)); 98 + 99 + $menu->addItem($item); 100 + } 101 + 102 + return $menu; 103 + } 104 + 105 + abstract protected function newProfileEngine(); 106 + 107 + final protected function installDashboard($profile_object, $custom_phid) { 108 + $dashboard = $this->getDashboard(); 109 + $engine = $this->newProfileEngine() 110 + ->setProfileObject($profile_object); 111 + 112 + $request = $this->getRequest(); 113 + $viewer = $this->getViewer(); 114 + 115 + $config = PhabricatorProfileMenuItemConfiguration::initializeNewItem( 116 + $profile_object, 117 + new PhabricatorDashboardProfileMenuItem(), 118 + $custom_phid); 119 + 120 + $config->setMenuItemProperty('dashboardPHID', $dashboard->getPHID()); 121 + 122 + $xactions = array(); 123 + 124 + $editor = id(new PhabricatorProfileMenuEditor()) 125 + ->setActor($viewer) 126 + ->setContinueOnNoEffect(true) 127 + ->setContinueOnMissingFields(true) 128 + ->setContentSourceFromRequest($request); 129 + 130 + $editor->applyTransactions($config, $xactions); 131 + 132 + $done_uri = $engine->getItemURI(urisprintf('view/%d/', $config->getID())); 133 + 134 + return id(new AphrontRedirectResponse()) 135 + ->setURI($done_uri); 136 + } 137 + 138 + final protected function getDashboardDisplayName() { 139 + $dashboard = $this->getDashboard(); 140 + return phutil_tag('strong', array(), $dashboard->getName()); 141 + } 142 + 143 + }
+99
src/applications/dashboard/install/PhabricatorDashboardObjectInstallWorkflow.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorDashboardObjectInstallWorkflow 4 + extends PhabricatorDashboardInstallWorkflow { 5 + 6 + abstract protected function newQuery(); 7 + abstract protected function newConfirmDialog($object); 8 + abstract protected function newObjectSelectionForm($object); 9 + 10 + public function handleRequest(AphrontRequest $request) { 11 + $viewer = $this->getViewer(); 12 + 13 + $target_identifier = null; 14 + 15 + $target_tokens = $request->getArr('target'); 16 + if ($target_tokens) { 17 + $target_identifier = head($target_tokens); 18 + } 19 + 20 + if (!strlen($target_identifier)) { 21 + $target_identifier = $request->getStr('target'); 22 + } 23 + 24 + if (!strlen($target_identifier)) { 25 + $target_identifier = $this->getMode(); 26 + } 27 + 28 + $target = null; 29 + if (strlen($target_identifier)) { 30 + $targets = array(); 31 + 32 + if (ctype_digit($target_identifier)) { 33 + $targets = $this->newQuery() 34 + ->setViewer($viewer) 35 + ->withIDs(array((int)$target_identifier)) 36 + ->execute(); 37 + } 38 + 39 + if (!$targets) { 40 + $targets = $this->newQuery() 41 + ->setViewer($viewer) 42 + ->withPHIDs(array($target_identifier)) 43 + ->execute(); 44 + } 45 + 46 + if ($targets) { 47 + $target = head($targets); 48 + } 49 + } 50 + 51 + if ($target) { 52 + $target_phid = $target->getPHID(); 53 + } else { 54 + $target_phid = null; 55 + } 56 + 57 + if ($target) { 58 + $can_edit = PhabricatorPolicyFilter::hasCapability( 59 + $viewer, 60 + $target, 61 + PhabricatorPolicyCapability::CAN_EDIT); 62 + } else { 63 + $can_edit = null; 64 + } 65 + 66 + if ($request->isFormPost() && $target && $can_edit) { 67 + if ($request->getBool('confirm')) { 68 + return $this->installDashboard($target, null); 69 + } else { 70 + return $this->newConfirmDialog($target) 71 + ->addHiddenInput('confirm', 1) 72 + ->addHiddenInput('target', $target_phid); 73 + } 74 + } 75 + 76 + $errors = array(); 77 + if (strlen($target_identifier)) { 78 + if (!$target) { 79 + $errors[] = pht('Choose a valid object.'); 80 + } else if (!$can_edit) { 81 + $errors[] = pht( 82 + 'You do not have permission to edit the selected object. '. 83 + 'You can only install dashboards on objects you can edit.'); 84 + } 85 + } else if ($request->getBool('pick')) { 86 + $errors[] = pht( 87 + 'Choose an object to install this dashboard on.'); 88 + } 89 + 90 + $form = $this->newObjectSelectionForm($target) 91 + ->addHiddenInput('pick', 1); 92 + 93 + return $this->newDialog() 94 + ->setTitle(pht('Add Dashboard to Project Menu')) 95 + ->setErrors($errors) 96 + ->appendForm($form) 97 + ->addSubmitButton(pht('Continue')); 98 + } 99 + }
+62
src/applications/dashboard/install/PhabricatorDashboardPortalInstallWorkflow.php
··· 1 + <?php 2 + 3 + final class PhabricatorDashboardPortalInstallWorkflow 4 + extends PhabricatorDashboardObjectInstallWorkflow { 5 + 6 + const WORKFLOWKEY = 'portal'; 7 + 8 + public function getOrder() { 9 + return 2000; 10 + } 11 + 12 + protected function newWorkflowMenuItem() { 13 + return $this->newMenuItem() 14 + ->setHeader(pht('Add to Portal Menu')) 15 + ->setImageIcon('fa-compass') 16 + ->addAttribute( 17 + pht('Add this dashboard to the menu on a portal.')); 18 + } 19 + 20 + protected function newProfileEngine() { 21 + return new PhabricatorDashboardPortalProfileMenuEngine(); 22 + } 23 + 24 + protected function newQuery() { 25 + return new PhabricatorDashboardPortalQuery(); 26 + } 27 + 28 + protected function newConfirmDialog($object) { 29 + return $this->newDialog() 30 + ->setTitle(pht('Add Dashboard to Portal Menu')) 31 + ->appendParagraph( 32 + pht( 33 + 'Add the dashboard %s to portal %s?', 34 + $this->getDashboardDisplayName(), 35 + phutil_tag('strong', array(), $object->getName()))) 36 + ->addSubmitButton(pht('Add to Portal')); 37 + } 38 + protected function newObjectSelectionForm($object) { 39 + $viewer = $this->getViewer(); 40 + 41 + if ($object) { 42 + $tokenizer_value = array($object->getPHID()); 43 + } else { 44 + $tokenizer_value = array(); 45 + } 46 + 47 + return id(new AphrontFormView()) 48 + ->setViewer($viewer) 49 + ->appendInstructions( 50 + pht( 51 + 'Select which portal you want to add the dashboard %s to.', 52 + $this->getDashboardDisplayName())) 53 + ->appendControl( 54 + id(new AphrontFormTokenizerControl()) 55 + ->setName('target') 56 + ->setLimit(1) 57 + ->setLabel(pht('Add to Portal')) 58 + ->setValue($tokenizer_value) 59 + ->setDatasource(new PhabricatorDashboardPortalDatasource())); 60 + } 61 + 62 + }
+63
src/applications/dashboard/install/PhabricatorDashboardProjectInstallWorkflow.php
··· 1 + <?php 2 + 3 + final class PhabricatorDashboardProjectInstallWorkflow 4 + extends PhabricatorDashboardObjectInstallWorkflow { 5 + 6 + const WORKFLOWKEY = 'project'; 7 + 8 + public function getOrder() { 9 + return 3000; 10 + } 11 + 12 + protected function newWorkflowMenuItem() { 13 + return $this->newMenuItem() 14 + ->setHeader(pht('Add to Project Menu')) 15 + ->setImageIcon('fa-briefcase') 16 + ->addAttribute( 17 + pht('Add this dashboard to the menu for a project.')); 18 + } 19 + 20 + protected function newProfileEngine() { 21 + return new PhabricatorProjectProfileMenuEngine(); 22 + } 23 + 24 + protected function newQuery() { 25 + return new PhabricatorProjectQuery(); 26 + } 27 + 28 + protected function newConfirmDialog($object) { 29 + return $this->newDialog() 30 + ->setTitle(pht('Add Dashboard to Project Menu')) 31 + ->appendParagraph( 32 + pht( 33 + 'Add the dashboard %s to the menu for project %s?', 34 + $this->getDashboardDisplayName(), 35 + phutil_tag('strong', array(), $object->getName()))) 36 + ->addSubmitButton(pht('Add to Project')); 37 + } 38 + 39 + protected function newObjectSelectionForm($object) { 40 + $viewer = $this->getViewer(); 41 + 42 + if ($object) { 43 + $tokenizer_value = array($object->getPHID()); 44 + } else { 45 + $tokenizer_value = array(); 46 + } 47 + 48 + return id(new AphrontFormView()) 49 + ->setViewer($viewer) 50 + ->appendInstructions( 51 + pht( 52 + 'Select which project menu you want to add the dashboard %s to.', 53 + $this->getDashboardDisplayName())) 54 + ->appendControl( 55 + id(new AphrontFormTokenizerControl()) 56 + ->setName('target') 57 + ->setLimit(1) 58 + ->setLabel(pht('Add to Project')) 59 + ->setValue($tokenizer_value) 60 + ->setDatasource(new PhabricatorProjectDatasource())); 61 + } 62 + 63 + }
+47
src/applications/dashboard/typeahead/PhabricatorDashboardPortalDatasource.php
··· 1 + <?php 2 + 3 + final class PhabricatorDashboardPortalDatasource 4 + extends PhabricatorTypeaheadDatasource { 5 + 6 + public function getBrowseTitle() { 7 + return pht('Browse Portals'); 8 + } 9 + 10 + public function getPlaceholderText() { 11 + return pht('Type a portal name...'); 12 + } 13 + 14 + public function getDatasourceApplicationClass() { 15 + return 'PhabricatorDashboardApplication'; 16 + } 17 + 18 + public function loadResults() { 19 + $results = $this->buildResults(); 20 + return $this->filterResultsAgainstTokens($results); 21 + } 22 + 23 + protected function renderSpecialTokens(array $values) { 24 + return $this->renderTokensFromResults($this->buildResults(), $values); 25 + } 26 + 27 + public function buildResults() { 28 + $query = new PhabricatorDashboardPortalQuery(); 29 + 30 + // TODO: Actually query by name so this scales past 100 portals. 31 + 32 + $portals = $this->executeQuery($query); 33 + 34 + $results = array(); 35 + foreach ($portals as $portal) { 36 + $result = id(new PhabricatorTypeaheadResult()) 37 + ->setName($portal->getObjectName().' '.$portal->getName()) 38 + ->setPHID($portal->getPHID()) 39 + ->setIcon('fa-compass'); 40 + 41 + $results[] = $result; 42 + } 43 + 44 + return $results; 45 + } 46 + 47 + }
+3 -2
src/applications/project/menuitem/PhabricatorProjectPointsProfileMenuItem.php
··· 165 165 ), 166 166 $bar); 167 167 168 - $item = $this->newItemView() 169 - ->newProgressBar($bar); 168 + $item = $this->newItemView(); 169 + 170 + $item->newProgressBar($bar); 170 171 171 172 return array( 172 173 $item,
-1
src/applications/search/menuitem/PhabricatorProfileMenuItem.php
··· 5 5 private $viewer; 6 6 private $engine; 7 7 8 - 9 8 public function getMenuItemTypeIcon() { 10 9 return null; 11 10 }
+2
src/view/phui/PHUIObjectItemView.php
··· 299 299 300 300 if ($this->disabled) { 301 301 $item_classes[] = 'phui-oi-disabled'; 302 + } else { 303 + $item_classes[] = 'phui-oi-enabled'; 302 304 } 303 305 304 306 switch ($this->effect) {
+14 -6
webroot/rsrc/css/phui/object-item/phui-oi-big-ui.css
··· 70 70 } 71 71 72 72 .phui-oi-list-big .phui-oi-linked-container { 73 - border: 1px solid {$lightblueborder}; 73 + border-width: 1px; 74 + border-style: solid; 74 75 border-radius: 4px; 75 - box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.035); 76 76 } 77 77 78 - .phui-oi-list-big .phui-oi-disabled { 79 - border-radius: 4px; 80 - background: {$lightgreybackground}; 78 + .phui-oi-list-big .phui-oi-enabled.phui-oi-linked-container { 79 + border-color: {$lightblueborder}; 80 + box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.05); 81 + } 82 + 83 + .phui-oi-list-big .phui-oi-disabled.phui-oi-linked-container { 84 + border-color: {$greybackground}; 85 + } 86 + 87 + .phui-oi-list-big .phui-oi-disabled .phui-oi-image-icon .phui-icon-view { 88 + color: {$darkgreybackground}; 81 89 } 82 90 83 91 .device-desktop .phui-oi-linked-container { 84 92 cursor: pointer; 85 93 } 86 94 87 - .device-desktop .phui-oi-linked-container:hover { 95 + .device-desktop .phui-oi-enabled.phui-oi-linked-container:hover { 88 96 background-color: {$hoverblue}; 89 97 border-color: {$blueborder}; 90 98 }