diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php index aed8bc7d94..934b39865c 100644 --- a/src/applications/base/PhabricatorApplication.php +++ b/src/applications/base/PhabricatorApplication.php @@ -1,307 +1,306 @@ pht('Core Applications'), self::GROUP_COMMUNICATION => pht('Communication'), self::GROUP_ORGANIZATION => pht('Organization'), self::GROUP_UTILITIES => pht('Utilities'), self::GROUP_ADMIN => pht('Administration'), self::GROUP_DEVELOPER => pht('Developer Tools'), self::GROUP_MISC => pht('Miscellaneous Applications'), ); } public static function getTileDisplayName($constant) { $names = array( self::TILE_INVISIBLE => pht('Invisible'), self::TILE_HIDE => pht('Hidden'), self::TILE_SHOW => pht('Show Small Tile'), self::TILE_FULL => pht('Show Large Tile'), ); return idx($names, $constant); } /* -( Application Information )-------------------------------------------- */ public function getName() { return substr(get_class($this), strlen('PhabricatorApplication')); } public function getShortDescription() { return $this->getName().' Application'; } public function isEnabled() { return true; } public function isInstalled() { $uninstalled = PhabricatorEnv::getEnvConfig('phabricator.uninstalled-applications'); if (!$this->canUninstall()) { return true; } else if (isset($uninstalled[get_class($this)])) { return false; } else { return true; } } public function isBeta() { return false; } public function canUninstall() { return true; } public function getPHID() { return 'PHID-APPS-'.get_class($this); } public function getTypeaheadURI() { return $this->getBaseURI(); } public function getBaseURI() { return null; } public function getIconURI() { return null; } public function getIconName() { return 'application'; } public function shouldAppearInLaunchView() { return true; } public function getApplicationOrder() { return PHP_INT_MAX; } public function getApplicationGroup() { return self::GROUP_MISC; } public function getTitleGlyph() { return null; } public function getHelpURI() { // TODO: When these applications get created, link to their docs: // // - Drydock // - OAuth Server return null; } public function getEventListeners() { return array(); } public function getDefaultTileDisplay(PhabricatorUser $user) { switch ($this->getApplicationGroup()) { case self::GROUP_CORE: return self::TILE_FULL; case self::GROUP_UTILITIES: case self::GROUP_DEVELOPER: return self::TILE_HIDE; case self::GROUP_ADMIN: if ($user->getIsAdmin()) { return self::TILE_SHOW; } else { return self::TILE_INVISIBLE; } break; default: return self::TILE_SHOW; } } /* -( URI Routing )-------------------------------------------------------- */ public function getRoutes() { return array(); } /* -( Fact Integration )--------------------------------------------------- */ public function getFactObjectsForAnalysis() { return array(); } /* -( UI Integration )----------------------------------------------------- */ /** * Render status elements (like "3 Waiting Reviews") for application list * views. These provide a way to alert users to new or pending action items * in applications. * * @param PhabricatorUser Viewing user. * @return list Application status elements. * @task ui */ public function loadStatus(PhabricatorUser $user) { return array(); } /** * You can provide an optional piece of flavor text for the application. This * is currently rendered in application launch views if the application has no * status elements. * * @return string|null Flavor text. * @task ui */ public function getFlavorText() { return null; } /** * Build items for the main menu. * * @param PhabricatorUser The viewing user. * @param AphrontController The current controller. May be null for special * pages like 404, exception handlers, etc. * @return list List of menu items. * @task ui */ public function buildMainMenuItems( PhabricatorUser $user, PhabricatorController $controller = null) { return array(); } /** * On the Phabricator homepage sidebar, this function returns the URL for * a quick create X link which is displayed in the wide button only. * * @return string * @task ui */ public function getQuickCreateURI() { return null; } /* -( Application Management )--------------------------------------------- */ public static function getByClass($class_name) { $selected = null; $applications = PhabricatorApplication::getAllApplications(); foreach ($applications as $application) { if (get_class($application) == $class_name) { $selected = $application; break; } } return $selected; } public static function getAllApplications() { - $classes = id(new PhutilSymbolLoader()) ->setAncestorClass(__CLASS__) ->setConcreteOnly(true) ->selectAndLoadSymbols(); $apps = array(); foreach ($classes as $class) { $app = newv($class['name'], array()); $apps[] = $app; } + // Reorder the applications into "application order". Notably, this ensures + // their event handlers register in application order. + $apps = msort($apps, 'getApplicationOrder'); + $apps = mgroup($apps, 'getApplicationGroup'); + $apps = array_select_keys($apps, self::getApplicationGroups()) + $apps; + $apps = array_mergev($apps); + return $apps; } public static function getAllInstalledApplications() { static $applications; $show_beta = PhabricatorEnv::getEnvConfig('phabricator.show-beta-applications'); $uninstalled = PhabricatorEnv::getEnvConfig('phabricator.uninstalled-applications'); - if (empty($applications)) { - $classes = id(new PhutilSymbolLoader()) - ->setAncestorClass(__CLASS__) - ->setConcreteOnly(true) - ->selectAndLoadSymbols(); - + $all_applications = self::getAllApplications(); $apps = array(); - foreach ($classes as $class) { - - if (isset($uninstalled[$class['name']])) { - continue; - } - - $app = newv($class['name'], array()); + foreach ($all_applications as $app) { + $class = get_class($app); + if (isset($uninstalled[$class])) { + continue; + } - if (!$app->isEnabled()) { + if (!$app->isEnabled()) { continue; - } + } - if (!$show_beta && $app->isBeta()) { + if (!$show_beta && $app->isBeta()) { continue; - } + } - $apps[] = $app; + $apps[] = $app; } $applications = $apps; } return $applications; } } diff --git a/src/applications/directory/controller/PhabricatorDirectoryController.php b/src/applications/directory/controller/PhabricatorDirectoryController.php index 70391fa2d7..293ead040f 100644 --- a/src/applications/directory/controller/PhabricatorDirectoryController.php +++ b/src/applications/directory/controller/PhabricatorDirectoryController.php @@ -1,157 +1,155 @@ buildStandardPageView(); $page->setBaseURI('/'); $page->setTitle(idx($data, 'title')); $page->setGlyph("\xE2\x9A\x92"); $page->appendChild($view); $response = new AphrontWebpageResponse(); return $response->setContent($page->render()); } public function buildNav() { $user = $this->getRequest()->getUser(); $nav = new AphrontSideNavFilterView(); $nav->setBaseURI(new PhutilURI('/')); $applications = PhabricatorApplication::getAllInstalledApplications(); foreach ($applications as $key => $application) { if (!$application->shouldAppearInLaunchView()) { // Remove hidden applications (usually internal stuff). unset($applications[$key]); } $invisible = PhabricatorApplication::TILE_INVISIBLE; if ($application->getDefaultTileDisplay($user) == $invisible) { // Remove invisible applications (e.g., admin apps for non-admins). unset($applications[$key]); } } $status = array(); foreach ($applications as $key => $application) { $status[get_class($application)] = $application->loadStatus($user); } $tile_groups = array(); $prefs = $user->loadPreferences()->getPreference( PhabricatorUserPreferences::PREFERENCE_APP_TILES, array()); foreach ($applications as $key => $application) { $display = idx( $prefs, get_class($application), $application->getDefaultTileDisplay($user)); $tile_groups[$display][] = $application; } $tile_groups = array_select_keys( $tile_groups, array( PhabricatorApplication::TILE_FULL, PhabricatorApplication::TILE_SHOW, PhabricatorApplication::TILE_HIDE, )); foreach ($tile_groups as $tile_display => $tile_group) { if (!$tile_group) { continue; } - $tile_group = msort($tile_group, 'getApplicationOrder'); - $is_small_tiles = ($tile_display == PhabricatorApplication::TILE_SHOW) || ($tile_display == PhabricatorApplication::TILE_HIDE); if ($is_small_tiles) { $groups = PhabricatorApplication::getApplicationGroups(); $tile_group = mgroup($tile_group, 'getApplicationGroup'); $tile_group = array_select_keys($tile_group, array_keys($groups)); } else { $tile_group = array($tile_group); } $is_hide = ($tile_display == PhabricatorApplication::TILE_HIDE); if ($is_hide) { $show_item_id = celerity_generate_unique_node_id(); $show_tiles_id = celerity_generate_unique_node_id(); $show_item = id(new PhabricatorMenuItemView()) ->setName(pht('Show More Applications')) ->setHref('#') ->addSigil('home-show-applications') ->setID($show_item_id); $hide_item = id(new PhabricatorMenuItemView()) ->setName(pht('Show Fewer Applications')) ->setHref('#') ->addSigil('home-hide-applications'); $nav->addMenuItem($show_item); $nav->addCustomBlock( '
'); Javelin::initBehavior('phabricator-home-reveal-tiles', array( 'tilesID' => $show_tiles_id, 'showID' => $show_item_id, )); } foreach ($tile_group as $group => $application_list) { $tiles = array(); foreach ($application_list as $key => $application) { $tile = id(new PhabricatorApplicationLaunchView()) ->setApplication($application) ->setApplicationStatus( idx($status, get_class($application), array())) ->setUser($user); if ($tile_display == PhabricatorApplication::TILE_FULL) { $tile->setFullWidth(true); } $tiles[] = $tile; } if ($is_small_tiles) { while (count($tiles) % 3) { $tiles[] = id(new PhabricatorApplicationLaunchView()); } $nav->addLabel($groups[$group]); } $nav->addCustomBlock( phutil_render_tag( 'div', array( 'class' => 'application-tile-group', ), id(new AphrontNullView())->appendChild($tiles)->render())); } $is_hide = ($tile_display == PhabricatorApplication::TILE_HIDE); if ($is_hide) { $nav->addMenuItem($hide_item); $nav->addCustomBlock('
'); } } $nav->addFilter( '', pht('Customize Applications...'), '/settings/panel/home/'); $nav->addClass('phabricator-side-menu-home'); $nav->selectFilter(null); return $nav; } } diff --git a/src/applications/meta/controller/PhabricatorApplicationsListController.php b/src/applications/meta/controller/PhabricatorApplicationsListController.php index 80ca3dcbfc..23837e6a17 100644 --- a/src/applications/meta/controller/PhabricatorApplicationsListController.php +++ b/src/applications/meta/controller/PhabricatorApplicationsListController.php @@ -1,70 +1,72 @@ getRequest(); $user = $request->getUser(); $nav = $this->buildSideNavView(); $nav->selectFilter('/'); $applications = PhabricatorApplication::getAllApplications(); $list = $this->buildInstalledApplicationsList($applications); $title = pht('Installed Applications'); $header = id(new PhabricatorHeaderView()) ->setHeader($title); $nav->appendChild( array( $header, $list )); $crumbs = $this ->buildApplicationCrumbs() ->addCrumb( id(new PhabricatorCrumbView()) ->setName(pht('Applications')) ->setHref($this->getApplicationURI())); $nav->setCrumbs($crumbs); return $this->buildApplicationPage( $nav, array( 'title' => $title, 'device' => true, ) ); } private function buildInstalledApplicationsList(array $applications) { $list = new PhabricatorObjectItemListView(); + $applications = msort($applications, 'getName'); + foreach ($applications as $application) { $item = id(new PhabricatorObjectItemView()) ->setHeader($application->getName()) ->setHref('/applications/view/'.get_class($application).'/') ->addAttribute( phutil_escape_html($application->getShortDescription())); if (!$application->isInstalled()) { $item->addIcon('delete', pht('Uninstalled')); } if ($application->isBeta()) { $item->addIcon('lint-warning', pht('Beta')); } $list->addItem($item); } return $list; } }