public function handleRequest(AphrontRequest $request)
 {
     $viewer = $request->getViewer();
     $open_items = PhabricatorSetupCheck::getOpenSetupIssueKeys();
     $issues = PhabricatorSetupCheck::runNormalChecks();
     PhabricatorSetupCheck::setOpenSetupIssueKeys(PhabricatorSetupCheck::getUnignoredIssueKeys($issues), $update_database = true);
     if ($issues) {
         require_celerity_resource('phabricator-notification-menu-css');
         $items = array();
         foreach ($issues as $issue) {
             $classes = array();
             $classes[] = 'phabricator-notification';
             if ($issue->getIsIgnored()) {
                 $classes[] = 'phabricator-notification-read';
             } else {
                 $classes[] = 'phabricator-notification-unread';
             }
             $uri = '/config/issue/' . $issue->getIssueKey() . '/';
             $title = $issue->getName();
             $summary = $issue->getSummary();
             $items[] = javelin_tag('div', array('class' => implode(' ', $classes), 'sigil' => 'notification', 'meta' => array('href' => $uri)), $title);
         }
         $content = phutil_tag_div('setup-issue-menu', $items);
     } else {
         $content = phutil_tag_div('phabricator-notification no-notifications', pht('You have no unresolved setup issues.'));
     }
     $content = hsprintf('<div class="phabricator-notification-header">%s</div>' . '%s', phutil_tag('a', array('href' => '/config/issue/'), pht('Unresolved Setup Issues')), $content);
     $unresolved_count = count($open_items);
     $json = array('content' => $content, 'number' => (int) $unresolved_count);
     return id(new AphrontAjaxResponse())->setContent($json);
 }
 protected function getBody()
 {
     $user = null;
     $request = $this->getRequest();
     if ($request) {
         $user = $request->getUser();
     }
     $header_chrome = null;
     if ($this->getShowChrome()) {
         $header_chrome = $this->menuContent;
     }
     $classes = array();
     $classes[] = 'main-page-frame';
     $developer_warning = null;
     if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode') && DarkConsoleErrorLogPluginAPI::getErrors()) {
         $developer_warning = phutil_tag_div('aphront-developer-error-callout', pht('This page raised PHP errors. Find them in DarkConsole ' . 'or the error log.'));
     }
     // Render the "you have unresolved setup issues..." warning.
     $setup_warning = null;
     if ($user && $user->getIsAdmin()) {
         $open = PhabricatorSetupCheck::getOpenSetupIssueKeys();
         if ($open) {
             $classes[] = 'page-has-warning';
             $setup_warning = phutil_tag_div('setup-warning-callout', phutil_tag('a', array('href' => '/config/issue/', 'title' => implode(', ', $open)), pht('You have %d unresolved setup issue(s)...', count($open))));
         }
     }
     $main_page = phutil_tag('div', array('id' => 'phabricator-standard-page', 'class' => 'phabricator-standard-page'), array($developer_warning, $header_chrome, $setup_warning, phutil_tag('div', array('id' => 'phabricator-standard-page-body', 'class' => 'phabricator-standard-page-body'), $this->renderPageBodyContent())));
     $durable_column = null;
     if ($this->getShowDurableColumn()) {
         $is_visible = $this->getDurableColumnVisible();
         $durable_column = id(new ConpherenceDurableColumnView())->setSelectedConpherence(null)->setUser($user)->setQuicksandConfig($this->buildQuicksandConfig())->setVisible($is_visible)->setInitialLoad(true);
     }
     Javelin::initBehavior('quicksand-blacklist', array('patterns' => $this->getQuicksandURIPatternBlacklist()));
     return phutil_tag('div', array('class' => implode(' ', $classes)), array($main_page, $durable_column));
 }
 private function renderNotificationMenu()
 {
     $viewer = $this->getViewer();
     require_celerity_resource('phabricator-notification-css');
     require_celerity_resource('phabricator-notification-menu-css');
     $container_classes = array('alert-notifications');
     $aural = array();
     $dropdown_query = id(new AphlictDropdownDataQuery())->setViewer($viewer);
     $dropdown_data = $dropdown_query->execute();
     $message_tag = '';
     $message_notification_dropdown = '';
     $conpherence_app = 'PhabricatorConpherenceApplication';
     $conpherence_data = $dropdown_data[$conpherence_app];
     if ($conpherence_data['isInstalled']) {
         $message_id = celerity_generate_unique_node_id();
         $message_count_id = celerity_generate_unique_node_id();
         $message_dropdown_id = celerity_generate_unique_node_id();
         $message_count_number = $conpherence_data['rawCount'];
         if ($message_count_number) {
             $aural[] = phutil_tag('a', array('href' => '/conpherence/'), pht('%s unread messages.', new PhutilNumber($message_count_number)));
         } else {
             $aural[] = pht('No messages.');
         }
         $message_count_tag = phutil_tag('span', array('id' => $message_count_id, 'class' => 'phabricator-main-menu-message-count'), $conpherence_data['count']);
         $message_icon_tag = javelin_tag('span', array('class' => 'phabricator-main-menu-message-icon phui-icon-view ' . 'phui-font-fa fa-comments', 'sigil' => 'menu-icon'), '');
         if ($message_count_number) {
             $container_classes[] = 'message-unread';
         }
         $message_tag = phutil_tag('a', array('href' => '/conpherence/', 'class' => implode(' ', $container_classes), 'id' => $message_id), array($message_icon_tag, $message_count_tag));
         Javelin::initBehavior('aphlict-dropdown', array('bubbleID' => $message_id, 'countID' => $message_count_id, 'dropdownID' => $message_dropdown_id, 'loadingText' => pht('Loading...'), 'uri' => '/conpherence/panel/', 'countType' => $conpherence_data['countType'], 'countNumber' => $message_count_number, 'unreadClass' => 'message-unread'));
         $message_notification_dropdown = javelin_tag('div', array('id' => $message_dropdown_id, 'class' => 'phabricator-notification-menu', 'sigil' => 'phabricator-notification-menu', 'style' => 'display: none;'), '');
     }
     $bubble_tag = '';
     $notification_dropdown = '';
     $notification_app = 'PhabricatorNotificationsApplication';
     $notification_data = $dropdown_data[$notification_app];
     if ($notification_data['isInstalled']) {
         $count_id = celerity_generate_unique_node_id();
         $dropdown_id = celerity_generate_unique_node_id();
         $bubble_id = celerity_generate_unique_node_id();
         $count_number = $notification_data['rawCount'];
         if ($count_number) {
             $aural[] = phutil_tag('a', array('href' => '/notification/'), pht('%s unread notifications.', new PhutilNumber($count_number)));
         } else {
             $aural[] = pht('No notifications.');
         }
         $count_tag = phutil_tag('span', array('id' => $count_id, 'class' => 'phabricator-main-menu-alert-count'), $notification_data['count']);
         $icon_tag = javelin_tag('span', array('class' => 'phabricator-main-menu-alert-icon phui-icon-view ' . 'phui-font-fa fa-bell', 'sigil' => 'menu-icon'), '');
         if ($count_number) {
             $container_classes[] = 'alert-unread';
         }
         $bubble_tag = phutil_tag('a', array('href' => '/notification/', 'class' => implode(' ', $container_classes), 'id' => $bubble_id), array($icon_tag, $count_tag));
         Javelin::initBehavior('aphlict-dropdown', array('bubbleID' => $bubble_id, 'countID' => $count_id, 'dropdownID' => $dropdown_id, 'loadingText' => pht('Loading...'), 'uri' => '/notification/panel/', 'countType' => $notification_data['countType'], 'countNumber' => $count_number, 'unreadClass' => 'alert-unread'));
         $notification_dropdown = javelin_tag('div', array('id' => $dropdown_id, 'class' => 'phabricator-notification-menu', 'sigil' => 'phabricator-notification-menu', 'style' => 'display: none;'), '');
     }
     // Admin Level Urgent Notification Channel
     $setup_tag = '';
     $setup_notification_dropdown = '';
     if ($viewer && $viewer->getIsAdmin()) {
         $open = PhabricatorSetupCheck::getOpenSetupIssueKeys();
         if ($open) {
             $setup_id = celerity_generate_unique_node_id();
             $setup_count_id = celerity_generate_unique_node_id();
             $setup_dropdown_id = celerity_generate_unique_node_id();
             $setup_count_number = count($open);
             if ($setup_count_number) {
                 $aural[] = phutil_tag('a', array('href' => '/config/issue/'), pht('%s unresolved issues.', new PhutilNumber($setup_count_number)));
             } else {
                 $aural[] = pht('No issues.');
             }
             $setup_count_tag = phutil_tag('span', array('id' => $setup_count_id, 'class' => 'phabricator-main-menu-setup-count'), $setup_count_number);
             $setup_icon_tag = javelin_tag('span', array('class' => 'phabricator-main-menu-setup-icon phui-icon-view ' . 'phui-font-fa fa-exclamation-circle', 'sigil' => 'menu-icon'), '');
             if ($setup_count_number) {
                 $container_classes[] = 'setup-unread';
             }
             $setup_tag = phutil_tag('a', array('href' => '/config/issue/', 'class' => implode(' ', $container_classes), 'id' => $setup_id), array($setup_icon_tag, $setup_count_tag));
             Javelin::initBehavior('aphlict-dropdown', array('bubbleID' => $setup_id, 'countID' => $setup_count_id, 'dropdownID' => $setup_dropdown_id, 'loadingText' => pht('Loading...'), 'uri' => '/config/issue/panel/', 'countType' => null, 'countNumber' => null, 'unreadClass' => 'setup-unread'));
             $setup_notification_dropdown = javelin_tag('div', array('id' => $setup_dropdown_id, 'class' => 'phabricator-notification-menu', 'sigil' => 'phabricator-notification-menu', 'style' => 'display: none;'), '');
         }
     }
     $dropdowns = array($notification_dropdown, $message_notification_dropdown, $setup_notification_dropdown);
     return array(array($bubble_tag, $message_tag, $setup_tag), $dropdowns, $aural);
 }
 public function buildWelcomeScreen(AphrontRequest $request)
 {
     $viewer = $request->getUser();
     $this->requireResource('config-welcome-css');
     $content = pht("=== Install Phabricator ===\n\n" . "You have successfully installed Phabricator. This screen will guide " . "you through configuration and orientation. " . "These steps are optional, and you can go through them in any order. " . "If you want to get back to this screen later on, you can find it in " . "the **Config** application under **Welcome Screen**.");
     $setup = array();
     $setup[] = $this->newItem($request, 'fa-check-square-o green', $content);
     $issues_resolved = !PhabricatorSetupCheck::getOpenSetupIssueKeys();
     $setup_href = PhabricatorEnv::getURI('/config/issue/');
     if ($issues_resolved) {
         $content = pht("=== Resolve Setup Issues ===\n\n" . "You've resolved (or ignored) all outstanding setup issues. " . "You can review issues in the **Config** application, under " . "**[[ %s | Setup Issues ]]**.", $setup_href);
         $icon = 'fa-check-square-o green';
     } else {
         $content = pht("=== Resolve Setup Issues ===\n\n" . "You have some unresolved setup issues to take care of. Click " . "the link in the yellow banner at the top of the screen to see " . "them, or find them in the **Config** application under " . "**[[ %s | Setup Issues ]]**. " . "Although most setup issues should be resolved, sometimes an issue " . "is not applicable to an install. " . "If you don't intend to fix a setup issue (or don't want to fix " . "it for now), you can use the \"Ignore\" action to mark it as " . "something you don't plan to deal with.", $setup_href);
         $icon = 'fa-warning red';
     }
     $setup[] = $this->newItem($request, $icon, $content);
     $configs = id(new PhabricatorAuthProviderConfigQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->execute();
     $auth_href = PhabricatorEnv::getURI('/auth/');
     $have_auth = (bool) $configs;
     if ($have_auth) {
         $content = pht("=== Login and Registration ===\n\n" . "You've configured at least one authentication provider, so users " . "can register or log in. " . "To configure more providers or adjust settings, use the " . "**[[ %s | Auth Application ]]**.", $auth_href);
         $icon = 'fa-check-square-o green';
     } else {
         $content = pht("=== Login and Registration ===\n\n" . "You haven't configured any authentication providers yet. " . "Authentication providers allow users to register accounts and " . "log in to Phabricator. You can configure Phabricator to accept " . "credentials like username and password, LDAP, or Google OAuth. " . "You can configure authentication using the " . "**[[ %s | Auth Application ]]**.", $auth_href);
         $icon = 'fa-warning red';
     }
     $setup[] = $this->newItem($request, $icon, $content);
     $config_href = PhabricatorEnv::getURI('/config/');
     // Just load any config value at all; if one exists the install has figured
     // out how to configure things.
     $have_config = (bool) id(new PhabricatorConfigEntry())->loadAllWhere('1 = 1 LIMIT 1');
     if ($have_config) {
         $content = pht("=== Configure Phabricator Settings ===\n\n" . "You've configured at least one setting from the web interface. " . "To configure more settings later, use the " . "**[[ %s | Config Application ]]**.", $config_href);
         $icon = 'fa-check-square-o green';
     } else {
         $content = pht("=== Configure Phabricator Settings ===\n\n" . 'Many aspects of Phabricator are configurable. To explore and ' . 'adjust settings, use the **[[ %s | Config Application ]]**.', $config_href);
         $icon = 'fa-info-circle';
     }
     $setup[] = $this->newItem($request, $icon, $content);
     $settings_href = PhabricatorEnv::getURI('/settings/');
     $prefs = $viewer->loadPreferences()->getPreferences();
     $have_settings = !empty($prefs);
     if ($have_settings) {
         $content = pht("=== Adjust Account Settings ===\n\n" . "You've adjusted at least one setting on your account. " . "To make more adjustments, visit the " . "**[[ %s | Settings Application ]]**.", $settings_href);
         $icon = 'fa-check-square-o green';
     } else {
         $content = pht("=== Adjust Account Settings ===\n\n" . 'You can configure settings for your account by clicking the ' . 'wrench icon in the main menu bar, or visiting the ' . '**[[ %s | Settings Application ]]** directly.', $settings_href);
         $icon = 'fa-info-circle';
     }
     $setup[] = $this->newItem($request, $icon, $content);
     $dashboard_href = PhabricatorEnv::getURI('/dashboard/');
     $have_dashboard = (bool) PhabricatorDashboardInstall::getDashboard($viewer, PhabricatorHomeApplication::DASHBOARD_DEFAULT, 'PhabricatorHomeApplication');
     if ($have_dashboard) {
         $content = pht("=== Customize Home Page ===\n\n" . "You've installed a default dashboard to replace this welcome screen " . "on the home page. " . "You can still visit the welcome screen here at any time if you " . "have steps you want to complete later, or if you feel lonely. " . "If you've changed your mind about the dashboard you installed, " . "you can install a different default dashboard with the " . "**[[ %s | Dashboards Application ]]**.", $dashboard_href);
         $icon = 'fa-check-square-o green';
     } else {
         $content = pht("=== Customize Home Page ===\n\n" . "When you're done setting things up, you can create a custom " . "dashboard and install it. Your dashboard will replace this " . "welcome screen on the Phabricator home page. " . "Dashboards can show users the information that's most important to " . "your organization. You can configure them to display things like: " . "a custom welcome message, a feed of recent activity, or a list of " . "open tasks, waiting reviews, recent commits, and so on. " . "After you install a default dashboard, it will replace this page. " . "You can find this page later by visiting the **Config** " . "application, under **Welcome Page**. " . "To get started building a dashboard, use the " . "**[[ %s | Dashboards Application ]]**. ", $dashboard_href);
         $icon = 'fa-info-circle';
     }
     $setup[] = $this->newItem($request, $icon, $content);
     $apps_href = PhabricatorEnv::getURI('/applications/');
     $content = pht("=== Explore Applications ===\n\n" . "Phabricator is a large suite of applications that work together to " . "help you develop software, manage tasks, and communicate. A few of " . "the most commonly used applications are pinned to the left navigation " . "bar by default.\n\n" . "To explore all of the Phabricator applications, adjust settings, or " . "uninstall applications you don't plan to use, visit the " . "**[[ %s | Applications Application ]]**. You can also click the " . "**Applications** button in the left navigation menu, or search for an " . "application by name in the main menu bar. ", $apps_href);
     $explore = array();
     $explore[] = $this->newItem($request, 'fa-globe', $content);
     // TODO: Restore some sort of "Support" link here, but just nuke it for
     // now as we figure stuff out.
     $differential_uri = PhabricatorEnv::getURI('/differential/');
     $differential_create_uri = PhabricatorEnv::getURI('/differential/diff/create/');
     $differential_all_uri = PhabricatorEnv::getURI('/differential/query/all/');
     $differential_user_guide = PhabricatorEnv::getDoclink('Differential User Guide');
     $differential_vs_uri = PhabricatorEnv::getDoclink('User Guide: Review vs Audit');
     $quick = array();
     $quick[] = $this->newItem($request, 'fa-gear', pht("=== Quick Start: Code Review ===\n\n" . "Review code with **[[ %s | Differential ]]**. " . "Engineers can use Differential to share, review, and approve " . "changes to source code. " . "To get started with code review:\n\n" . "  - **[[ %s | Create a Revision ]]** //(Copy and paste a diff from " . "    the command line into the web UI to quickly get a feel for " . "    review.)//\n" . "  - **[[ %s | View All Revisions ]]**\n\n" . "For more information, see these articles in the documentation:\n\n" . "  - **[[ %s | Differential User Guide ]]**, for a general overview " . "    of Differential.\n" . "  - **[[ %s | User Guide: Review vs Audit ]]**, for a discussion " . "    of different code review workflows.", $differential_uri, $differential_create_uri, $differential_all_uri, $differential_user_guide, $differential_vs_uri));
     $maniphest_uri = PhabricatorEnv::getURI('/maniphest/');
     $maniphest_create_uri = PhabricatorEnv::getURI('/maniphest/task/edit/');
     $maniphest_all_uri = PhabricatorEnv::getURI('/maniphest/query/all/');
     $quick[] = $this->newItem($request, 'fa-anchor', pht("=== Quick Start: Bugs and Tasks ===\n\n" . "Track bugs and tasks in Phabricator with " . "**[[ %s | Maniphest ]]**. " . "Users in all roles can use Maniphest to manage current and " . "planned work and to track bugs and issues. " . "To get started with bugs and tasks:\n\n" . "  - **[[ %s | Create a Task ]]**\n" . "  - **[[ %s | View All Tasks ]]**\n", $maniphest_uri, $maniphest_create_uri, $maniphest_all_uri));
     $pholio_uri = PhabricatorEnv::getURI('/pholio/');
     $pholio_create_uri = PhabricatorEnv::getURI('/pholio/new/');
     $pholio_all_uri = PhabricatorEnv::getURI('/pholio/query/all/');
     $quick[] = $this->newItem($request, 'fa-camera-retro', pht("=== Quick Start: Design Review ===\n\n" . "Review proposed designs with **[[ %s | Pholio ]]**. " . "Designers can use Pholio to share images of what they're working on " . "and show off things they've made. " . "To get started with design review:\n\n" . "  - **[[ %s | Create a Mock ]]**\n" . "  - **[[ %s | View All Mocks ]]**", $pholio_uri, $pholio_create_uri, $pholio_all_uri));
     $diffusion_uri = PhabricatorEnv::getURI('/diffusion/');
     $diffusion_create_uri = PhabricatorEnv::getURI('/diffusion/create/');
     $diffusion_all_uri = PhabricatorEnv::getURI('/diffusion/query/all/');
     $diffusion_user_guide = PhabricatorEnv::getDoclink('Diffusion User Guide');
     $diffusion_setup_guide = PhabricatorEnv::getDoclink('Diffusion User Guide: Repository Hosting');
     $quick[] = $this->newItem($request, 'fa-code', pht("=== Quick Start: Repositories ===\n\n" . "Manage and browse source code repositories with " . "**[[ %s | Diffusion ]]**. " . "Engineers can use Diffusion to browse and audit source code. " . "You can configure Phabricator to host repositories, or have it " . "track existing repositories hosted elsewhere (like GitHub, " . "Bitbucket, or an internal server). " . "To get started with repositories:\n\n" . "  - **[[ %s | Create a New Repository ]]**\n" . "  - **[[ %s | View All Repositories ]]**\n\n" . "For more information, see these articles in the documentation:\n\n" . "  - **[[ %s | Diffusion User Guide ]]**, for a general overview of " . "    Diffusion.\n" . "  - **[[ %s | Diffusion User Guide: Repository Hosting ]]**, " . "    for instructions on configuring repository hosting.\n\n" . "Phabricator supports Git, Mercurial and Subversion.", $diffusion_uri, $diffusion_create_uri, $diffusion_all_uri, $diffusion_user_guide, $diffusion_setup_guide));
     $header = id(new PHUIHeaderView())->setHeader(pht('Welcome to Phabricator'));
     $setup_header = PhabricatorMarkupEngine::renderOneObject(id(new PhabricatorMarkupOneOff())->setContent(pht('=Setup and Configuration')), 'default', $viewer);
     $explore_header = PhabricatorMarkupEngine::renderOneObject(id(new PhabricatorMarkupOneOff())->setContent(pht('=Explore Phabricator')), 'default', $viewer);
     $quick_header = PhabricatorMarkupEngine::renderOneObject(id(new PhabricatorMarkupOneOff())->setContent(pht('=Quick Start Guides')), 'default', $viewer);
     return id(new PHUIDocumentView())->setHeader($header)->setFluid(true)->appendChild($setup_header)->appendChild($setup)->appendChild($explore_header)->appendChild($explore)->appendChild($quick_header)->appendChild($quick);
 }
 public function renderModuleStatus(AphrontRequest $request)
 {
     $viewer = $request->getViewer();
     $guide_items = new PhabricatorGuideListView();
     $title = pht('Resolve Setup Issues');
     $issues_resolved = !PhabricatorSetupCheck::getOpenSetupIssueKeys();
     $href = PhabricatorEnv::getURI('/config/issue/');
     if ($issues_resolved) {
         $icon = 'fa-check';
         $icon_bg = 'bg-green';
         $description = pht("You've resolved (or ignored) all outstanding setup issues.");
     } else {
         $icon = 'fa-warning';
         $icon_bg = 'bg-red';
         $description = pht('You have some unresolved setup issues to take care of.');
     }
     $item = id(new PhabricatorGuideItemView())->setTitle($title)->setHref($href)->setIcon($icon)->setIconBackground($icon_bg)->setDescription($description);
     $guide_items->addItem($item);
     $configs = id(new PhabricatorAuthProviderConfigQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->execute();
     $title = pht('Login and Registration');
     $href = PhabricatorEnv::getURI('/auth/');
     $have_auth = (bool) $configs;
     if ($have_auth) {
         $icon = 'fa-check';
         $icon_bg = 'bg-green';
         $description = pht("You've configured at least one authentication provider.");
     } else {
         $icon = 'fa-key';
         $icon_bg = 'bg-sky';
         $description = pht('Authentication providers allow users to register accounts and ' . 'log in to Phabricator.');
     }
     $item = id(new PhabricatorGuideItemView())->setTitle($title)->setHref($href)->setIcon($icon)->setIconBackground($icon_bg)->setDescription($description);
     $guide_items->addItem($item);
     $title = pht('Configure Phabricator');
     $href = PhabricatorEnv::getURI('/config/');
     // Just load any config value at all; if one exists the install has figured
     // out how to configure things.
     $have_config = (bool) id(new PhabricatorConfigEntry())->loadAllWhere('1 = 1 LIMIT 1');
     if ($have_config) {
         $icon = 'fa-check';
         $icon_bg = 'bg-green';
         $description = pht("You've configured at least one setting from the web interface.");
     } else {
         $icon = 'fa-sliders';
         $icon_bg = 'bg-sky';
         $description = pht('Learn how to configure mail and other options in Phabricator.');
     }
     $item = id(new PhabricatorGuideItemView())->setTitle($title)->setHref($href)->setIcon($icon)->setIconBackground($icon_bg)->setDescription($description);
     $guide_items->addItem($item);
     $title = pht('User Account Settings');
     $href = PhabricatorEnv::getURI('/settings/');
     $preferences = id(new PhabricatorUserPreferencesQuery())->setViewer($viewer)->withUsers(array($viewer))->executeOne();
     $have_settings = $preferences && $preferences->getPreferences();
     if ($have_settings) {
         $icon = 'fa-check';
         $icon_bg = 'bg-green';
         $description = pht("You've adjusted at least one setting on your account.");
     } else {
         $icon = 'fa-wrench';
         $icon_bg = 'bg-sky';
         $description = pht('Configure account settings for all users, or just yourself');
     }
     $item = id(new PhabricatorGuideItemView())->setTitle($title)->setHref($href)->setIcon($icon)->setIconBackground($icon_bg)->setDescription($description);
     $guide_items->addItem($item);
     $title = pht('Notification Server');
     $href = PhabricatorEnv::getURI('/config/edit/notification.servers/');
     $have_notifications = PhabricatorEnv::getEnvConfig('notification.servers');
     if ($have_notifications) {
         $icon = 'fa-check';
         $icon_bg = 'bg-green';
         $description = pht("You've set up a real-time notification server.");
     } else {
         $icon = 'fa-bell';
         $icon_bg = 'bg-sky';
         $description = pht('Phabricator can deliver notifications in real-time with WebSockets.');
     }
     $item = id(new PhabricatorGuideItemView())->setTitle($title)->setHref($href)->setIcon($icon)->setIconBackground($icon_bg)->setDescription($description);
     $guide_items->addItem($item);
     $intro = pht('Phabricator has been successfully installed. These next guides will ' . 'take you through configuration and new user orientation. ' . 'These steps are optional, and you can go through them in any order. ' . 'If you want to get back to this guide later on, you can find it in ' . '{icon globe} **Applications** under {icon map-o} **Guides**.');
     $intro = new PHUIRemarkupView($viewer, $intro);
     $intro = id(new PHUIDocumentViewPro())->appendChild($intro);
     return array($intro, $guide_items);
 }