Path: blob/master/src/view/page/menu/PhabricatorMainMenuView.php
12242 views
<?php12final class PhabricatorMainMenuView extends AphrontView {34private $controller;5private $applicationMenu;67public function setApplicationMenu(PHUIListView $application_menu) {8$this->applicationMenu = $application_menu;9return $this;10}1112public function getApplicationMenu() {13return $this->applicationMenu;14}1516public function setController(PhabricatorController $controller) {17$this->controller = $controller;18return $this;19}2021public function getController() {22return $this->controller;23}2425private static function getFavicons() {26$refs = array();2728$refs['favicon'] = id(new PhabricatorFaviconRef())29->setWidth(64)30->setHeight(64);3132$refs['message_favicon'] = id(new PhabricatorFaviconRef())33->setWidth(64)34->setHeight(64)35->setEmblems(36array(37'dot-pink',38null,39null,40null,41));4243id(new PhabricatorFaviconRefQuery())44->withRefs($refs)45->execute();4647return mpull($refs, 'getURI');48}4950public function render() {51$viewer = $this->getViewer();5253require_celerity_resource('phabricator-main-menu-view');5455$header_id = celerity_generate_unique_node_id();56$menu_bar = array();57$alerts = array();58$search_button = '';59$app_button = '';60$aural = null;6162$is_full = $this->isFullSession($viewer);6364if ($is_full) {65list($menu, $dropdowns, $aural) = $this->renderNotificationMenu();66if (array_filter($menu)) {67$alerts[] = $menu;68}69$menu_bar = array_merge($menu_bar, $dropdowns);70$app_button = $this->renderApplicationMenuButton();71$search_button = $this->renderSearchMenuButton($header_id);72} else if (!$viewer->isLoggedIn()) {73$app_button = $this->renderApplicationMenuButton();74if (PhabricatorEnv::getEnvConfig('policy.allow-public')) {75$search_button = $this->renderSearchMenuButton($header_id);76}77}7879if ($search_button) {80$search_menu = $this->renderPhabricatorSearchMenu();81} else {82$search_menu = null;83}8485if ($alerts) {86$alerts = javelin_tag(87'div',88array(89'class' => 'phabricator-main-menu-alerts',90'aural' => false,91),92$alerts);93}9495if ($aural) {96$aural = javelin_tag(97'span',98array(99'aural' => true,100),101phutil_implode_html(' ', $aural));102}103104$extensions = PhabricatorMainMenuBarExtension::getAllEnabledExtensions();105foreach ($extensions as $extension) {106$extension107->setViewer($viewer)108->setIsFullSession($is_full);109110$controller = $this->getController();111if ($controller) {112$extension->setController($controller);113$application = $controller->getCurrentApplication();114if ($application) {115$extension->setApplication($application);116}117}118}119120if (!$is_full) {121foreach ($extensions as $key => $extension) {122if ($extension->shouldRequireFullSession()) {123unset($extensions[$key]);124}125}126}127128foreach ($extensions as $key => $extension) {129if (!$extension->isExtensionEnabledForViewer($extension->getViewer())) {130unset($extensions[$key]);131}132}133134$menus = array();135foreach ($extensions as $extension) {136foreach ($extension->buildMainMenus() as $menu) {137$menus[] = $menu;138}139}140141// Because we display these with "float: right", reverse their order before142// rendering them into the document so that the extension order and display143// order are the same.144$menus = array_reverse($menus);145146foreach ($menus as $menu) {147$menu_bar[] = $menu;148}149150$classes = array();151$classes[] = 'phabricator-main-menu';152$classes[] = 'phabricator-main-menu-background';153154return phutil_tag(155'div',156array(157'class' => implode(' ', $classes),158'id' => $header_id,159),160array(161$app_button,162$search_button,163$this->renderPhabricatorLogo(),164$alerts,165$aural,166$search_menu,167$menu_bar,168));169}170171private function renderSearch() {172$viewer = $this->getViewer();173174$result = null;175176$keyboard_config = array(177'helpURI' => '/help/keyboardshortcut/',178);179180if ($viewer->isLoggedIn()) {181$show_search = $viewer->isUserActivated();182} else {183$show_search = PhabricatorEnv::getEnvConfig('policy.allow-public');184}185186if ($show_search) {187$search = new PhabricatorMainMenuSearchView();188$search->setViewer($viewer);189190$application = null;191$controller = $this->getController();192if ($controller) {193$application = $controller->getCurrentApplication();194}195if ($application) {196$search->setApplication($application);197}198199$result = $search;200$keyboard_config['searchID'] = $search->getID();201}202203$keyboard_config['pht'] = array(204'/' => pht('Give keyboard focus to the search box.'),205'?' => pht('Show keyboard shortcut help for the current page.'),206);207208Javelin::initBehavior(209'phabricator-keyboard-shortcuts',210$keyboard_config);211212if ($result) {213$result = id(new PHUIListItemView())214->addClass('phabricator-main-menu-search')215->appendChild($result);216}217218return $result;219}220221public function renderApplicationMenuButton() {222$dropdown = $this->renderApplicationMenu();223if (!$dropdown) {224return null;225}226227return id(new PHUIButtonView())228->setTag('a')229->setHref('#')230->setIcon('fa-bars')231->addClass('phabricator-core-user-menu')232->addClass('phabricator-core-user-mobile-menu')233->setNoCSS(true)234->setDropdownMenu($dropdown)235->setAuralLabel(pht('Page Menu'));236}237238private function renderApplicationMenu() {239$viewer = $this->getViewer();240$view = $this->getApplicationMenu();241if ($view) {242$items = $view->getItems();243$view = id(new PhabricatorActionListView())244->setViewer($viewer);245foreach ($items as $item) {246$view->addAction(247id(new PhabricatorActionView())248->setName($item->getName())249->setHref($item->getHref())250->setType($item->getType()));251}252}253return $view;254}255256public function renderSearchMenuButton($header_id) {257$button_id = celerity_generate_unique_node_id();258return javelin_tag(259'a',260array(261'class' => 'phabricator-main-menu-search-button '.262'phabricator-expand-application-menu',263'sigil' => 'jx-toggle-class',264'meta' => array(265'map' => array(266$header_id => 'phabricator-search-menu-expanded',267$button_id => 'menu-icon-selected',268),269),270),271phutil_tag(272'span',273array(274'class' => 'phabricator-menu-button-icon phui-icon-view '.275'phui-font-fa fa-search',276'id' => $button_id,277),278''));279}280281private function renderPhabricatorSearchMenu() {282283$view = new PHUIListView();284$view->addClass('phabricator-search-menu');285286$search = $this->renderSearch();287if ($search) {288$view->addMenuItem($search);289}290291return $view;292}293294private function renderPhabricatorLogo() {295$custom_header = PhabricatorCustomLogoConfigType::getLogoImagePHID();296297$logo_style = array();298if ($custom_header) {299$cache = PhabricatorCaches::getImmutableCache();300$cache_key_logo = 'ui.custom-header.logo-phid.v3.'.$custom_header;301302$logo_uri = $cache->getKey($cache_key_logo);303if (!$logo_uri) {304// NOTE: If the file policy has been changed to be restrictive, we'll305// miss here and just show the default logo. The cache will fill later306// when someone who can see the file loads the page. This might be a307// little spooky, see T11982.308$files = id(new PhabricatorFileQuery())309->setViewer($this->getViewer())310->withPHIDs(array($custom_header))311->execute();312$file = head($files);313if ($file) {314$logo_uri = $file->getViewURI();315$cache->setKey($cache_key_logo, $logo_uri);316}317}318319if ($logo_uri) {320$logo_style[] = 'background-size: 40px 40px;';321$logo_style[] = 'background-position: 0 0;';322$logo_style[] = 'background-image: url('.$logo_uri.')';323}324}325326$logo_node = phutil_tag(327'span',328array(329'class' => 'phabricator-main-menu-eye',330'style' => implode(' ', $logo_style),331));332333334$wordmark_text = PhabricatorCustomLogoConfigType::getLogoWordmark();335if ($wordmark_text === null || !strlen($wordmark_text)) {336$wordmark_text = PlatformSymbols::getPlatformServerName();337}338339$wordmark_node = phutil_tag(340'span',341array(342'class' => 'phabricator-wordmark',343),344$wordmark_text);345346return phutil_tag(347'a',348array(349'class' => 'phabricator-main-menu-brand',350'href' => '/',351),352array(353javelin_tag(354'span',355array(356'aural' => true,357),358pht('Home')),359$logo_node,360$wordmark_node,361));362}363364private function renderNotificationMenu() {365$viewer = $this->getViewer();366367require_celerity_resource('phabricator-notification-css');368require_celerity_resource('phabricator-notification-menu-css');369370$container_classes = array('alert-notifications');371$aural = array();372373$dropdown_query = id(new AphlictDropdownDataQuery())374->setViewer($viewer);375$dropdown_data = $dropdown_query->execute();376377$message_tag = '';378$message_notification_dropdown = '';379$conpherence_app = 'PhabricatorConpherenceApplication';380$conpherence_data = $dropdown_data[$conpherence_app];381if ($conpherence_data['isInstalled']) {382$message_id = celerity_generate_unique_node_id();383$message_count_id = celerity_generate_unique_node_id();384$message_dropdown_id = celerity_generate_unique_node_id();385386$message_count_number = $conpherence_data['rawCount'];387388if ($message_count_number) {389$aural[] = phutil_tag(390'a',391array(392'href' => '/conpherence/',393),394pht(395'%s unread messages.',396new PhutilNumber($message_count_number)));397} else {398$aural[] = pht('No messages.');399}400401$message_count_tag = phutil_tag(402'span',403array(404'id' => $message_count_id,405'class' => 'phabricator-main-menu-message-count',406),407$conpherence_data['count']);408409$message_icon_tag = javelin_tag(410'span',411array(412'class' => 'phabricator-main-menu-message-icon phui-icon-view '.413'phui-font-fa fa-comments',414'sigil' => 'menu-icon',415),416'');417418if ($message_count_number) {419$container_classes[] = 'message-unread';420}421422$message_tag = phutil_tag(423'a',424array(425'href' => '/conpherence/',426'class' => implode(' ', $container_classes),427'id' => $message_id,428),429array(430$message_icon_tag,431$message_count_tag,432));433434Javelin::initBehavior(435'aphlict-dropdown',436array(437'bubbleID' => $message_id,438'countID' => $message_count_id,439'dropdownID' => $message_dropdown_id,440'loadingText' => pht('Loading...'),441'uri' => '/conpherence/panel/',442'countType' => $conpherence_data['countType'],443'countNumber' => $message_count_number,444'unreadClass' => 'message-unread',445) + self::getFavicons());446447$message_notification_dropdown = javelin_tag(448'div',449array(450'id' => $message_dropdown_id,451'class' => 'phabricator-notification-menu',452'sigil' => 'phabricator-notification-menu',453'style' => 'display: none;',454),455'');456}457458$bubble_tag = '';459$notification_dropdown = '';460$notification_app = 'PhabricatorNotificationsApplication';461$notification_data = $dropdown_data[$notification_app];462if ($notification_data['isInstalled']) {463$count_id = celerity_generate_unique_node_id();464$dropdown_id = celerity_generate_unique_node_id();465$bubble_id = celerity_generate_unique_node_id();466467$count_number = $notification_data['rawCount'];468469if ($count_number) {470$aural[] = phutil_tag(471'a',472array(473'href' => '/notification/',474),475pht(476'%s unread notifications.',477new PhutilNumber($count_number)));478} else {479$aural[] = pht('No notifications.');480}481482$count_tag = phutil_tag(483'span',484array(485'id' => $count_id,486'class' => 'phabricator-main-menu-alert-count',487),488$notification_data['count']);489490$icon_tag = javelin_tag(491'span',492array(493'class' => 'phabricator-main-menu-alert-icon phui-icon-view '.494'phui-font-fa fa-bell',495'sigil' => 'menu-icon',496),497'');498499if ($count_number) {500$container_classes[] = 'alert-unread';501}502503$bubble_tag = phutil_tag(504'a',505array(506'href' => '/notification/',507'class' => implode(' ', $container_classes),508'id' => $bubble_id,509),510array($icon_tag, $count_tag));511512Javelin::initBehavior(513'aphlict-dropdown',514array(515'bubbleID' => $bubble_id,516'countID' => $count_id,517'dropdownID' => $dropdown_id,518'loadingText' => pht('Loading...'),519'uri' => '/notification/panel/',520'countType' => $notification_data['countType'],521'countNumber' => $count_number,522'unreadClass' => 'alert-unread',523) + self::getFavicons());524525$notification_dropdown = javelin_tag(526'div',527array(528'id' => $dropdown_id,529'class' => 'phabricator-notification-menu',530'sigil' => 'phabricator-notification-menu',531'style' => 'display: none;',532),533'');534}535536// Admin Level Urgent Notification Channel537$setup_tag = '';538$setup_notification_dropdown = '';539if ($viewer && $viewer->getIsAdmin()) {540$open = PhabricatorSetupCheck::getOpenSetupIssueKeys();541if ($open) {542$setup_id = celerity_generate_unique_node_id();543$setup_count_id = celerity_generate_unique_node_id();544$setup_dropdown_id = celerity_generate_unique_node_id();545546$setup_count_number = count($open);547548if ($setup_count_number) {549$aural[] = phutil_tag(550'a',551array(552'href' => '/config/issue/',553),554pht(555'%s unresolved issues.',556new PhutilNumber($setup_count_number)));557} else {558$aural[] = pht('No issues.');559}560561$setup_count_tag = phutil_tag(562'span',563array(564'id' => $setup_count_id,565'class' => 'phabricator-main-menu-setup-count',566),567$setup_count_number);568569$setup_icon_tag = javelin_tag(570'span',571array(572'class' => 'phabricator-main-menu-setup-icon phui-icon-view '.573'phui-font-fa fa-exclamation-circle',574'sigil' => 'menu-icon',575),576'');577578if ($setup_count_number) {579$container_classes[] = 'setup-unread';580}581582$setup_tag = phutil_tag(583'a',584array(585'href' => '/config/issue/',586'class' => implode(' ', $container_classes),587'id' => $setup_id,588),589array(590$setup_icon_tag,591$setup_count_tag,592));593594Javelin::initBehavior(595'aphlict-dropdown',596array(597'bubbleID' => $setup_id,598'countID' => $setup_count_id,599'dropdownID' => $setup_dropdown_id,600'loadingText' => pht('Loading...'),601'uri' => '/config/issue/panel/',602'countType' => null,603'countNumber' => null,604'unreadClass' => 'setup-unread',605) + self::getFavicons());606607$setup_notification_dropdown = javelin_tag(608'div',609array(610'id' => $setup_dropdown_id,611'class' => 'phabricator-notification-menu',612'sigil' => 'phabricator-notification-menu',613'style' => 'display: none;',614),615'');616}617}618619$user_dropdown = null;620$user_tag = null;621if ($viewer->isLoggedIn()) {622if (!$viewer->getIsEmailVerified()) {623$bubble_id = celerity_generate_unique_node_id();624$count_id = celerity_generate_unique_node_id();625$dropdown_id = celerity_generate_unique_node_id();626627$settings_uri = id(new PhabricatorEmailAddressesSettingsPanel())628->setViewer($viewer)629->setUser($viewer)630->getPanelURI();631632$user_icon = javelin_tag(633'span',634array(635'class' => 'phabricator-main-menu-setup-icon phui-icon-view '.636'phui-font-fa fa-user',637'sigil' => 'menu-icon',638));639640$user_count = javelin_tag(641'span',642array(643'class' => 'phabricator-main-menu-setup-count',644'id' => $count_id,645),6461);647648$user_tag = phutil_tag(649'a',650array(651'href' => $settings_uri,652'class' => 'setup-unread',653'id' => $bubble_id,654),655array(656$user_icon,657$user_count,658));659660Javelin::initBehavior(661'aphlict-dropdown',662array(663'bubbleID' => $bubble_id,664'countID' => $count_id,665'dropdownID' => $dropdown_id,666'loadingText' => pht('Loading...'),667'uri' => '/settings/issue/',668'unreadClass' => 'setup-unread',669));670671$user_dropdown = javelin_tag(672'div',673array(674'id' => $dropdown_id,675'class' => 'phabricator-notification-menu',676'sigil' => 'phabricator-notification-menu',677'style' => 'display: none;',678));679}680}681682$dropdowns = array(683$notification_dropdown,684$message_notification_dropdown,685$setup_notification_dropdown,686$user_dropdown,687);688689return array(690array(691$bubble_tag,692$message_tag,693$setup_tag,694$user_tag,695),696$dropdowns,697$aural,698);699}700701private function isFullSession(PhabricatorUser $viewer) {702if (!$viewer->isLoggedIn()) {703return false;704}705706if (!$viewer->isUserActivated()) {707return false;708}709710if (!$viewer->hasSession()) {711return false;712}713714$session = $viewer->getSession();715if ($session->getIsPartial()) {716return false;717}718719if (!$session->getSignedLegalpadDocuments()) {720return false;721}722723$mfa_key = 'security.require-multi-factor-auth';724$need_mfa = PhabricatorEnv::getEnvConfig($mfa_key);725if ($need_mfa) {726$have_mfa = $viewer->getIsEnrolledInMultiFactor();727if (!$have_mfa) {728return false;729}730}731732return true;733}734735}736737738