diff --git a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php index b789fe57d0..a95ec0e1c9 100644 --- a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php +++ b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php @@ -1,252 +1,259 @@ newIssue('config.unknown.'.$key) ->setShortName($short) ->setName($name) ->setSummary($summary); $stack = PhabricatorEnv::getConfigSourceStack(); $stack = $stack->getStack(); $found = array(); $found_local = false; $found_database = false; foreach ($stack as $source_key => $source) { $value = $source->getKeys(array($key)); if ($value) { $found[] = $source->getName(); if ($source instanceof PhabricatorConfigDatabaseSource) { $found_database = true; } if ($source instanceof PhabricatorConfigLocalSource) { $found_local = true; } } } $message = $message."\n\n".pht( 'This configuration value is defined in these %d '. 'configuration source(s): %s.', count($found), implode(', ', $found)); $issue->setMessage($message); if ($found_local) { $command = csprintf('phabricator/ $ ./bin/config delete %s', $key); $issue->addCommand($command); } if ($found_database) { $issue->addPhabricatorConfig($key); } } } /** * Return a map of deleted config options. Keys are option keys; values are * explanations of what happened to the option. */ public static function getAncientConfig() { $reason_auth = pht( 'This option has been migrated to the "Auth" application. Your old '. 'configuration is still in effect, but now stored in "Auth" instead of '. 'configuration. Going forward, you can manage authentication from '. 'the web UI.'); $auth_config = array( 'controller.oauth-registration', 'auth.password-auth-enabled', 'facebook.auth-enabled', 'facebook.registration-enabled', 'facebook.auth-permanent', 'facebook.application-id', 'facebook.application-secret', 'facebook.require-https-auth', 'github.auth-enabled', 'github.registration-enabled', 'github.auth-permanent', 'github.application-id', 'github.application-secret', 'google.auth-enabled', 'google.registration-enabled', 'google.auth-permanent', 'google.application-id', 'google.application-secret', 'ldap.auth-enabled', 'ldap.hostname', 'ldap.port', 'ldap.base_dn', 'ldap.search_attribute', 'ldap.search-first', 'ldap.username-attribute', 'ldap.real_name_attributes', 'ldap.activedirectory_domain', 'ldap.version', 'ldap.referrals', 'ldap.anonymous-user-name', 'ldap.anonymous-user-password', 'ldap.start-tls', 'disqus.auth-enabled', 'disqus.registration-enabled', 'disqus.auth-permanent', 'disqus.application-id', 'disqus.application-secret', 'phabricator.oauth-uri', 'phabricator.auth-enabled', 'phabricator.registration-enabled', 'phabricator.auth-permanent', 'phabricator.application-id', 'phabricator.application-secret', ); $ancient_config = array_fill_keys($auth_config, $reason_auth); $markup_reason = pht( 'Custom remarkup rules are now added by subclassing '. 'PhabricatorRemarkupCustomInlineRule or '. 'PhabricatorRemarkupCustomBlockRule.'); $session_reason = pht( 'Sessions now expire and are garbage collected rather than having an '. 'arbitrary concurrency limit.'); $differential_field_reason = pht( 'All Differential fields are now managed through the configuration '. 'option "%s". Use that option to configure which fields are shown.', 'differential.fields'); $reply_domain_reason = pht( 'Individual application reply handler domains have been removed. '. 'Configure a reply domain with "%s".', 'metamta.reply-handler-domain'); $reply_handler_reason = pht( 'Reply handlers can no longer be overridden with configuration.'); + $monospace_reason = pht( + 'Phabricator no longer supports global customization of monospaced '. + 'fonts.'); + $ancient_config += array( 'phid.external-loaders' => pht( 'External loaders have been replaced. Extend `PhabricatorPHIDType` '. 'to implement new PHID and handle types.'), 'maniphest.custom-task-extensions-class' => pht( 'Maniphest fields are now loaded automatically. You can configure '. 'them with `maniphest.fields`.'), 'maniphest.custom-fields' => pht( 'Maniphest fields are now defined in '. '`maniphest.custom-field-definitions`. Existing definitions have '. 'been migrated.'), 'differential.custom-remarkup-rules' => $markup_reason, 'differential.custom-remarkup-block-rules' => $markup_reason, 'auth.sshkeys.enabled' => pht( 'SSH keys are now actually useful, so they are always enabled.'), 'differential.anonymous-access' => pht( 'Phabricator now has meaningful global access controls. See '. '`policy.allow-public`.'), 'celerity.resource-path' => pht( 'An alternate resource map is no longer supported. Instead, use '. 'multiple maps. See T4222.'), 'metamta.send-immediately' => pht( 'Mail is now always delivered by the daemons.'), 'auth.sessions.conduit' => $session_reason, 'auth.sessions.web' => $session_reason, 'tokenizer.ondemand' => pht( 'Phabricator now manages typeahead strategies automatically.'), 'differential.revision-custom-detail-renderer' => pht( 'Obsolete; use standard rendering events instead.'), 'differential.show-host-field' => $differential_field_reason, 'differential.show-test-plan-field' => $differential_field_reason, 'differential.field-selector' => $differential_field_reason, 'phabricator.show-beta-applications' => pht( 'This option has been renamed to `phabricator.show-prototypes` '. 'to emphasize the unfinished nature of many prototype applications. '. 'Your existing setting has been migrated.'), 'notification.user' => pht( 'The notification server no longer requires root permissions. Start '. 'the server as the user you want it to run under.'), 'notification.debug' => pht( 'Notifications no longer have a dedicated debugging mode.'), 'translation.provider' => pht( 'The translation implementation has changed and providers are no '. 'longer used or supported.'), 'config.mask' => pht( 'Use `config.hide` instead of this option.'), 'phd.start-taskmasters' => pht( 'Taskmasters now use an autoscaling pool. You can configure the '. 'pool size with `phd.taskmasters`.'), 'storage.engine-selector' => pht( 'Phabricator now automatically discovers available storage engines '. 'at runtime.'), 'storage.upload-size-limit' => pht( 'Phabricator now supports arbitrarily large files. Consult the '. 'documentation for configuration details.'), 'security.allow-outbound-http' => pht( 'This option has been replaced with the more granular option '. '`security.outbound-blacklist`.'), 'metamta.reply.show-hints' => pht( 'Phabricator no longer shows reply hints in mail.'), 'metamta.differential.reply-handler-domain' => $reply_domain_reason, 'metamta.diffusion.reply-handler-domain' => $reply_domain_reason, 'metamta.macro.reply-handler-domain' => $reply_domain_reason, 'metamta.maniphest.reply-handler-domain' => $reply_domain_reason, 'metamta.pholio.reply-handler-domain' => $reply_domain_reason, 'metamta.diffusion.reply-handler' => $reply_handler_reason, 'metamta.differential.reply-handler' => $reply_handler_reason, 'metamta.maniphest.reply-handler' => $reply_handler_reason, 'metamta.package.reply-handler' => $reply_handler_reason, 'metamta.precedence-bulk' => pht( 'Phabricator now always sends transaction mail with '. '"Precedence: bulk" to improve deliverability.'), + + 'style.monospace' => $monospace_reason, + 'style.monospace.windows' => $monospace_reason, ); return $ancient_config; } } diff --git a/src/applications/config/option/PhabricatorSyntaxHighlightingConfigOptions.php b/src/applications/config/option/PhabricatorSyntaxHighlightingConfigOptions.php index ab40f72898..90c1e87906 100644 --- a/src/applications/config/option/PhabricatorSyntaxHighlightingConfigOptions.php +++ b/src/applications/config/option/PhabricatorSyntaxHighlightingConfigOptions.php @@ -1,165 +1,143 @@ newOption( 'syntax-highlighter.engine', 'class', 'PhutilDefaultSyntaxHighlighterEngine') ->setBaseClass('PhutilSyntaxHighlighterEngine') ->setSummary(pht('Default non-pygments syntax highlighter engine.')) ->setDescription( pht( 'Phabricator can highlight PHP by default and use Pygments for '. 'other languages if enabled. You can provide a custom '. 'highlighter engine by extending class '. 'PhutilSyntaxHighlighterEngine.')), $this->newOption('pygments.enabled', 'bool', false) ->setSummary( pht('Should Phabricator use Pygments to highlight code?')) ->setBoolOptions( array( pht('Use Pygments'), pht('Do Not Use Pygments'), )) ->setDescription( pht( 'Phabricator supports syntax highlighting a few languages by '. 'default, but you can install Pygments (a third-party syntax '. 'highlighting tool) to provide support for many more languages.'. "\n\n". 'To install Pygments, visit '. '[[ http://pygments.org | pygments.org ]] and follow the '. 'download and install instructions.'. "\n\n". 'Once Pygments is installed, enable this option '. '(`pygments.enabled`) to make Phabricator use Pygments when '. 'highlighting source code.'. "\n\n". 'After you install and enable Pygments, newly created source '. 'code (like diffs and pastes) should highlight correctly. '. 'You may need to clear Phabricator\'s caches to get previously '. 'existing source code to highlight. For instructions on '. 'managing caches, see [[ %s | Managing Caches ]].', $caches_href)), $this->newOption( 'pygments.dropdown-choices', 'wild', array( 'apacheconf' => 'Apache Configuration', 'bash' => 'Bash Scripting', 'brainfuck' => 'Brainf*ck', 'c' => 'C', 'coffee-script' => 'CoffeeScript', 'cpp' => 'C++', 'css' => 'CSS', 'd' => 'D', 'diff' => 'Diff', 'django' => 'Django Templating', 'erb' => 'Embedded Ruby/ERB', 'erlang' => 'Erlang', 'go' => 'Golang', 'groovy' => 'Groovy', 'haskell' => 'Haskell', 'html' => 'HTML', 'invisible' => 'Invisible', 'java' => 'Java', 'js' => 'Javascript', 'json' => 'JSON', 'mysql' => 'MySQL', 'objc' => 'Objective-C', 'perl' => 'Perl', 'php' => 'PHP', 'puppet' => 'Puppet', 'rest' => 'reStructuredText', 'text' => 'Plain Text', 'python' => 'Python', 'rainbow' => 'Rainbow', 'remarkup' => 'Remarkup', 'ruby' => 'Ruby', 'xml' => 'XML', 'yaml' => 'YAML', )) ->setSummary( pht('Set the language list which appears in dropdowns.')) ->setDescription( pht( 'In places that we display a dropdown to syntax-highlight code, '. 'this is where that list is defined.')), $this->newOption( 'syntax.filemap', 'wild', array( '@\.arcconfig$@' => 'js', '@\.arclint$@' => 'js', '@\.divinerconfig$@' => 'js', )) ->setSummary( pht('Override what language files (based on filename) highlight as.')) ->setDescription( pht( 'This is an override list of regular expressions which allows '. 'you to choose what language files are highlighted as. If your '. 'projects have certain rules about filenames or use unusual or '. 'ambiguous language extensions, you can create a mapping here. '. 'This is an ordered dictionary of regular expressions which will '. 'be tested against the filename. They should map to either an '. 'explicit language as a string value, or a numeric index into '. 'the captured groups as an integer.')) ->addExample('{"@\\.xyz$@": "php"}', pht('Highlight *.xyz as PHP.')) ->addExample( '{"@/httpd\\.conf@": "apacheconf"}', pht('Highlight httpd.conf as "apacheconf".')) ->addExample( '{"@\\.([^.]+)\\.bak$@": 1}', pht( "Treat all '*.x.bak' file as '.x'. NOTE: We map to capturing group ". "1 by specifying the mapping as '1'")), - $this->newOption( - 'style.monospace', - 'string', - null) - ->setLocked(true) - ->setSummary( - pht('Default monospace font.')) - ->setDescription( - pht( - "Set the default monospaced font style for users who haven't set ". - "a custom style.")), - $this->newOption( - 'style.monospace.windows', - 'string', - null) - ->setLocked(true) - ->setSummary( - pht('Default monospace font for clients on Windows.')) - ->setDescription( - pht( - "Set the default monospaced font style for users who haven't set ". - "a custom style and are using Windows.")), ); } } diff --git a/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php index 0ba3521a80..b58d22a748 100644 --- a/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php @@ -1,178 +1,172 @@ getUser(); $preferences = $user->loadPreferences(); $pref_monospaced = PhabricatorUserPreferences::PREFERENCE_MONOSPACED; $pref_editor = PhabricatorUserPreferences::PREFERENCE_EDITOR; $pref_multiedit = PhabricatorUserPreferences::PREFERENCE_MULTIEDIT; $pref_titles = PhabricatorUserPreferences::PREFERENCE_TITLES; $pref_monospaced_textareas = PhabricatorUserPreferences::PREFERENCE_MONOSPACED_TEXTAREAS; $errors = array(); $e_editor = null; if ($request->isFormPost()) { $monospaced = $request->getStr($pref_monospaced); // Prevent the user from doing stupid things. $monospaced = preg_replace('/[^a-z0-9 ,".]+/i', '', $monospaced); $preferences->setPreference($pref_titles, $request->getStr($pref_titles)); $preferences->setPreference($pref_editor, $request->getStr($pref_editor)); $preferences->setPreference( $pref_multiedit, $request->getStr($pref_multiedit)); $preferences->setPreference($pref_monospaced, $monospaced); $preferences->setPreference( $pref_monospaced_textareas, $request->getStr($pref_monospaced_textareas)); $editor_pattern = $preferences->getPreference($pref_editor); if (strlen($editor_pattern)) { $ok = PhabricatorHelpEditorProtocolController::hasAllowedProtocol( $editor_pattern); if (!$ok) { $allowed_key = 'uri.allowed-editor-protocols'; $allowed_protocols = PhabricatorEnv::getEnvConfig($allowed_key); $proto_names = array(); foreach (array_keys($allowed_protocols) as $protocol) { $proto_names[] = $protocol.'://'; } $errors[] = pht( 'Editor link has an invalid or missing protocol. You must '. 'use a whitelisted editor protocol from this list: %s. To '. 'add protocols, update %s.', implode(', ', $proto_names), phutil_tag('tt', array(), $allowed_key)); $e_editor = pht('Invalid'); } } if (!$errors) { $preferences->save(); return id(new AphrontRedirectResponse()) ->setURI($this->getPanelURI('?saved=true')); } } $example_string = << PhabricatorEnv::getDoclink( 'User Guide: Configuring an External Editor'), ), pht('User Guide: Configuring an External Editor')); - $font_default = PhabricatorEnv::getEnvConfig('style.monospace'); - $pref_monospaced_textareas_value = $preferences ->getPreference($pref_monospaced_textareas); if (!$pref_monospaced_textareas_value) { $pref_monospaced_textareas_value = 'disabled'; } $editor_instructions = pht('Link to edit files in external editor. '. '%%f is replaced by filename, %%l by line number, %%r by repository '. 'callsign, %%%% by literal %%. For documentation, see: %s', $editor_doc_link); $form = id(new AphrontFormView()) ->setUser($user) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel(pht('Page Titles')) ->setName($pref_titles) ->setValue($preferences->getPreference($pref_titles)) ->setOptions( array( 'glyph' => pht("In page titles, show Tool names as unicode glyphs: ". "\xE2\x9A\x99"), 'text' => pht('In page titles, show Tool names as plain text: '. '[Differential]'), ))) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Editor Link')) ->setName($pref_editor) ->setCaption($editor_instructions) ->setError($e_editor) ->setValue($preferences->getPreference($pref_editor))) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel(pht('Edit Multiple Files')) ->setName($pref_multiedit) ->setOptions(array( '' => pht('Supported (paths separated by spaces)'), 'disable' => pht('Not Supported'), )) ->setValue($preferences->getPreference($pref_multiedit))) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Monospaced Font')) ->setName($pref_monospaced) - // Check plz - ->setCaption(hsprintf( - '%s
(%s: %s)', - pht('Overrides default fonts in tools like Differential.'), - pht('Default'), - $font_default)) + ->setCaption( + pht('Overrides default fonts in tools like Differential.')) ->setValue($preferences->getPreference($pref_monospaced))) ->appendChild( id(new AphrontFormMarkupControl()) ->setValue(phutil_tag( 'pre', array('class' => 'PhabricatorMonospaced'), $example_string))) ->appendChild( id(new AphrontFormRadioButtonControl()) ->setLabel(pht('Monospaced Textareas')) ->setName($pref_monospaced_textareas) ->setValue($pref_monospaced_textareas_value) ->addButton('enabled', pht('Enabled'), pht('Show all textareas using the monospaced font defined above.')) ->addButton('disabled', pht('Disabled'), null)); $form->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Save Preferences'))); $form_box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Display Preferences')) ->setFormErrors($errors) ->setFormSaved($request->getStr('saved') === 'true') ->setForm($form); return array( $form_box, ); } } diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php index 61709265f4..1c84d46ff5 100644 --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -1,627 +1,613 @@ showFooter = $show_footer; return $this; } public function getShowFooter() { return $this->showFooter; } public function setApplicationMenu(PHUIListView $application_menu) { $this->applicationMenu = $application_menu; return $this; } public function getApplicationMenu() { return $this->applicationMenu; } public function setApplicationName($application_name) { $this->applicationName = $application_name; return $this; } public function setDisableConsole($disable) { $this->disableConsole = $disable; return $this; } public function getApplicationName() { return $this->applicationName; } public function setBaseURI($base_uri) { $this->baseURI = $base_uri; return $this; } public function getBaseURI() { return $this->baseURI; } public function setShowChrome($show_chrome) { $this->showChrome = $show_chrome; return $this; } public function getShowChrome() { return $this->showChrome; } public function appendPageObjects(array $objs) { foreach ($objs as $obj) { $this->pageObjects[] = $obj; } } public function setShowDurableColumn($show) { $this->showDurableColumn = $show; return $this; } public function getShowDurableColumn() { $request = $this->getRequest(); if (!$request) { return false; } $viewer = $request->getUser(); if (!$viewer->isLoggedIn()) { return false; } $conpherence_installed = PhabricatorApplication::isClassInstalledForViewer( 'PhabricatorConpherenceApplication', $viewer); if (!$conpherence_installed) { return false; } $patterns = $this->getQuicksandURIPatternBlacklist(); $path = $request->getRequestURI()->getPath(); foreach ($patterns as $pattern) { if (preg_match('(^'.$pattern.'$)', $path)) { return false; } } return true; } public function getDurableColumnVisible() { $column_key = PhabricatorUserPreferences::PREFERENCE_CONPHERENCE_COLUMN; return (bool)$this->getUserPreference($column_key, 0); } public function getTitle() { $glyph_key = PhabricatorUserPreferences::PREFERENCE_TITLES; if ($this->getUserPreference($glyph_key) == 'text') { $use_glyph = false; } else { $use_glyph = true; } $title = parent::getTitle(); $prefix = null; if ($use_glyph) { $prefix = $this->getGlyph(); } else { $application_name = $this->getApplicationName(); if (strlen($application_name)) { $prefix = '['.$application_name.']'; } } if (strlen($prefix)) { $title = $prefix.' '.$title; } return $title; } protected function willRenderPage() { parent::willRenderPage(); if (!$this->getRequest()) { throw new Exception( pht( 'You must set the Request to render a PhabricatorStandardPageView.')); } $console = $this->getConsole(); require_celerity_resource('phabricator-core-css'); require_celerity_resource('phabricator-zindex-css'); require_celerity_resource('phui-button-css'); require_celerity_resource('phui-spacing-css'); require_celerity_resource('phui-form-css'); require_celerity_resource('sprite-gradient-css'); require_celerity_resource('phabricator-standard-page-view'); require_celerity_resource('conpherence-durable-column-view'); Javelin::initBehavior('workflow', array()); $request = $this->getRequest(); $user = null; if ($request) { $user = $request->getUser(); } if ($user) { $default_img_uri = celerity_get_resource_uri( 'rsrc/image/icon/fatcow/document_black.png'); $download_form = phabricator_form( $user, array( 'action' => '#', 'method' => 'POST', 'class' => 'lightbox-download-form', 'sigil' => 'download', ), phutil_tag( 'button', array(), pht('Download'))); Javelin::initBehavior( 'lightbox-attachments', array( 'defaultImageUri' => $default_img_uri, 'downloadForm' => $download_form, )); } Javelin::initBehavior('aphront-form-disable-on-submit'); Javelin::initBehavior('toggle-class', array()); Javelin::initBehavior('history-install'); Javelin::initBehavior('phabricator-gesture'); $current_token = null; if ($user) { $current_token = $user->getCSRFToken(); } Javelin::initBehavior( 'refresh-csrf', array( 'tokenName' => AphrontRequest::getCSRFTokenName(), 'header' => AphrontRequest::getCSRFHeaderName(), 'current' => $current_token, )); Javelin::initBehavior('device'); if ($user->hasSession()) { $hisec = ($user->getSession()->getHighSecurityUntil() - time()); if ($hisec > 0) { $remaining_time = phutil_format_relative_time($hisec); Javelin::initBehavior( 'high-security-warning', array( 'uri' => '/auth/session/downgrade/', 'message' => pht( 'Your session is in high security mode. When you '. 'finish using it, click here to leave.', $remaining_time), )); } } if ($console) { require_celerity_resource('aphront-dark-console-css'); $headers = array(); if (DarkConsoleXHProfPluginAPI::isProfilerStarted()) { $headers[DarkConsoleXHProfPluginAPI::getProfilerHeader()] = 'page'; } if (DarkConsoleServicesPlugin::isQueryAnalyzerRequested()) { $headers[DarkConsoleServicesPlugin::getQueryAnalyzerHeader()] = true; } Javelin::initBehavior( 'dark-console', array( // NOTE: We use a generic label here to prevent input reflection // and mitigate compression attacks like BREACH. See discussion in // T3684. 'uri' => pht('Main Request'), 'selected' => $user ? $user->getConsoleTab() : null, 'visible' => $user ? (int)$user->getConsoleVisible() : true, 'headers' => $headers, )); // Change this to initBehavior when there is some behavior to initialize require_celerity_resource('javelin-behavior-error-log'); } if ($user) { $viewer = $user; } else { $viewer = new PhabricatorUser(); } $menu = id(new PhabricatorMainMenuView()) ->setUser($viewer); if ($this->getController()) { $menu->setController($this->getController()); } if ($this->getApplicationMenu()) { $menu->setApplicationMenu($this->getApplicationMenu()); } $this->menuContent = $menu->render(); } protected function getHead() { - $monospaced = PhabricatorEnv::getEnvConfig('style.monospace'); - $monospaced_win = PhabricatorEnv::getEnvConfig('style.monospace.windows'); + $monospaced = null; $request = $this->getRequest(); if ($request) { $user = $request->getUser(); if ($user) { - $pref = $user->loadPreferences()->getPreference( + $monospaced = $user->loadPreferences()->getPreference( PhabricatorUserPreferences::PREFERENCE_MONOSPACED); - $monospaced = nonempty($pref, $monospaced); - $monospaced_win = nonempty($pref, $monospaced_win); } } $response = CelerityAPI::getStaticResourceResponse(); $font_css = null; if (!empty($monospaced)) { $font_css = hsprintf( '', $monospaced); } - $font_css_win = null; - if (!empty($monospaced_win)) { - $font_css_win = hsprintf( - '', $monospaced_win); - } - return hsprintf( - '%s%s%s%s', + '%s%s%s', parent::getHead(), $font_css, - $font_css_win, $response->renderSingleResource('javelin-magical-init', 'phabricator')); } public function setGlyph($glyph) { $this->glyph = $glyph; return $this; } public function getGlyph() { return $this->glyph; } protected function willSendResponse($response) { $request = $this->getRequest(); $response = parent::willSendResponse($response); $console = $request->getApplicationConfiguration()->getConsole(); if ($console) { $response = PhutilSafeHTML::applyFunction( 'str_replace', hsprintf(''), $console->render($request), $response); } return $response; } protected function getBody() { $user = null; $request = $this->getRequest(); if ($request) { $user = $request->getUser(); } $header_chrome = null; if ($this->getShowChrome()) { $header_chrome = $this->menuContent; } $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) { $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)))); } } Javelin::initBehavior( 'scrollbar', array( 'nodeID' => 'phabricator-standard-page', 'isMainContent' => true, )); $main_page = phutil_tag( 'div', array( 'id' => 'phabricator-standard-page', 'class' => 'phabricator-standard-page', ), array( $developer_warning, $setup_warning, $header_chrome, 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) ->setVisible($is_visible) ->setInitialLoad(true); } Javelin::initBehavior('quicksand-blacklist', array( 'patterns' => $this->getQuicksandURIPatternBlacklist(), )); return phutil_tag( 'div', array( 'class' => 'main-page-frame', ), array( $main_page, $durable_column, )); } private function renderPageBodyContent() { $console = $this->getConsole(); return array( ($console ? hsprintf('') : null), parent::getBody(), $this->renderFooter(), ); } protected function getTail() { $request = $this->getRequest(); $user = $request->getUser(); $tail = array( parent::getTail(), ); $response = CelerityAPI::getStaticResourceResponse(); if (PhabricatorEnv::getEnvConfig('notification.enabled')) { if ($user && $user->isLoggedIn()) { $client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri'); $client_uri = new PhutilURI($client_uri); if ($client_uri->getDomain() == 'localhost') { $this_host = $this->getRequest()->getHost(); $this_host = new PhutilURI('http://'.$this_host.'/'); $client_uri->setDomain($this_host->getDomain()); } $subscriptions = $this->pageObjects; if ($user) { $subscriptions[] = $user->getPHID(); } if ($request->isHTTPS()) { $client_uri->setProtocol('wss'); } else { $client_uri->setProtocol('ws'); } Javelin::initBehavior( 'aphlict-listen', array( 'websocketURI' => (string)$client_uri, 'pageObjects' => array_fill_keys($this->pageObjects, true), 'subscriptions' => $subscriptions, )); } } $tail[] = $response->renderHTMLFooter(); return $tail; } protected function getBodyClasses() { $classes = array(); if (!$this->getShowChrome()) { $classes[] = 'phabricator-chromeless-page'; } $agent = AphrontRequest::getHTTPHeader('User-Agent'); // Try to guess the device resolution based on UA strings to avoid a flash // of incorrectly-styled content. $device_guess = 'device-desktop'; if (preg_match('@iPhone|iPod|(Android.*Chrome/[.0-9]* Mobile)@', $agent)) { $device_guess = 'device-phone device'; } else if (preg_match('@iPad|(Android.*Chrome/)@', $agent)) { $device_guess = 'device-tablet device'; } $classes[] = $device_guess; if (preg_match('@Windows@', $agent)) { $classes[] = 'platform-windows'; } else if (preg_match('@Macintosh@', $agent)) { $classes[] = 'platform-mac'; } else if (preg_match('@X11@', $agent)) { $classes[] = 'platform-linux'; } if ($this->getRequest()->getStr('__print__')) { $classes[] = 'printable'; } if ($this->getRequest()->getStr('__aural__')) { $classes[] = 'audible'; } return implode(' ', $classes); } private function getConsole() { if ($this->disableConsole) { return null; } return $this->getRequest()->getApplicationConfiguration()->getConsole(); } private function renderFooter() { if (!$this->getShowChrome()) { return null; } if (!$this->getShowFooter()) { return null; } $items = PhabricatorEnv::getEnvConfig('ui.footer-items'); if (!$items) { return null; } $foot = array(); foreach ($items as $item) { $name = idx($item, 'name', pht('Unnamed Footer Item')); $href = idx($item, 'href'); if (!PhabricatorEnv::isValidURIForLink($href)) { $href = null; } if ($href !== null) { $tag = 'a'; } else { $tag = 'span'; } $foot[] = phutil_tag( $tag, array( 'href' => $href, ), $name); } $foot = phutil_implode_html(" \xC2\xB7 ", $foot); return phutil_tag( 'div', array( 'class' => 'phabricator-standard-page-footer grouped', ), $foot); } public function renderForQuicksand() { parent::willRenderPage(); $response = $this->renderPageBodyContent(); $response = $this->willSendResponse($response); return array( 'content' => hsprintf('%s', $response), ); } private function getQuicksandURIPatternBlacklist() { $applications = PhabricatorApplication::getAllApplications(); $blacklist = array(); foreach ($applications as $application) { $blacklist[] = $application->getQuicksandURIPatternBlacklist(); } return array_mergev($blacklist); } private function getUserPreference($key, $default = null) { $request = $this->getRequest(); if (!$request) { return $default; } $user = $request->getUser(); if (!$user) { return $default; } return $user->loadPreferences()->getPreference($key, $default); } }