public static function getJumpResponse(PhabricatorUser $viewer, $jump) { $jump = trim($jump); $help_href = PhabricatorEnv::getDocLink('Jump Nav User Guide'); $patterns = array('/^help/i' => 'uri:' . $help_href, '/^a$/i' => 'uri:/audit/', '/^f$/i' => 'uri:/feed/', '/^d$/i' => 'uri:/differential/', '/^r$/i' => 'uri:/diffusion/', '/^t$/i' => 'uri:/maniphest/', '/^p$/i' => 'uri:/project/', '/^u$/i' => 'uri:/people/', '/^p\\s+(.+)$/i' => 'project', '/^u\\s+(\\S+)$/i' => 'user', '/^task:\\s*(.+)/i' => 'create-task', '/^(?:s|symbol)\\s+(\\S+)/i' => 'find-symbol', '/^r\\s+(.+)$/i' => 'find-repository'); foreach ($patterns as $pattern => $effect) { $matches = null; if (preg_match($pattern, $jump, $matches)) { if (!strncmp($effect, 'uri:', 4)) { return id(new AphrontRedirectResponse())->setURI(substr($effect, 4)); } else { switch ($effect) { case 'user': return id(new AphrontRedirectResponse())->setURI('/p/' . $matches[1] . '/'); case 'project': $project = self::findCloselyNamedProject($matches[1]); if ($project) { return id(new AphrontRedirectResponse())->setURI('/project/view/' . $project->getID() . '/'); } else { $jump = $matches[1]; } break; case 'find-symbol': $context = ''; $symbol = $matches[1]; $parts = array(); if (preg_match('/(.*)(?:\\.|::|->)(.*)/', $symbol, $parts)) { $context = '&context=' . phutil_escape_uri($parts[1]); $symbol = $parts[2]; } return id(new AphrontRedirectResponse())->setURI("/diffusion/symbol/{$symbol}/?jump=true{$context}"); case 'find-repository': $name = $matches[1]; $repositories = id(new PhabricatorRepositoryQuery())->setViewer($viewer)->withNameContains($name)->execute(); if (count($repositories) == 1) { // Just one match, jump to repository. $uri = '/diffusion/' . head($repositories)->getCallsign() . '/'; } else { // More than one match, jump to search. $uri = urisprintf('/diffusion/?order=name&name=%s', $name); } return id(new AphrontRedirectResponse())->setURI($uri); case 'create-task': return id(new AphrontRedirectResponse())->setURI('/maniphest/task/create/?title=' . phutil_escape_uri($matches[1])); default: throw new Exception("Unknown jump effect '{$effect}'!"); } } } } // If none of the patterns matched, look for an object by name. $objects = id(new PhabricatorObjectQuery())->setViewer($viewer)->withNames(array($jump))->execute(); if (count($objects) == 1) { $handle = id(new PhabricatorHandleQuery())->setViewer($viewer)->withPHIDs(mpull($objects, 'getPHID'))->executeOne(); return id(new AphrontRedirectResponse())->setURI($handle->getURI()); } return null; }
public static function renderUploadLimit() { $limit = PhabricatorEnv::getEnvConfig('storage.upload-size-limit'); $limit = phabricator_parse_bytes($limit); if ($limit) { $formatted = phabricator_format_bytes($limit); return 'Maximum file size: ' . phutil_escape_html($formatted); } $doc_href = PhabricatorEnv::getDocLink('article/Configuring_File_Upload_Limits.html'); $doc_link = phutil_render_tag('a', array('href' => $doc_href, 'target' => '_blank'), 'Configuring File Upload Limits'); return 'Upload limit is not configured, see ' . $doc_link . '.'; }
private function renderUploadLimit() { $limit = PhabricatorEnv::getEnvConfig('storage.upload-size-limit'); $limit = phutil_parse_bytes($limit); if ($limit) { $formatted = phutil_format_bytes($limit); return 'Maximum file size: ' . $formatted; } $doc_href = PhabricatorEnv::getDocLink('Configuring File Upload Limits'); $doc_link = phutil_tag('a', array('href' => $doc_href, 'target' => '_blank'), 'Configuring File Upload Limits'); return hsprintf('Upload limit is not configured, see %s.', $doc_link); }
public static function jumpPostResponse($jump) { $jump = trim($jump); $help_href = PhabricatorEnv::getDocLink('article/Jump_Nav_User_Guide.html'); $patterns = array('/^help/i' => 'uri:' . $help_href, '/^a$/i' => 'uri:/audit/', '/^f$/i' => 'uri:/feed/', '/^d$/i' => 'uri:/differential/', '/^r$/i' => 'uri:/diffusion/', '/^t$/i' => 'uri:/maniphest/', '/^p$/i' => 'uri:/project/', '/^u$/i' => 'uri:/people/', '/^r([A-Z]+)$/' => 'repository', '/^r([A-Z]+)(\\S+)$/' => 'commit', '/^d(\\d+)$/i' => 'revision', '/^t(\\d+)$/i' => 'task', '/^p\\s+(.+)$/i' => 'project', '/^u\\s+(\\S+)$/i' => 'user', '/^task:\\s*(.+)/i' => 'create-task', '/^(?:s|symbol)\\s+(\\S+)/i' => 'find-symbol'); foreach ($patterns as $pattern => $effect) { $matches = null; if (preg_match($pattern, $jump, $matches)) { if (!strncmp($effect, 'uri:', 4)) { return id(new AphrontRedirectResponse())->setURI(substr($effect, 4)); } else { switch ($effect) { case 'repository': return id(new AphrontRedirectResponse())->setURI('/diffusion/' . $matches[1] . '/'); case 'commit': return id(new AphrontRedirectResponse())->setURI('/' . $matches[0]); case 'revision': return id(new AphrontRedirectResponse())->setURI('/D' . $matches[1]); case 'task': return id(new AphrontRedirectResponse())->setURI('/T' . $matches[1]); case 'user': return id(new AphrontRedirectResponse())->setURI('/p/' . $matches[1] . '/'); case 'project': $project = self::findCloselyNamedProject($matches[1]); if ($project) { return id(new AphrontRedirectResponse())->setURI('/project/view/' . $project->getID() . '/'); } else { $jump = $matches[1]; } break; case 'find-symbol': $context = ''; $symbol = $matches[1]; $parts = array(); if (preg_match('/(.*)(?:\\.|::|->)(.*)/', $symbol, $parts)) { $context = '&context=' . phutil_escape_uri($parts[1]); $symbol = $parts[2]; } return id(new AphrontRedirectResponse())->setURI("/diffusion/symbol/{$symbol}/?jump=true{$context}"); case 'create-task': return id(new AphrontRedirectResponse())->setURI('/maniphest/task/create/?title=' . phutil_escape_uri($matches[1])); default: throw new Exception("Unknown jump effect '{$effect}'!"); } } } } return null; }
protected function executeChecks() { if (empty($_SERVER['REMOTE_ADDR'])) { $doc_href = PhabricatorEnv::getDocLink('Configuring a Preamble Script'); $summary = pht('You likely need to fix your preamble script so ' . 'REMOTE_ADDR is no longer empty.'); $message = pht('No REMOTE_ADDR is available, so Phabricator cannot determine the ' . 'origin address for requests. This will prevent Phabricator from ' . 'performing important security checks. This most often means you ' . 'have a mistake in your preamble script. Consult the documentation ' . '(%s) and double-check that the script is written correctly.', phutil_tag('a', array('href' => $doc_href, 'target' => '_blank'), pht('Configuring a Preamble Script'))); $this->newIssue('php.remote_addr')->setName(pht('No REMOTE_ADDR available'))->setSummary($summary)->setMessage($message); } $raw_post_data = (int) ini_get('always_populate_raw_post_data'); if ($raw_post_data != -1) { $summary = pht('PHP setting "%s" should be set to "-1" to avoid deprecation ' . 'warnings.', 'always_populate_raw_post_data'); $message = pht('The "%s" key is set to some value other than "-1" in your PHP ' . 'configuration. This can cause PHP to raise deprecation warnings ' . 'during process startup. Set this option to "-1" to prevent these ' . 'warnings from appearing.', 'always_populate_raw_post_data'); $this->newIssue('php.always_populate_raw_post_data')->setName(pht('Disable PHP %s', 'always_populate_raw_post_data'))->setSummary($summary)->setMessage($message)->addPHPConfig('always_populate_raw_post_data'); } }
/** * @phutil-external-symbol class PhabricatorStartup */ protected function executeChecks() { $engines = PhabricatorFileStorageEngine::loadWritableChunkEngines(); $chunk_engine_active = (bool) $engines; $this->checkS3(); if (!$chunk_engine_active) { $doc_href = PhabricatorEnv::getDocLink('Configuring File Storage'); $message = pht('Large file storage has not been configured, which will limit ' . 'the maximum size of file uploads. See %s for ' . 'instructions on configuring uploads and storage.', phutil_tag('a', array('href' => $doc_href, 'target' => '_blank'), pht('Configuring File Storage'))); $this->newIssue('large-files')->setShortName(pht('Large Files'))->setName(pht('Large File Storage Not Configured'))->setMessage($message); } $post_max_size = ini_get('post_max_size'); if ($post_max_size && (int) $post_max_size > 0) { $post_max_bytes = phutil_parse_bytes($post_max_size); $post_max_need = 32 * 1024 * 1024; if ($post_max_need > $post_max_bytes) { $summary = pht('Set %s in your PHP configuration to at least 32MB ' . 'to support large file uploads.', phutil_tag('tt', array(), 'post_max_size')); $message = pht('Adjust %s in your PHP configuration to at least 32MB. When ' . 'set to smaller value, large file uploads may not work properly.', phutil_tag('tt', array(), 'post_max_size')); $this->newIssue('php.post_max_size')->setName(pht('PHP post_max_size Not Configured'))->setSummary($summary)->setMessage($message)->setGroup(self::GROUP_PHP)->addPHPConfig('post_max_size'); } } // This is somewhat arbitrary, but make sure we have enough headroom to // upload a default file at the chunk threshold (8MB), which may be // base64 encoded, then JSON encoded in the request, and may need to be // held in memory in the raw and as a query string. $need_bytes = 64 * 1024 * 1024; $memory_limit = PhabricatorStartup::getOldMemoryLimit(); if ($memory_limit && (int) $memory_limit > 0) { $memory_limit_bytes = phutil_parse_bytes($memory_limit); $memory_usage_bytes = memory_get_usage(); $available_bytes = $memory_limit_bytes - $memory_usage_bytes; if ($need_bytes > $available_bytes) { $summary = pht('Your PHP memory limit is configured in a way that may prevent ' . 'you from uploading large files or handling large requests.'); $message = pht('When you upload a file via drag-and-drop or the API, chunks must ' . 'be buffered into memory before being written to permanent ' . 'storage. Phabricator needs memory available to store these ' . 'chunks while they are uploaded, but PHP is currently configured ' . 'to severly limit the available memory.' . "\n\n" . 'PHP processes currently have very little free memory available ' . '(%s). To work well, processes should have at least %s.' . "\n\n" . '(Note that the application itself must also fit in available ' . 'memory, so not all of the memory under the memory limit is ' . 'available for running workloads.)' . "\n\n" . "The easiest way to resolve this issue is to set %s to %s in your " . "PHP configuration, to disable the memory limit. There is " . "usually little or no value to using this option to limit " . "Phabricator process memory." . "\n\n" . "You can also increase the limit or ignore this issue and accept " . "that you may encounter problems uploading large files and " . "processing large requests.", phutil_format_bytes($available_bytes), phutil_format_bytes($need_bytes), phutil_tag('tt', array(), 'memory_limit'), phutil_tag('tt', array(), '-1')); $this->newIssue('php.memory_limit.upload')->setName(pht('Memory Limit Restricts File Uploads'))->setSummary($summary)->setMessage($message)->setGroup(self::GROUP_PHP)->addPHPConfig('memory_limit')->addPHPConfigOriginalValue('memory_limit', $memory_limit); } } $local_path = PhabricatorEnv::getEnvConfig('storage.local-disk.path'); if (!$local_path) { return; } if (!Filesystem::pathExists($local_path) || !is_readable($local_path) || !is_writable($local_path)) { $message = pht('Configured location for storing uploaded files on disk ("%s") does ' . 'not exist, or is not readable or writable. Verify the directory ' . 'exists and is readable and writable by the webserver.', $local_path); $this->newIssue('config.storage.local-disk.path')->setShortName(pht('Local Disk Storage'))->setName(pht('Local Disk Storage Not Readable/Writable'))->setMessage($message)->addPhabricatorConfig('storage.local-disk.path'); } }
protected function executeChecks() { // This checks for a version of bash with the "Shellshock" vulnerability. // For details, see T6185. $payload = array('SHELLSHOCK_PAYLOAD' => '() { :;} ; echo VULNERABLE'); list($err, $stdout) = id(new ExecFuture('echo shellshock-test'))->setEnv($payload, $wipe_process_env = true)->resolve(); if (!$err && preg_match('/VULNERABLE/', $stdout)) { $summary = pht('This system has an unpatched version of Bash with a severe, widely ' . 'disclosed vulnerability.'); $message = pht('The version of %s on this system is out of date and contains a ' . 'major, widely disclosed vulnerability (the "Shellshock" ' . 'vulnerability).' . "\n\n" . 'Upgrade %s to a patched version.' . "\n\n" . 'To learn more about how this issue affects Phabricator, see %s.', phutil_tag('tt', array(), 'bash'), phutil_tag('tt', array(), 'bash'), phutil_tag('a', array('href' => 'https://secure.phabricator.com/T6185', 'target' => '_blank'), pht('T6185 "Shellshock" Bash Vulnerability'))); $this->newIssue('security.shellshock')->setName(pht('Severe Security Vulnerability: Unpatched Bash'))->setSummary($summary)->setMessage($message); } $file_key = 'security.alternate-file-domain'; $file_domain = PhabricatorEnv::getEnvConfig($file_key); if (!$file_domain) { $doc_href = PhabricatorEnv::getDocLink('Configuring a File Domain'); $this->newIssue('security.' . $file_key)->setName(pht('Alternate File Domain Not Configured'))->setSummary(pht('Increase security (and improve performance) by configuring ' . 'a CDN or alternate file domain.'))->setMessage(pht('Phabricator is currently configured to serve user uploads ' . 'directly from the same domain as other content. This is a ' . 'security risk.' . "\n\n" . 'Configure a CDN (or alternate file domain) to eliminate this ' . 'risk. Using a CDN will also improve performance. See the ' . 'guide below for instructions.'))->addPhabricatorConfig($file_key)->addLink($doc_href, pht('Configuration Guide: Configuring a File Domain')); } }
protected function executeChecks() { $task_daemon = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_RUNNING)->withDaemonClasses(array('PhabricatorTaskmasterDaemon'))->setLimit(1)->execute(); if (!$task_daemon) { $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd'); $summary = pht('You must start the Phabricator daemons to send email, rebuild ' . 'search indexes, and do other background processing.'); $message = pht('The Phabricator daemons are not running, so Phabricator will not ' . 'be able to perform background processing (including sending email, ' . 'rebuilding search indexes, importing commits, cleaning up old data, ' . 'and running builds).' . "\n\n" . 'Use %s to start daemons. See %s for more information.', phutil_tag('tt', array(), 'bin/phd start'), phutil_tag('a', array('href' => $doc_href, 'target' => '_blank'), pht('Managing Daemons with phd'))); $this->newIssue('daemons.not-running')->setShortName(pht('Daemons Not Running'))->setName(pht('Phabricator Daemons Are Not Running'))->setSummary($summary)->setMessage($message)->addCommand('phabricator/ $ ./bin/phd start'); } $phd_user = PhabricatorEnv::getEnvConfig('phd.user'); $environment_hash = PhabricatorEnv::calculateEnvironmentHash(); $all_daemons = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)->execute(); foreach ($all_daemons as $daemon) { if ($phd_user) { if ($daemon->getRunningAsUser() != $phd_user) { $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd'); $summary = pht('At least one daemon is currently running as a different ' . 'user than configured in the Phabricator %s setting', 'phd.user'); $message = pht('A daemon is running as user %s while the Phabricator config ' . 'specifies %s to be %s.' . "\n\n" . 'Either adjust %s to match %s or start ' . 'the daemons as the correct user. ' . "\n\n" . '%s Daemons will try to use %s to start as the configured user. ' . 'Make sure that the user who starts %s has the correct ' . 'sudo permissions to start %s daemons as %s', 'phd.user', 'phd.user', 'phd', 'sudo', 'phd', 'phd', phutil_tag('tt', array(), $daemon->getRunningAsUser()), phutil_tag('tt', array(), $phd_user), phutil_tag('tt', array(), $daemon->getRunningAsUser()), phutil_tag('tt', array(), $phd_user)); $this->newIssue('daemons.run-as-different-user')->setName(pht('Daemons are running as the wrong user'))->setSummary($summary)->setMessage($message)->addCommand('phabricator/ $ ./bin/phd restart'); } } if ($daemon->getEnvHash() != $environment_hash) { $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd'); $summary = pht('At least one daemon is currently running with different ' . 'configuration than the Phabricator web application.'); $list_section = null; $env_info = $daemon->getEnvInfo(); if ($env_info) { $issues = PhabricatorEnv::compareEnvironmentInfo(PhabricatorEnv::calculateEnvironmentInfo(), $env_info); if ($issues) { foreach ($issues as $key => $issue) { $issues[$key] = phutil_tag('li', array(), $issue); } $list_section = array(pht('The configurations differ in the following %s way(s):', phutil_count($issues)), phutil_tag('ul', array(), $issues)); } } $message = pht('At least one daemon is currently running with a different ' . 'configuration (config checksum %s) than the web application ' . '(config checksum %s).' . "\n\n%s" . 'This usually means that you have just made a configuration change ' . 'from the web UI, but have not yet restarted the daemons. You ' . 'need to restart the daemons after making configuration changes ' . 'so they will pick up the new values: until you do, they will ' . 'continue operating with the old settings.' . "\n\n" . '(If you plan to make more changes, you can restart the daemons ' . 'once after you finish making all of your changes.)' . "\n\n" . 'Use %s to restart daemons. You can find a list of running daemons ' . 'in the %s, which will also help you identify which daemon (or ' . 'daemons) have divergent configuration. For more information about ' . 'managing the daemons, see %s in the documentation.' . "\n\n" . 'This can also happen if you use the %s environmental variable to ' . 'choose a configuration file, but the daemons run with a different ' . 'value than the web application. If restarting the daemons does ' . 'not resolve this issue and you use %s to select configuration, ' . 'check that it is set consistently.' . "\n\n" . 'A third possible cause is that you run several machines, and ' . 'the %s configuration file differs between them. This file is ' . 'updated when you edit configuration from the CLI with %s. If ' . 'restarting the daemons does not resolve this issue and you ' . 'run multiple machines, check that all machines have identical ' . '%s configuration files.' . "\n\n" . 'This issue is not severe, but usually indicates that something ' . 'is not configured the way you expect, and may cause the daemons ' . 'to exhibit different behavior than the web application does.', phutil_tag('tt', array(), substr($daemon->getEnvHash(), 0, 12)), phutil_tag('tt', array(), substr($environment_hash, 0, 12)), $list_section, phutil_tag('tt', array(), 'bin/phd restart'), phutil_tag('a', array('href' => '/daemon/', 'target' => '_blank'), pht('Daemon Console')), phutil_tag('a', array('href' => $doc_href, 'target' => '_blank'), pht('Managing Daemons with phd')), phutil_tag('tt', array(), 'PHABRICATOR_ENV'), phutil_tag('tt', array(), 'PHABRICATOR_ENV'), phutil_tag('tt', array(), 'phabricator/conf/local/local.json'), phutil_tag('tt', array(), 'bin/config'), phutil_tag('tt', array(), 'phabricator/conf/local/local.json')); $this->newIssue('daemons.need-restarting')->setName(pht('Daemons and Web Have Different Config'))->setSummary($summary)->setMessage($message)->addCommand('phabricator/ $ ./bin/phd restart'); break; } } }
protected function executeChecks() { $task_daemon = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_RUNNING)->withDaemonClasses(array('PhabricatorTaskmasterDaemon'))->setLimit(1)->execute(); if (!$task_daemon) { $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd'); $summary = pht('You must start the Phabricator daemons to send email, rebuild ' . 'search indexes, and do other background processing.'); $message = pht('The Phabricator daemons are not running, so Phabricator will not ' . 'be able to perform background processing (including sending email, ' . 'rebuilding search indexes, importing commits, cleaning up old data, ' . 'and running builds).' . "\n\n" . 'Use %s to start daemons. See %s for more information.', phutil_tag('tt', array(), 'bin/phd start'), phutil_tag('a', array('href' => $doc_href, 'target' => '_blank'), pht('Managing Daemons with phd'))); $this->newIssue('daemons.not-running')->setShortName(pht('Daemons Not Running'))->setName(pht('Phabricator Daemons Are Not Running'))->setSummary($summary)->setMessage($message)->addCommand('phabricator/ $ ./bin/phd start'); } $phd_user = PhabricatorEnv::getEnvConfig('phd.user'); $all_daemons = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)->execute(); foreach ($all_daemons as $daemon) { if ($phd_user) { if ($daemon->getRunningAsUser() != $phd_user) { $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd'); $summary = pht('At least one daemon is currently running as a different ' . 'user than configured in the Phabricator %s setting', 'phd.user'); $message = pht('A daemon is running as user %s while the Phabricator config ' . 'specifies %s to be %s.' . "\n\n" . 'Either adjust %s to match %s or start ' . 'the daemons as the correct user. ' . "\n\n" . '%s Daemons will try to use %s to start as the configured user. ' . 'Make sure that the user who starts %s has the correct ' . 'sudo permissions to start %s daemons as %s', 'phd.user', 'phd.user', 'phd', 'sudo', 'phd', 'phd', phutil_tag('tt', array(), $daemon->getRunningAsUser()), phutil_tag('tt', array(), $phd_user), phutil_tag('tt', array(), $daemon->getRunningAsUser()), phutil_tag('tt', array(), $phd_user)); $this->newIssue('daemons.run-as-different-user')->setName(pht('Daemons are running as the wrong user'))->setSummary($summary)->setMessage($message)->addCommand('phabricator/ $ ./bin/phd restart'); } } } }
protected function executeChecks() { $task_daemon = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_RUNNING)->withDaemonClasses(array('PhabricatorTaskmasterDaemon'))->setLimit(1)->execute(); if (!$task_daemon) { $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd'); $summary = pht('You must start the Phabricator daemons to send email, rebuild ' . 'search indexes, and do other background processing.'); $message = pht('The Phabricator daemons are not running, so Phabricator will not ' . 'be able to perform background processing (including sending email, ' . 'rebuilding search indexes, importing commits, cleaning up old data, ' . 'and running builds).' . "\n\n" . 'Use %s to start daemons. See %s for more information.', phutil_tag('tt', array(), 'bin/phd start'), phutil_tag('a', array('href' => $doc_href, 'target' => '_blank'), pht('Managing Daemons with phd'))); $this->newIssue('daemons.not-running')->setShortName(pht('Daemons Not Running'))->setName(pht('Phabricator Daemons Are Not Running'))->setSummary($summary)->setMessage($message)->addCommand('phabricator/ $ ./bin/phd start'); } $expect_user = PhabricatorEnv::getEnvConfig('phd.user'); if (strlen($expect_user)) { $all_daemons = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)->execute(); foreach ($all_daemons as $daemon) { $actual_user = $daemon->getRunningAsUser(); if ($actual_user == $expect_user) { continue; } $summary = pht('At least one daemon is currently running as the wrong user.'); $message = pht('A daemon is running as user %s, but daemons should be ' . 'running as %s.' . "\n\n" . 'Either adjust the configuration setting %s or restart the ' . 'daemons. Daemons should attempt to run as the proper user when ' . 'restarted.', phutil_tag('tt', array(), $actual_user), phutil_tag('tt', array(), $expect_user), phutil_tag('tt', array(), 'phd.user')); $this->newIssue('daemons.run-as-different-user')->setName(pht('Daemon Running as Wrong User'))->setSummary($summary)->setMessage($message)->addPhabricatorConfig('phd.user')->addCommand('phabricator/ $ ./bin/phd restart'); break; } } }
private function buildRepositoryStatus(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $is_cluster = $repository->getAlmanacServicePHID(); $view = new PHUIStatusListView(); $messages = id(new PhabricatorRepositoryStatusMessage())->loadAllWhere('repositoryID = %d', $repository->getID()); $messages = mpull($messages, null, 'getStatusType'); if ($repository->isTracked()) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Repository Active'))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'bluegrey')->setTarget(pht('Repository Inactive'))->setNote(pht('Activate this repository to begin or resume import.'))); return $view; } $binaries = array(); $svnlook_check = false; switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binaries[] = 'git'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binaries[] = 'svn'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binaries[] = 'hg'; break; } if ($repository->isHosted()) { if ($repository->getServeOverHTTP() != PhabricatorRepository::SERVE_OFF) { switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binaries[] = 'git-http-backend'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binaries[] = 'svnserve'; $binaries[] = 'svnadmin'; $binaries[] = 'svnlook'; $svnlook_check = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binaries[] = 'hg'; break; } } if ($repository->getServeOverSSH() != PhabricatorRepository::SERVE_OFF) { switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binaries[] = 'git-receive-pack'; $binaries[] = 'git-upload-pack'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binaries[] = 'svnserve'; $binaries[] = 'svnadmin'; $binaries[] = 'svnlook'; $svnlook_check = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binaries[] = 'hg'; break; } } } $binaries = array_unique($binaries); if (!$is_cluster) { // We're only checking for binaries if we aren't running with a cluster // configuration. In theory, we could check for binaries on the // repository host machine, but we'd need to make this more complicated // to do that. foreach ($binaries as $binary) { $where = Filesystem::resolveBinary($binary); if (!$where) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Missing Binary %s', phutil_tag('tt', array(), $binary)))->setNote(pht("Unable to find this binary in the webserver's PATH. You may " . "need to configure %s.", $this->getEnvConfigLink()))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Found Binary %s', phutil_tag('tt', array(), $binary)))->setNote(phutil_tag('tt', array(), $where))); } } // This gets checked generically above. However, for svn commit hooks, we // need this to be in environment.append-paths because subversion strips // PATH. if ($svnlook_check) { $where = Filesystem::resolveBinary('svnlook'); if ($where) { $path = substr($where, 0, strlen($where) - strlen('svnlook')); $dirs = PhabricatorEnv::getEnvConfig('environment.append-paths'); $in_path = false; foreach ($dirs as $dir) { if (Filesystem::isDescendant($path, $dir)) { $in_path = true; break; } } if (!$in_path) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Missing Binary %s', phutil_tag('tt', array(), $binary)))->setNote(pht('Unable to find this binary in `%s`. ' . 'You need to configure %s and include %s.', 'environment.append-paths', $this->getEnvConfigLink(), $path))); } } } } $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd'); $daemon_instructions = pht('Use %s to start daemons. See %s.', phutil_tag('tt', array(), 'bin/phd start'), phutil_tag('a', array('href' => $doc_href), pht('Managing Daemons with phd'))); $pull_daemon = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)->withDaemonClasses(array('PhabricatorRepositoryPullLocalDaemon'))->setLimit(1)->execute(); if ($pull_daemon) { // TODO: In a cluster environment, we need a daemon on this repository's // host, specifically, and we aren't checking for that right now. This // is a reasonable proxy for things being more-or-less correctly set up, // though. $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Pull Daemon Running'))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Pull Daemon Not Running'))->setNote($daemon_instructions)); } $task_daemon = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)->withDaemonClasses(array('PhabricatorTaskmasterDaemon'))->setLimit(1)->execute(); if ($task_daemon) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Task Daemon Running'))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Task Daemon Not Running'))->setNote($daemon_instructions)); } if ($is_cluster) { // Just omit this status check for now in cluster environments. We // could make a service call and pull it from the repository host // eventually. } else { if ($repository->usesLocalWorkingCopy()) { $local_parent = dirname($repository->getLocalPath()); if (Filesystem::pathExists($local_parent)) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Storage Directory OK'))->setNote(phutil_tag('tt', array(), $local_parent))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('No Storage Directory'))->setNote(pht('Storage directory %s does not exist, or is not readable by ' . 'the webserver. Create this directory or make it readable.', phutil_tag('tt', array(), $local_parent)))); return $view; } $local_path = $repository->getLocalPath(); $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_INIT); if ($message) { switch ($message->getStatusCode()) { case PhabricatorRepositoryStatusMessage::CODE_ERROR: $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Initialization Error'))->setNote($message->getParameter('message'))); return $view; case PhabricatorRepositoryStatusMessage::CODE_OKAY: if (Filesystem::pathExists($local_path)) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Working Copy OK'))->setNote(phutil_tag('tt', array(), $local_path))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Working Copy Error'))->setNote(pht('Working copy %s has been deleted, or is not ' . 'readable by the webserver. Make this directory ' . 'readable. If it has been deleted, the daemons should ' . 'restore it automatically.', phutil_tag('tt', array(), $local_path)))); return $view; } break; case PhabricatorRepositoryStatusMessage::CODE_WORKING: $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green')->setTarget(pht('Initializing Working Copy'))->setNote(pht('Daemons are initializing the working copy.'))); return $view; default: $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Unknown Init Status'))->setNote($message->getStatusCode())); return $view; } } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange')->setTarget(pht('No Working Copy Yet'))->setNote(pht('Waiting for daemons to build a working copy.'))); return $view; } } } $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_FETCH); if ($message) { switch ($message->getStatusCode()) { case PhabricatorRepositoryStatusMessage::CODE_ERROR: $message = $message->getParameter('message'); $suggestion = null; if (preg_match('/Permission denied \\(publickey\\)./', $message)) { $suggestion = pht('Public Key Error: This error usually indicates that the ' . 'keypair you have configured does not have permission to ' . 'access the repository.'); } $message = phutil_escape_html_newlines($message); if ($suggestion !== null) { $message = array(phutil_tag('strong', array(), $suggestion), phutil_tag('br'), phutil_tag('br'), phutil_tag('em', array(), pht('Raw Error')), phutil_tag('br'), $message); } $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Update Error'))->setNote($message)); return $view; case PhabricatorRepositoryStatusMessage::CODE_OKAY: $ago = PhabricatorTime::getNow() - $message->getEpoch(); $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Updates OK'))->setNote(pht('Last updated %s (%s ago).', phabricator_datetime($message->getEpoch(), $viewer), phutil_format_relative_time_detailed($ago)))); break; } } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange')->setTarget(pht('Waiting For Update'))->setNote(pht('Waiting for daemons to read updates.'))); } if ($repository->isImporting()) { $progress = queryfx_all($repository->establishConnection('r'), 'SELECT importStatus, count(*) N FROM %T WHERE repositoryID = %d GROUP BY importStatus', id(new PhabricatorRepositoryCommit())->getTableName(), $repository->getID()); $done = 0; $total = 0; foreach ($progress as $row) { $total += $row['N'] * 4; $status = $row['importStatus']; if ($status & PhabricatorRepositoryCommit::IMPORTED_MESSAGE) { $done += $row['N']; } if ($status & PhabricatorRepositoryCommit::IMPORTED_CHANGE) { $done += $row['N']; } if ($status & PhabricatorRepositoryCommit::IMPORTED_OWNERS) { $done += $row['N']; } if ($status & PhabricatorRepositoryCommit::IMPORTED_HERALD) { $done += $row['N']; } } if ($total) { $percentage = 100 * ($done / $total); } else { $percentage = 0; } // Cap this at "99.99%", because it's confusing to users when the actual // fraction is "99.996%" and it rounds up to "100.00%". if ($percentage > 99.98999999999999) { $percentage = 99.98999999999999; } $percentage = sprintf('%.2f%%', $percentage); $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green')->setTarget(pht('Importing'))->setNote(pht('%s Complete', $percentage))); } else { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Fully Imported'))); } if (idx($messages, PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE)) { $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_UP, 'indigo')->setTarget(pht('Prioritized'))->setNote(pht('This repository will be updated soon!'))); } return $view; }
private function buildJumpPanel($query = null) { $request = $this->getRequest(); $user = $request->getUser(); $uniq_id = celerity_generate_unique_node_id(); Javelin::initBehavior('phabricator-autofocus', array('id' => $uniq_id)); require_celerity_resource('phabricator-jump-nav'); $doc_href = PhabricatorEnv::getDocLink('article/Jump_Nav_User_Guide.html'); $doc_link = phutil_render_tag('a', array('href' => $doc_href), 'Jump Nav User Guide'); $jump_input = phutil_render_tag('input', array('type' => 'text', 'class' => 'phabricator-jump-nav', 'name' => 'jump', 'id' => $uniq_id, 'value' => $query)); $jump_caption = phutil_render_tag('p', array('class' => 'phabricator-jump-nav-caption'), 'Enter the name of an object like <tt>D123</tt> to quickly jump to ' . 'it. See ' . $doc_link . ' or type <tt>help</tt>.'); $panel = new AphrontPanelView(); $panel->addClass('aphront-unpadded-panel-view'); $panel->appendChild(phabricator_render_form($user, array('action' => '/jump/', 'method' => 'POST', 'class' => 'phabricator-jump-nav-form'), $jump_input . $jump_caption)); return $panel; }
protected final function getInboundEmailSupportLink() { return PhabricatorEnv::getDocLink('Configuring Inbound Email'); }
public function getOptions() { $caches_href = PhabricatorEnv::getDocLink('Managing Caches'); return array($this->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 %s.', '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++', 'csharp' => 'C#', 'css' => 'CSS', 'd' => 'D', 'diff' => 'Diff', 'django' => 'Django Templating', 'docker' => 'Docker', 'erb' => 'Embedded Ruby/ERB', 'erlang' => 'Erlang', 'go' => 'Golang', 'groovy' => 'Groovy', 'haskell' => 'Haskell', 'html' => 'HTML', 'http' => 'HTTP', 'invisible' => 'Invisible', 'java' => 'Java', 'js' => 'Javascript', 'json' => 'JSON', 'make' => 'Makefile', 'mysql' => 'MySQL', 'nginx' => 'Nginx Configuration', 'objc' => 'Objective-C', 'perl' => 'Perl', 'php' => 'PHP', 'postgresql' => 'PostgreSQL', 'pot' => 'Gettext Catalog', 'puppet' => 'Puppet', 'python' => 'Python', 'rainbow' => 'Rainbow', 'remarkup' => 'Remarkup', 'rst' => 'reStructuredText', 'robotframework' => 'RobotFramework', 'ruby' => 'Ruby', 'sql' => 'SQL', 'tex' => 'LaTeX', 'text' => 'Plain Text', 'twig' => 'Twig', '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 %s as PHP.', '*.xyz'))->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'"))); }