Allow applications to have multiple "help" menu items
Summary:
Ref T7199. Convert the single help menu item into a dropdown and allow applications to list multiple items there.
When an application has mail command objects, link them in the menu.
Test Plan:
{F355925}
{F355926}
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T7199
Differential Revision: https://secure.phabricator.com/D12244
			
			
This commit is contained in:
		| @@ -1894,6 +1894,7 @@ phutil_register_library_map(array( | ||||
|     'PhabricatorHashTestCase' => 'infrastructure/util/__tests__/PhabricatorHashTestCase.php', | ||||
|     'PhabricatorHelpApplication' => 'applications/help/application/PhabricatorHelpApplication.php', | ||||
|     'PhabricatorHelpController' => 'applications/help/controller/PhabricatorHelpController.php', | ||||
|     'PhabricatorHelpDocumentationController' => 'applications/help/controller/PhabricatorHelpDocumentationController.php', | ||||
|     'PhabricatorHelpEditorProtocolController' => 'applications/help/controller/PhabricatorHelpEditorProtocolController.php', | ||||
|     'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/PhabricatorHelpKeyboardShortcutController.php', | ||||
|     'PhabricatorHeraldApplication' => 'applications/herald/application/PhabricatorHeraldApplication.php', | ||||
| @@ -5232,6 +5233,7 @@ phutil_register_library_map(array( | ||||
|     'PhabricatorHashTestCase' => 'PhabricatorTestCase', | ||||
|     'PhabricatorHelpApplication' => 'PhabricatorApplication', | ||||
|     'PhabricatorHelpController' => 'PhabricatorController', | ||||
|     'PhabricatorHelpDocumentationController' => 'PhabricatorHelpController', | ||||
|     'PhabricatorHelpEditorProtocolController' => 'PhabricatorHelpController', | ||||
|     'PhabricatorHelpKeyboardShortcutController' => 'PhabricatorHelpController', | ||||
|     'PhabricatorHeraldApplication' => 'PhabricatorApplication', | ||||
|   | ||||
| @@ -26,8 +26,13 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication { | ||||
|     return self::GROUP_UTILITIES; | ||||
|   } | ||||
|  | ||||
|   public function getHelpURI() { | ||||
|     return PhabricatorEnv::getDoclink('Almanac User Guide'); | ||||
|   public function getHelpDocumentationArticles(PhabricatorUser $viewer) { | ||||
|     return array( | ||||
|       array( | ||||
|         'name' => pht('Alamanac User Guide'), | ||||
|         'href' => PhabricatorEnv::getDoclink('Almanac User Guide'), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function isPrototype() { | ||||
|   | ||||
| @@ -22,8 +22,13 @@ final class PhabricatorAuditApplication extends PhabricatorApplication { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function getHelpURI() { | ||||
|     return PhabricatorEnv::getDoclink('Audit User Guide'); | ||||
|   public function getHelpDocumentationArticles(PhabricatorUser $viewer) { | ||||
|     return array( | ||||
|       array( | ||||
|         'name' => pht('Audit User Guide'), | ||||
|         'href' => PhabricatorEnv::getDoclink('Audit User Guide'), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function getRoutes() { | ||||
|   | ||||
| @@ -26,16 +26,16 @@ final class PhabricatorAuthApplication extends PhabricatorApplication { | ||||
|     return pht('Login/Registration'); | ||||
|   } | ||||
|  | ||||
|   public function getHelpURI() { | ||||
|   public function getHelpDocumentationArticles(PhabricatorUser $viewer) { | ||||
|     // NOTE: Although reasonable help exists for this in "Configuring Accounts | ||||
|     // and Registration", specifying a help URI here means we get the menu | ||||
|     // and Registration", specifying help items here means we get the menu | ||||
|     // item in all the login/link interfaces, which is confusing and not | ||||
|     // helpful. | ||||
|  | ||||
|     // TODO: Special case this, or split the auth and auth administration | ||||
|     // applications? | ||||
|  | ||||
|     return null; | ||||
|     return array(); | ||||
|   } | ||||
|  | ||||
|   public function buildMainMenuItems( | ||||
|   | ||||
| @@ -169,8 +169,48 @@ abstract class PhabricatorApplication implements PhabricatorPolicyInterface { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   public function getHelpURI() { | ||||
|     return null; | ||||
|   public function getHelpMenuItems(PhabricatorUser $viewer) { | ||||
|     $items = array(); | ||||
|  | ||||
|     $articles = $this->getHelpDocumentationArticles($viewer); | ||||
|     if ($articles) { | ||||
|       $items[] = id(new PHUIListItemView()) | ||||
|         ->setType(PHUIListItemView::TYPE_LABEL) | ||||
|         ->setName(pht('%s Documentation', $this->getName())); | ||||
|       foreach ($articles as $article) { | ||||
|         $item = id(new PHUIListItemView()) | ||||
|           ->setName($article['name']) | ||||
|           ->setIcon('fa-book') | ||||
|           ->setHref($article['href']); | ||||
|  | ||||
|         $items[] = $item; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     $command_specs = $this->getMailCommandObjects(); | ||||
|     if ($command_specs) { | ||||
|       $items[] = id(new PHUIListItemView()) | ||||
|         ->setType(PHUIListItemView::TYPE_LABEL) | ||||
|         ->setName(pht('Email Help')); | ||||
|       foreach ($command_specs as $key => $spec) { | ||||
|         $object = $spec['object']; | ||||
|  | ||||
|         $class = get_class($this); | ||||
|         $href = '/applications/mailcommands/'.$class.'/'.$key.'/'; | ||||
|  | ||||
|         $item = id(new PHUIListItemView()) | ||||
|           ->setName($spec['name']) | ||||
|           ->setIcon('fa-envelope-o') | ||||
|           ->setHref($href); | ||||
|         $items[] = $item; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return $items; | ||||
|   } | ||||
|  | ||||
|   public function getHelpDocumentationArticles(PhabricatorUser $viewer) { | ||||
|     return array(); | ||||
|   } | ||||
|  | ||||
|   public function getOverview() { | ||||
|   | ||||
| @@ -14,8 +14,13 @@ final class PhabricatorConduitApplication extends PhabricatorApplication { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   public function getHelpURI() { | ||||
|     return PhabricatorEnv::getDoclink('Conduit Technical Documentation'); | ||||
|   public function getHelpDocumentationArticles(PhabricatorUser $viewer) { | ||||
|     return array( | ||||
|       array( | ||||
|         'name' => pht('Conduit Technical Documentation'), | ||||
|         'href' => PhabricatorEnv::getDoclink('Conduit Technical Documentation'), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function getName() { | ||||
|   | ||||
| @@ -22,8 +22,13 @@ final class PhabricatorDifferentialApplication extends PhabricatorApplication { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function getHelpURI() { | ||||
|     return PhabricatorEnv::getDoclink('Differential User Guide'); | ||||
|   public function getHelpDocumentationArticles(PhabricatorUser $viewer) { | ||||
|     return array( | ||||
|       array( | ||||
|         'name' => pht('Differential User Guide'), | ||||
|         'href' => PhabricatorEnv::getDoclink('Differential User Guide'), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function getFactObjectsForAnalysis() { | ||||
| @@ -189,6 +194,7 @@ EOTEXT | ||||
|   public function getMailCommandObjects() { | ||||
|     return array( | ||||
|       'revision' => array( | ||||
|         'name' => pht('Email Commands: Revisions'), | ||||
|         'object' => new DifferentialRevision(), | ||||
|       ), | ||||
|     ); | ||||
|   | ||||
| @@ -22,8 +22,13 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function getHelpURI() { | ||||
|     return PhabricatorEnv::getDoclink('Diffusion User Guide'); | ||||
|   public function getHelpDocumentationArticles(PhabricatorUser $viewer) { | ||||
|     return array( | ||||
|       array( | ||||
|         'name' => pht('Diffusion User Guide'), | ||||
|         'href' => PhabricatorEnv::getDoclink('Diffusion User Guide'), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function getFactObjectsForAnalysis() { | ||||
|   | ||||
| @@ -34,8 +34,13 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function getHelpURI() { | ||||
|     return PhabricatorEnv::getDoclink('Drydock User Guide'); | ||||
|   public function getHelpDocumentationArticles(PhabricatorUser $viewer) { | ||||
|     return array( | ||||
|       array( | ||||
|         'name' => pht('Drydock User Guide'), | ||||
|         'href' => PhabricatorEnv::getDoclink('Drydock User Guide'), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function getRoutes() { | ||||
|   | ||||
| @@ -19,6 +19,8 @@ final class PhabricatorHelpApplication extends PhabricatorApplication { | ||||
|       '/help/' => array( | ||||
|         'keyboardshortcut/' => 'PhabricatorHelpKeyboardShortcutController', | ||||
|         'editorprotocol/' => 'PhabricatorHelpEditorProtocolController', | ||||
|         'documentation/(?P<application>\w+)/' | ||||
|           => 'PhabricatorHelpDocumentationController', | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| @@ -27,27 +29,75 @@ final class PhabricatorHelpApplication extends PhabricatorApplication { | ||||
|     PhabricatorUser $user, | ||||
|     PhabricatorController $controller = null) { | ||||
|  | ||||
|     $items = array(); | ||||
|  | ||||
|     $application = null; | ||||
|     if ($controller) { | ||||
|       $application = $controller->getCurrentApplication(); | ||||
|     } | ||||
|  | ||||
|     if ($application && $application->getHelpURI()) { | ||||
|       $help_name = pht('%s Help', $application->getName()); | ||||
|     $items = array(); | ||||
|     if ($application) { | ||||
|       $help_items = $application->getHelpMenuItems($user); | ||||
|       if ($help_items) { | ||||
|         $help_id = celerity_generate_unique_node_id(); | ||||
|  | ||||
|       $item = id(new PHUIListItemView()) | ||||
|         ->setName($help_name) | ||||
|         ->addClass('core-menu-item') | ||||
|         ->setIcon('fa-info-circle') | ||||
|         ->setAural($help_name) | ||||
|         ->setOrder(200) | ||||
|         ->setHref($application->getHelpURI()); | ||||
|       $items[] = $item; | ||||
|         Javelin::initBehavior( | ||||
|           'aphlict-dropdown', | ||||
|           array( | ||||
|             'bubbleID' => $help_id, | ||||
|             'dropdownID' => 'phabricator-help-menu', | ||||
|             'local' => true, | ||||
|             'desktop' => true, | ||||
|             'right' => true, | ||||
|           )); | ||||
|  | ||||
|         $help_name = pht('%s Help', $application->getName()); | ||||
|  | ||||
|         $item = id(new PHUIListItemView()) | ||||
|           ->setName($help_name) | ||||
|           ->setIcon('fa-life-ring') | ||||
|           ->setHref('/help/documentation/'.get_class($application).'/') | ||||
|           ->addClass('core-menu-item') | ||||
|           ->setID($help_id) | ||||
|           ->setAural($help_name) | ||||
|           ->setOrder(200); | ||||
|         $items[] = $item; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return $items; | ||||
|   } | ||||
|  | ||||
|   public function buildMainMenuExtraNodes( | ||||
|     PhabricatorUser $viewer, | ||||
|     PhabricatorController $controller = null) { | ||||
|  | ||||
|     if (!$controller) { | ||||
|       return null; | ||||
|     } | ||||
|  | ||||
|     $application = $controller->getCurrentApplication(); | ||||
|     if (!$application) { | ||||
|       return null; | ||||
|     } | ||||
|  | ||||
|     $help_items = $application->getHelpMenuItems($viewer); | ||||
|     if (!$help_items) { | ||||
|       return null; | ||||
|     } | ||||
|  | ||||
|     $view = new PHUIListView(); | ||||
|     foreach ($help_items as $item) { | ||||
|       $view->addMenuItem($item); | ||||
|     } | ||||
|  | ||||
|     return phutil_tag( | ||||
|       'div', | ||||
|       array( | ||||
|         'id' => 'phabricator-help-menu', | ||||
|         'class' => 'phabricator-main-menu-dropdown phui-list-sidenav', | ||||
|         'style' => 'display: none', | ||||
|       ), | ||||
|       $view); | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,52 @@ | ||||
| <?php | ||||
|  | ||||
| final class PhabricatorHelpDocumentationController | ||||
|   extends PhabricatorHelpController { | ||||
|  | ||||
|   public function shouldAllowPublic() { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function handleRequest(AphrontRequest $request) { | ||||
|     $viewer = $this->getViewer(); | ||||
|  | ||||
|     $application_class = $request->getURIData('application'); | ||||
|     $application = id(new PhabricatorApplicationQuery()) | ||||
|       ->setViewer($viewer) | ||||
|       ->withClasses(array($application_class)) | ||||
|       ->executeOne(); | ||||
|     if (!$application) { | ||||
|       return new Aphront404Response(); | ||||
|     } | ||||
|  | ||||
|     $items = $application->getHelpMenuItems($viewer); | ||||
|     $title = pht('%s Help', $application->getName()); | ||||
|  | ||||
|     $list = id(new PHUIObjectItemListView()) | ||||
|       ->setUser($viewer); | ||||
|     foreach ($items as $item) { | ||||
|       if ($item->getType() == PHUIListItemView::TYPE_LABEL) { | ||||
|         continue; | ||||
|       } | ||||
|       $list->addItem( | ||||
|         id(new PHUIObjectItemView()) | ||||
|           ->setHeader($item->getName()) | ||||
|           ->setWorkflow($item->getWorkflow()) | ||||
|           ->setHref($item->getHref())); | ||||
|     } | ||||
|  | ||||
|     $crumbs = $this->buildApplicationCrumbs(); | ||||
|     $crumbs->addTextCrumb($title); | ||||
|  | ||||
|     return $this->buildApplicationPage( | ||||
|       array( | ||||
|         $crumbs, | ||||
|         $list, | ||||
|       ), | ||||
|       array( | ||||
|         'title' => $title, | ||||
|       )); | ||||
|   } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -22,8 +22,13 @@ final class PhabricatorHeraldApplication extends PhabricatorApplication { | ||||
|     return "\xE2\x98\xBF"; | ||||
|   } | ||||
|  | ||||
|   public function getHelpURI() { | ||||
|     return PhabricatorEnv::getDoclink('Herald User Guide'); | ||||
|   public function getHelpDocumentationArticles(PhabricatorUser $viewer) { | ||||
|     return array( | ||||
|       array( | ||||
|         'name' => pht('Herald User Guide'), | ||||
|         'href' => PhabricatorEnv::getDoclink('Herald User Guide'), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function getFlavorText() { | ||||
|   | ||||
| @@ -32,5 +32,4 @@ final class PhabricatorHomeQuickCreateController | ||||
|       )); | ||||
|   } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -32,8 +32,13 @@ final class PhabricatorLegalpadApplication extends PhabricatorApplication { | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function getHelpURI() { | ||||
|     return PhabricatorEnv::getDoclink('Legalpad User Guide'); | ||||
|   public function getHelpDocumentationArticles(PhabricatorUser $viewer) { | ||||
|     return array( | ||||
|       array( | ||||
|         'name' => pht('Legalpad User Guide'), | ||||
|         'href' => PhabricatorEnv::getDoclink('Legalpad User Guide'), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function getOverview() { | ||||
|   | ||||
| @@ -146,6 +146,7 @@ final class PhabricatorManiphestApplication extends PhabricatorApplication { | ||||
|   public function getMailCommandObjects() { | ||||
|     return array( | ||||
|       'task' => array( | ||||
|         'name' => pht('Email Commands: Tasks'), | ||||
|         'object' => new ManiphestTask(), | ||||
|       ), | ||||
|     ); | ||||
|   | ||||
| @@ -138,14 +138,6 @@ final class PhabricatorApplicationDetailViewController | ||||
|       ->setUser($user) | ||||
|       ->setObjectURI($this->getRequest()->getRequestURI()); | ||||
|  | ||||
|     if ($selected->getHelpURI()) { | ||||
|       $view->addAction( | ||||
|         id(new PhabricatorActionView()) | ||||
|           ->setName(pht('Help / Documentation')) | ||||
|           ->setIcon('fa-life-ring') | ||||
|           ->setHref($selected->getHelpURI())); | ||||
|     } | ||||
|  | ||||
|     $can_edit = PhabricatorPolicyFilter::hasCapability( | ||||
|       $user, | ||||
|       $selected, | ||||
|   | ||||
| @@ -78,9 +78,11 @@ final class PhabricatorApplicationEmailCommandsController | ||||
|  | ||||
|     $content = implode("\n\n", $content); | ||||
|  | ||||
|     $title = $spec['name']; | ||||
|  | ||||
|     $crumbs = $this->buildApplicationCrumbs(); | ||||
|     $this->addApplicationCrumb($crumbs, $selected); | ||||
|     $crumbs->addTextCrumb(pht('Mail Commands')); | ||||
|     $crumbs->addTextCrumb($title); | ||||
|  | ||||
|     $content_box = id(new PHUIBoxView()) | ||||
|       ->addMargin(PHUI::MARGIN_LARGE) | ||||
| @@ -91,7 +93,7 @@ final class PhabricatorApplicationEmailCommandsController | ||||
|           $viewer)); | ||||
|  | ||||
|     $box = id(new PHUIObjectBoxView()) | ||||
|       ->setHeaderText(pht('Mail Commands')) | ||||
|       ->setHeaderText($title) | ||||
|       ->appendChild($content_box); | ||||
|  | ||||
|     return $this->buildApplicationPage( | ||||
| @@ -100,7 +102,7 @@ final class PhabricatorApplicationEmailCommandsController | ||||
|         $box, | ||||
|       ), | ||||
|       array( | ||||
|         'title' => 'asdf', | ||||
|         'title' => $title, | ||||
|       )); | ||||
|  | ||||
|   } | ||||
|   | ||||
| @@ -34,8 +34,14 @@ final class PhabricatorOAuthServerApplication extends PhabricatorApplication { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function getHelpURI() { | ||||
|     return PhabricatorEnv::getDoclink('Using the Phabricator OAuth Server'); | ||||
|   public function getHelpDocumentationArticles(PhabricatorUser $viewer) { | ||||
|     return array( | ||||
|       array( | ||||
|         'name' => pht('Using the Phabricator OAuth Server'), | ||||
|         'href' => PhabricatorEnv::getDoclink( | ||||
|           'Using the Phabricator OAuth Server'), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function getRoutes() { | ||||
|   | ||||
| @@ -22,8 +22,13 @@ final class PhabricatorOwnersApplication extends PhabricatorApplication { | ||||
|     return "\xE2\x98\x81"; | ||||
|   } | ||||
|  | ||||
|   public function getHelpURI() { | ||||
|     return PhabricatorEnv::getDoclink('Owners Tool User Guide'); | ||||
|   public function getHelpDocumentationArticles(PhabricatorUser $viewer) { | ||||
|     return array( | ||||
|       array( | ||||
|         'name' => pht('Owners User Guide'), | ||||
|         'href' => PhabricatorEnv::getDoclink('Owners Tool User Guide'), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function getFlavorText() { | ||||
|   | ||||
| @@ -22,8 +22,13 @@ final class PhabricatorPhameApplication extends PhabricatorApplication { | ||||
|     return "\xe2\x9c\xa9"; | ||||
|   } | ||||
|  | ||||
|   public function getHelpURI() { | ||||
|     return PhabricatorEnv::getDoclink('Phame User Guide'); | ||||
|   public function getHelpDocumentationArticles(PhabricatorUser $viewer) { | ||||
|     return array( | ||||
|       array( | ||||
|         'name' => pht('Phame User Guide'), | ||||
|         'href' => PhabricatorEnv::getDoclink('Phame User Guide'), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function isPrototype() { | ||||
|   | ||||
| @@ -22,8 +22,13 @@ final class PhabricatorPhrictionApplication extends PhabricatorApplication { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public function getHelpURI() { | ||||
|     return PhabricatorEnv::getDoclink('Phriction User Guide'); | ||||
|   public function getHelpDocumentationArticles(PhabricatorUser $viewer) { | ||||
|     return array( | ||||
|       array( | ||||
|         'name' => pht('Phriction User Guide'), | ||||
|         'href' => PhabricatorEnv::getDoclink('Phriction User Guide'), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function getTitleGlyph() { | ||||
|   | ||||
| @@ -22,8 +22,13 @@ final class PhabricatorSlowvoteApplication extends PhabricatorApplication { | ||||
|     return "\xE2\x9C\x94"; | ||||
|   } | ||||
|  | ||||
|   public function getHelpURI() { | ||||
|     return PhabricatorEnv::getDoclink('Slowvote User Guide'); | ||||
|   public function getHelpDocumentationArticles(PhabricatorUser $viewer) { | ||||
|     return array( | ||||
|       array( | ||||
|         'name' => pht('Slowvote User Guide'), | ||||
|         'href' => PhabricatorEnv::getDoclink('Slowvote User Guide'), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   public function getFlavorText() { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 epriestley
					epriestley