Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/aphront/configuration/AphrontApplicationConfiguration.php b/src/aphront/configuration/AphrontApplicationConfiguration.php
index 9e22ca7f47..39569ae00f 100644
--- a/src/aphront/configuration/AphrontApplicationConfiguration.php
+++ b/src/aphront/configuration/AphrontApplicationConfiguration.php
@@ -1,879 +1,879 @@
<?php
/**
* @task routing URI Routing
* @task response Response Handling
* @task exception Exception Handling
*/
final class AphrontApplicationConfiguration
extends Phobject {
private $request;
private $host;
private $path;
private $console;
public function buildRequest() {
$parser = new PhutilQueryStringParser();
$data = array();
$data += $_POST;
$data += $parser->parseQueryString(idx($_SERVER, 'QUERY_STRING', ''));
$cookie_prefix = PhabricatorEnv::getEnvConfig('phabricator.cookie-prefix');
$request = new AphrontRequest($this->getHost(), $this->getPath());
$request->setRequestData($data);
$request->setApplicationConfiguration($this);
$request->setCookiePrefix($cookie_prefix);
$request->updateEphemeralCookies();
return $request;
}
public function buildRedirectController($uri, $external) {
return array(
new PhabricatorRedirectController(),
array(
'uri' => $uri,
'external' => $external,
),
);
}
public function setRequest(AphrontRequest $request) {
$this->request = $request;
return $this;
}
public function getRequest() {
return $this->request;
}
public function getConsole() {
return $this->console;
}
public function setConsole($console) {
$this->console = $console;
return $this;
}
public function setHost($host) {
$this->host = $host;
return $this;
}
public function getHost() {
return $this->host;
}
public function setPath($path) {
$this->path = $path;
return $this;
}
public function getPath() {
return $this->path;
}
/**
* @phutil-external-symbol class PhabricatorStartup
*/
public static function runHTTPRequest(AphrontHTTPSink $sink) {
- if (isset($_SERVER['HTTP_X_PHABRICATOR_SELFCHECK'])) {
+ if (isset($_SERVER['HTTP_X_SETUP_SELFCHECK'])) {
$response = self::newSelfCheckResponse();
return self::writeResponse($sink, $response);
}
PhabricatorStartup::beginStartupPhase('multimeter');
$multimeter = MultimeterControl::newInstance();
$multimeter->setEventContext('<http-init>');
$multimeter->setEventViewer('<none>');
// Build a no-op write guard for the setup phase. We'll replace this with a
// real write guard later on, but we need to survive setup and build a
// request object first.
$write_guard = new AphrontWriteGuard('id');
PhabricatorStartup::beginStartupPhase('preflight');
$response = PhabricatorSetupCheck::willPreflightRequest();
if ($response) {
return self::writeResponse($sink, $response);
}
PhabricatorStartup::beginStartupPhase('env.init');
self::readHTTPPOSTData();
try {
PhabricatorEnv::initializeWebEnvironment();
$database_exception = null;
} catch (PhabricatorClusterStrandedException $ex) {
$database_exception = $ex;
}
// If we're in developer mode, set a flag so that top-level exception
// handlers can add more information.
if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
$sink->setShowStackTraces(true);
}
if ($database_exception) {
$issue = PhabricatorSetupIssue::newDatabaseConnectionIssue(
$database_exception,
true);
$response = PhabricatorSetupCheck::newIssueResponse($issue);
return self::writeResponse($sink, $response);
}
$multimeter->setSampleRate(
PhabricatorEnv::getEnvConfig('debug.sample-rate'));
$debug_time_limit = PhabricatorEnv::getEnvConfig('debug.time-limit');
if ($debug_time_limit) {
PhabricatorStartup::setDebugTimeLimit($debug_time_limit);
}
// This is the earliest we can get away with this, we need env config first.
PhabricatorStartup::beginStartupPhase('log.access');
PhabricatorAccessLog::init();
$access_log = PhabricatorAccessLog::getLog();
PhabricatorStartup::setAccessLog($access_log);
$address = PhabricatorEnv::getRemoteAddress();
if ($address) {
$address_string = $address->getAddress();
} else {
$address_string = '-';
}
$access_log->setData(
array(
'R' => AphrontRequest::getHTTPHeader('Referer', '-'),
'r' => $address_string,
'M' => idx($_SERVER, 'REQUEST_METHOD', '-'),
));
DarkConsoleXHProfPluginAPI::hookProfiler();
// We just activated the profiler, so we don't need to keep track of
// startup phases anymore: it can take over from here.
PhabricatorStartup::beginStartupPhase('startup.done');
DarkConsoleErrorLogPluginAPI::registerErrorHandler();
$response = PhabricatorSetupCheck::willProcessRequest();
if ($response) {
return self::writeResponse($sink, $response);
}
$host = AphrontRequest::getHTTPHeader('Host');
$path = PhabricatorStartup::getRequestPath();
$application = new self();
$application->setHost($host);
$application->setPath($path);
$request = $application->buildRequest();
// Now that we have a request, convert the write guard into one which
// actually checks CSRF tokens.
$write_guard->dispose();
$write_guard = new AphrontWriteGuard(array($request, 'validateCSRF'));
// Build the server URI implied by the request headers. If an administrator
// has not configured "phabricator.base-uri" yet, we'll use this to generate
// links.
$request_protocol = ($request->isHTTPS() ? 'https' : 'http');
$request_base_uri = "{$request_protocol}://{$host}/";
PhabricatorEnv::setRequestBaseURI($request_base_uri);
$access_log->setData(
array(
'U' => (string)$request->getRequestURI()->getPath(),
));
$processing_exception = null;
try {
$response = $application->processRequest(
$request,
$access_log,
$sink,
$multimeter);
$response_code = $response->getHTTPResponseCode();
} catch (Exception $ex) {
$processing_exception = $ex;
$response_code = 500;
}
$write_guard->dispose();
$access_log->setData(
array(
'c' => $response_code,
'T' => PhabricatorStartup::getMicrosecondsSinceStart(),
));
$multimeter->newEvent(
MultimeterEvent::TYPE_REQUEST_TIME,
$multimeter->getEventContext(),
PhabricatorStartup::getMicrosecondsSinceStart());
$access_log->write();
$multimeter->saveEvents();
DarkConsoleXHProfPluginAPI::saveProfilerSample($access_log);
PhabricatorStartup::disconnectRateLimits(
array(
'viewer' => $request->getUser(),
));
if ($processing_exception) {
throw $processing_exception;
}
}
public function processRequest(
AphrontRequest $request,
PhutilDeferredLog $access_log,
AphrontHTTPSink $sink,
MultimeterControl $multimeter) {
$this->setRequest($request);
list($controller, $uri_data) = $this->buildController();
$controller_class = get_class($controller);
$access_log->setData(
array(
'C' => $controller_class,
));
$multimeter->setEventContext('web.'.$controller_class);
$request->setController($controller);
$request->setURIMap($uri_data);
$controller->setRequest($request);
// If execution throws an exception and then trying to render that
// exception throws another exception, we want to show the original
// exception, as it is likely the root cause of the rendering exception.
$original_exception = null;
try {
$response = $controller->willBeginExecution();
if ($request->getUser() && $request->getUser()->getPHID()) {
$access_log->setData(
array(
'u' => $request->getUser()->getUserName(),
'P' => $request->getUser()->getPHID(),
));
$multimeter->setEventViewer('user.'.$request->getUser()->getPHID());
}
if (!$response) {
$controller->willProcessRequest($uri_data);
$response = $controller->handleRequest($request);
$this->validateControllerResponse($controller, $response);
}
} catch (Exception $ex) {
$original_exception = $ex;
} catch (Throwable $ex) {
$original_exception = $ex;
}
$response_exception = null;
try {
if ($original_exception) {
$response = $this->handleThrowable($original_exception);
}
$response = $this->produceResponse($request, $response);
$response = $controller->willSendResponse($response);
$response->setRequest($request);
self::writeResponse($sink, $response);
} catch (Exception $ex) {
$response_exception = $ex;
} catch (Throwable $ex) {
$response_exception = $ex;
}
if ($response_exception) {
// If we encountered an exception while building a normal response, then
// encountered another exception while building a response for the first
// exception, throw an aggregate exception that will be unpacked by the
// higher-level handler. This is above our pay grade.
if ($original_exception) {
throw new PhutilAggregateException(
pht(
'Encountered a processing exception, then another exception when '.
'trying to build a response for the first exception.'),
array(
$response_exception,
$original_exception,
));
}
// If we built a response successfully and then ran into an exception
// trying to render it, try to handle and present that exception to the
// user using the standard handler.
// The problem here might be in rendering (more common) or in the actual
// response mechanism (less common). If it's in rendering, we can likely
// still render a nice exception page: the majority of rendering issues
// are in main page content, not content shared with the exception page.
$handling_exception = null;
try {
$response = $this->handleThrowable($response_exception);
$response = $this->produceResponse($request, $response);
$response = $controller->willSendResponse($response);
$response->setRequest($request);
self::writeResponse($sink, $response);
} catch (Exception $ex) {
$handling_exception = $ex;
} catch (Throwable $ex) {
$handling_exception = $ex;
}
// If we didn't have any luck with that, raise the original response
// exception. As above, this is the root cause exception and more likely
// to be useful. This will go to the fallback error handler at top
// level.
if ($handling_exception) {
throw $response_exception;
}
}
return $response;
}
private static function writeResponse(
AphrontHTTPSink $sink,
AphrontResponse $response) {
$unexpected_output = PhabricatorStartup::endOutputCapture();
if ($unexpected_output) {
$unexpected_output = pht(
"Unexpected output:\n\n%s",
$unexpected_output);
phlog($unexpected_output);
if ($response instanceof AphrontWebpageResponse) {
$response->setUnexpectedOutput($unexpected_output);
}
}
$sink->writeResponse($response);
}
/* -( URI Routing )-------------------------------------------------------- */
/**
* Build a controller to respond to the request.
*
* @return pair<AphrontController,dict> Controller and dictionary of request
* parameters.
* @task routing
*/
private function buildController() {
$request = $this->getRequest();
// If we're configured to operate in cluster mode, reject requests which
// were not received on a cluster interface.
//
// For example, a host may have an internal address like "170.0.0.1", and
// also have a public address like "51.23.95.16". Assuming the cluster
// is configured on a range like "170.0.0.0/16", we want to reject the
// requests received on the public interface.
//
// Ideally, nodes in a cluster should only be listening on internal
// interfaces, but they may be configured in such a way that they also
// listen on external interfaces, since this is easy to forget about or
// get wrong. As a broad security measure, reject requests received on any
// interfaces which aren't on the whitelist.
$cluster_addresses = PhabricatorEnv::getEnvConfig('cluster.addresses');
if ($cluster_addresses) {
$server_addr = idx($_SERVER, 'SERVER_ADDR');
if (!$server_addr) {
if (php_sapi_name() == 'cli') {
// This is a command line script (probably something like a unit
// test) so it's fine that we don't have SERVER_ADDR defined.
} else {
throw new AphrontMalformedRequestException(
pht('No %s', 'SERVER_ADDR'),
pht(
'Phabricator is configured to operate in cluster mode, but '.
'%s is not defined in the request context. Your webserver '.
'configuration needs to forward %s to PHP so Phabricator can '.
'reject requests received on external interfaces.',
'SERVER_ADDR',
'SERVER_ADDR'));
}
} else {
if (!PhabricatorEnv::isClusterAddress($server_addr)) {
throw new AphrontMalformedRequestException(
pht('External Interface'),
pht(
'Phabricator is configured in cluster mode and the address '.
'this request was received on ("%s") is not whitelisted as '.
'a cluster address.',
$server_addr));
}
}
}
$site = $this->buildSiteForRequest($request);
if ($site->shouldRequireHTTPS()) {
if (!$request->isHTTPS()) {
// Don't redirect intracluster requests: doing so drops headers and
// parameters, imposes a performance penalty, and indicates a
// misconfiguration.
if ($request->isProxiedClusterRequest()) {
throw new AphrontMalformedRequestException(
pht('HTTPS Required'),
pht(
'This request reached a site which requires HTTPS, but the '.
'request is not marked as HTTPS.'));
}
$https_uri = $request->getRequestURI();
$https_uri->setDomain($request->getHost());
$https_uri->setProtocol('https');
// In this scenario, we'll be redirecting to HTTPS using an absolute
// URI, so we need to permit an external redirect.
return $this->buildRedirectController($https_uri, true);
}
}
$maps = $site->getRoutingMaps();
$path = $request->getPath();
$result = $this->routePath($maps, $path);
if ($result) {
return $result;
}
// If we failed to match anything but don't have a trailing slash, try
// to add a trailing slash and issue a redirect if that resolves.
// NOTE: We only do this for GET, since redirects switch to GET and drop
// data like POST parameters.
if (!preg_match('@/$@', $path) && $request->isHTTPGet()) {
$result = $this->routePath($maps, $path.'/');
if ($result) {
$target_uri = $request->getAbsoluteRequestURI();
// We need to restore URI encoding because the webserver has
// interpreted it. For example, this allows us to redirect a path
// like `/tag/aa%20bb` to `/tag/aa%20bb/`, which may eventually be
// resolved meaningfully by an application.
$target_path = phutil_escape_uri($path.'/');
$target_uri->setPath($target_path);
$target_uri = (string)$target_uri;
return $this->buildRedirectController($target_uri, true);
}
}
$result = $site->new404Controller($request);
if ($result) {
return array($result, array());
}
throw new Exception(
pht(
'Aphront site ("%s") failed to build a 404 controller.',
get_class($site)));
}
/**
* Map a specific path to the corresponding controller. For a description
* of routing, see @{method:buildController}.
*
* @param list<AphrontRoutingMap> List of routing maps.
* @param string Path to route.
* @return pair<AphrontController,dict> Controller and dictionary of request
* parameters.
* @task routing
*/
private function routePath(array $maps, $path) {
foreach ($maps as $map) {
$result = $map->routePath($path);
if ($result) {
return array($result->getController(), $result->getURIData());
}
}
}
private function buildSiteForRequest(AphrontRequest $request) {
$sites = PhabricatorSite::getAllSites();
$site = null;
foreach ($sites as $candidate) {
$site = $candidate->newSiteForRequest($request);
if ($site) {
break;
}
}
if (!$site) {
$path = $request->getPath();
$host = $request->getHost();
throw new AphrontMalformedRequestException(
pht('Site Not Found'),
pht(
'This request asked for "%s" on host "%s", but no site is '.
'configured which can serve this request.',
$path,
$host),
true);
}
$request->setSite($site);
return $site;
}
/* -( Response Handling )-------------------------------------------------- */
/**
* Tests if a response is of a valid type.
*
* @param wild Supposedly valid response.
* @return bool True if the object is of a valid type.
* @task response
*/
private function isValidResponseObject($response) {
if ($response instanceof AphrontResponse) {
return true;
}
if ($response instanceof AphrontResponseProducerInterface) {
return true;
}
return false;
}
/**
* Verifies that the return value from an @{class:AphrontController} is
* of an allowed type.
*
* @param AphrontController Controller which returned the response.
* @param wild Supposedly valid response.
* @return void
* @task response
*/
private function validateControllerResponse(
AphrontController $controller,
$response) {
if ($this->isValidResponseObject($response)) {
return;
}
throw new Exception(
pht(
'Controller "%s" returned an invalid response from call to "%s". '.
'This method must return an object of class "%s", or an object '.
'which implements the "%s" interface.',
get_class($controller),
'handleRequest()',
'AphrontResponse',
'AphrontResponseProducerInterface'));
}
/**
* Verifies that the return value from an
* @{class:AphrontResponseProducerInterface} is of an allowed type.
*
* @param AphrontResponseProducerInterface Object which produced
* this response.
* @param wild Supposedly valid response.
* @return void
* @task response
*/
private function validateProducerResponse(
AphrontResponseProducerInterface $producer,
$response) {
if ($this->isValidResponseObject($response)) {
return;
}
throw new Exception(
pht(
'Producer "%s" returned an invalid response from call to "%s". '.
'This method must return an object of class "%s", or an object '.
'which implements the "%s" interface.',
get_class($producer),
'produceAphrontResponse()',
'AphrontResponse',
'AphrontResponseProducerInterface'));
}
/**
* Verifies that the return value from an
* @{class:AphrontRequestExceptionHandler} is of an allowed type.
*
* @param AphrontRequestExceptionHandler Object which produced this
* response.
* @param wild Supposedly valid response.
* @return void
* @task response
*/
private function validateErrorHandlerResponse(
AphrontRequestExceptionHandler $handler,
$response) {
if ($this->isValidResponseObject($response)) {
return;
}
throw new Exception(
pht(
'Exception handler "%s" returned an invalid response from call to '.
'"%s". This method must return an object of class "%s", or an object '.
'which implements the "%s" interface.',
get_class($handler),
'handleRequestException()',
'AphrontResponse',
'AphrontResponseProducerInterface'));
}
/**
* Resolves a response object into an @{class:AphrontResponse}.
*
* Controllers are permitted to return actual responses of class
* @{class:AphrontResponse}, or other objects which implement
* @{interface:AphrontResponseProducerInterface} and can produce a response.
*
* If a controller returns a response producer, invoke it now and produce
* the real response.
*
* @param AphrontRequest Request being handled.
* @param AphrontResponse|AphrontResponseProducerInterface Response, or
* response producer.
* @return AphrontResponse Response after any required production.
* @task response
*/
private function produceResponse(AphrontRequest $request, $response) {
$original = $response;
// Detect cycles on the exact same objects. It's still possible to produce
// infinite responses as long as they're all unique, but we can only
// reasonably detect cycles, not guarantee that response production halts.
$seen = array();
while (true) {
// NOTE: It is permissible for an object to be both a response and a
// response producer. If so, being a producer is "stronger". This is
// used by AphrontProxyResponse.
// If this response is a valid response, hand over the request first.
if ($response instanceof AphrontResponse) {
$response->setRequest($request);
}
// If this isn't a producer, we're all done.
if (!($response instanceof AphrontResponseProducerInterface)) {
break;
}
$hash = spl_object_hash($response);
if (isset($seen[$hash])) {
throw new Exception(
pht(
'Failure while producing response for object of class "%s": '.
'encountered production cycle (identical object, of class "%s", '.
'was produced twice).',
get_class($original),
get_class($response)));
}
$seen[$hash] = true;
$new_response = $response->produceAphrontResponse();
$this->validateProducerResponse($response, $new_response);
$response = $new_response;
}
return $response;
}
/* -( Error Handling )----------------------------------------------------- */
/**
* Convert an exception which has escaped the controller into a response.
*
* This method delegates exception handling to available subclasses of
* @{class:AphrontRequestExceptionHandler}.
*
* @param Throwable Exception which needs to be handled.
* @return wild Response or response producer, or null if no available
* handler can produce a response.
* @task exception
*/
private function handleThrowable($throwable) {
$handlers = AphrontRequestExceptionHandler::getAllHandlers();
$request = $this->getRequest();
foreach ($handlers as $handler) {
if ($handler->canHandleRequestThrowable($request, $throwable)) {
$response = $handler->handleRequestThrowable($request, $throwable);
$this->validateErrorHandlerResponse($handler, $response);
return $response;
}
}
throw $throwable;
}
private static function newSelfCheckResponse() {
$path = PhabricatorStartup::getRequestPath();
$query = idx($_SERVER, 'QUERY_STRING', '');
$pairs = id(new PhutilQueryStringParser())
->parseQueryStringToPairList($query);
$params = array();
foreach ($pairs as $v) {
$params[] = array(
'name' => $v[0],
'value' => $v[1],
);
}
$raw_input = @file_get_contents('php://input');
if ($raw_input !== false) {
$base64_input = base64_encode($raw_input);
} else {
$base64_input = null;
}
$result = array(
'path' => $path,
'params' => $params,
'user' => idx($_SERVER, 'PHP_AUTH_USER'),
'pass' => idx($_SERVER, 'PHP_AUTH_PW'),
'raw.base64' => $base64_input,
// This just makes sure that the response compresses well, so reasonable
// algorithms should want to gzip or deflate it.
'filler' => str_repeat('Q', 1024 * 16),
);
return id(new AphrontJSONResponse())
->setAddJSONShield(false)
->setContent($result);
}
private static function readHTTPPOSTData() {
$request_method = idx($_SERVER, 'REQUEST_METHOD');
if ($request_method === 'PUT') {
// For PUT requests, do nothing: in particular, do NOT read input. This
// allows us to stream input later and process very large PUT requests,
// like those coming from Git LFS.
return;
}
// For POST requests, we're going to read the raw input ourselves here
// if we can. Among other things, this corrects variable names with
// the "." character in them, which PHP normally converts into "_".
// If "enable_post_data_reading" is on, the documentation suggests we
// can not read the body. In practice, we seem to be able to. This may
// need to be resolved at some point, likely by instructing installs
// to disable this option.
// If the content type is "multipart/form-data", we need to build both
// $_POST and $_FILES, which is involved. The body itself is also more
// difficult to parse than other requests.
$raw_input = PhabricatorStartup::getRawInput();
$parser = new PhutilQueryStringParser();
if (strlen($raw_input)) {
$content_type = idx($_SERVER, 'CONTENT_TYPE');
$is_multipart = preg_match('@^multipart/form-data@i', $content_type);
if ($is_multipart) {
$multipart_parser = id(new AphrontMultipartParser())
->setContentType($content_type);
$multipart_parser->beginParse();
$multipart_parser->continueParse($raw_input);
$parts = $multipart_parser->endParse();
// We're building and then parsing a query string so that requests
// with arrays (like "x[]=apple&x[]=banana") work correctly. This also
// means we can't use "phutil_build_http_querystring()", since it
// can't build a query string with duplicate names.
$query_string = array();
foreach ($parts as $part) {
if (!$part->isVariable()) {
continue;
}
$name = $part->getName();
$value = $part->getVariableValue();
$query_string[] = rawurlencode($name).'='.rawurlencode($value);
}
$query_string = implode('&', $query_string);
$post = $parser->parseQueryString($query_string);
$files = array();
foreach ($parts as $part) {
if ($part->isVariable()) {
continue;
}
$files[$part->getName()] = $part->getPHPFileDictionary();
}
$_FILES = $files;
} else {
$post = $parser->parseQueryString($raw_input);
}
$_POST = $post;
PhabricatorStartup::rebuildRequest();
} else if ($_POST) {
$post = filter_input_array(INPUT_POST, FILTER_UNSAFE_RAW);
if (is_array($post)) {
$_POST = $post;
PhabricatorStartup::rebuildRequest();
}
}
}
}
diff --git a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php
index 7ef44dc384..608bac675e 100644
--- a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php
+++ b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php
@@ -1,101 +1,100 @@
<?php
final class PhabricatorDaemonsSetupCheck extends PhabricatorSetupCheck {
public function getDefaultGroup() {
return self::GROUP_IMPORTANT;
}
protected function executeChecks() {
try {
$task_daemons = id(new PhabricatorDaemonLogQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)
->withDaemonClasses(array('PhabricatorTaskmasterDaemon'))
->setLimit(1)
->execute();
$no_daemons = !$task_daemons;
} catch (Exception $ex) {
// Just skip this warning if the query fails for some reason.
$no_daemons = false;
}
if ($no_daemons) {
$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.');
+ 'You must start the 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).'.
+ 'The daemons are not running, background processing (including '.
+ 'sending email, rebuilding search indexes, importing commits, '.
+ 'cleaning up old data, and running builds) can not be performed.'.
"\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'))
+ ->setName(pht('Daemons Are Not Running'))
->setSummary($summary)
->setMessage($message)
- ->addCommand('phabricator/ $ ./bin/phd start');
+ ->addCommand('$ ./bin/phd start');
}
$expect_user = PhabricatorEnv::getEnvConfig('phd.user');
if (strlen($expect_user)) {
try {
$all_daemons = id(new PhabricatorDaemonLogQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)
->execute();
} catch (Exception $ex) {
// If this query fails for some reason, just skip this check.
$all_daemons = array();
}
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');
+ ->addCommand('$ ./bin/phd restart');
break;
}
}
}
}
diff --git a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php
index 1de39f3468..ae14a0ab0a 100644
--- a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php
+++ b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php
@@ -1,553 +1,551 @@
<?php
final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
public function getDefaultGroup() {
return self::GROUP_OTHER;
}
protected function executeChecks() {
$ancient_config = self::getAncientConfig();
$all_keys = PhabricatorEnv::getAllConfigKeys();
$all_keys = array_keys($all_keys);
sort($all_keys);
$defined_keys = PhabricatorApplicationConfigOptions::loadAllOptions();
$stack = PhabricatorEnv::getConfigSourceStack();
$stack = $stack->getStack();
foreach ($all_keys as $key) {
if (isset($defined_keys[$key])) {
continue;
}
if (isset($ancient_config[$key])) {
$summary = pht(
'This option has been removed. You may delete it at your '.
'convenience.');
$message = pht(
"The configuration option '%s' has been removed. You may delete ".
"it at your convenience.".
"\n\n%s",
$key,
$ancient_config[$key]);
$short = pht('Obsolete Config');
$name = pht('Obsolete Configuration Option "%s"', $key);
} else {
$summary = pht('This option is not recognized. It may be misspelled.');
$message = pht(
- "The configuration option '%s' is not recognized. It may be ".
- "misspelled, or it might have existed in an older version of ".
- "Phabricator. It has no effect, and should be corrected or deleted.",
+ 'The configuration option "%s" is not recognized. It may be '.
+ 'misspelled, or it might have existed in an older version of '.
+ 'the software. It has no effect, and should be corrected or deleted.',
$key);
$short = pht('Unknown Config');
$name = pht('Unknown Configuration Option "%s"', $key);
}
$issue = $this->newIssue('config.unknown.'.$key)
->setShortName($short)
->setName($name)
->setSummary($summary);
$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);
+ $command = csprintf('$ ./bin/config delete %s', $key);
$issue->addCommand($command);
}
if ($found_database) {
$issue->addPhabricatorConfig($key);
}
}
$options = PhabricatorApplicationConfigOptions::loadAllOptions();
foreach ($defined_keys as $key => $value) {
$option = idx($options, $key);
if (!$option) {
continue;
}
if (!$option->getLocked()) {
continue;
}
$found_database = false;
foreach ($stack as $source_key => $source) {
$value = $source->getKeys(array($key));
if ($value) {
if ($source instanceof PhabricatorConfigDatabaseSource) {
$found_database = true;
break;
}
}
}
if (!$found_database) {
continue;
}
// NOTE: These are values which we don't let you edit directly, but edit
// via other UI workflows. For now, don't raise this warning about them.
// In the future, before we stop reading database configuration for
// locked values, we either need to add a flag which lets these values
// continue reading from the database or move them to some other storage
// mechanism.
$soft_locks = array(
'phabricator.uninstalled-applications',
'phabricator.application-settings',
'config.ignore-issues',
'auth.lock-config',
);
$soft_locks = array_fuse($soft_locks);
if (isset($soft_locks[$key])) {
continue;
}
$doc_name = 'Configuration Guide: Locked and Hidden Configuration';
$doc_href = PhabricatorEnv::getDoclink($doc_name);
$set_command = phutil_tag(
'tt',
array(),
csprintf(
'bin/config set %R <value>',
$key));
$summary = pht(
'Configuration value "%s" is locked, but has a value in the database.',
$key);
$message = pht(
'The configuration value "%s" is locked (so it can not be edited '.
'from the web UI), but has a database value. Usually, this means '.
'that it was previously not locked, you set it using the web UI, '.
'and it later became locked.'.
"\n\n".
'You should copy this configuration value to a local configuration '.
'source (usually by using %s) and then remove it from the database '.
'with the command below.'.
"\n\n".
'For more information on locked and hidden configuration, including '.
'details about this setup issue, see %s.'.
"\n\n".
'This database value is currently respected, but a future version '.
- 'of Phabricator will stop respecting database values for locked '.
+ 'of the software will stop respecting database values for locked '.
'configuration options.',
$key,
$set_command,
phutil_tag(
'a',
array(
'href' => $doc_href,
'target' => '_blank',
),
$doc_name));
$command = csprintf(
- 'phabricator/ $ ./bin/config delete --database %R',
+ '$ ./bin/config delete --database %R',
$key);
$this->newIssue('config.locked.'.$key)
->setShortName(pht('Deprecated Config Source'))
->setName(
pht(
'Locked Configuration Option "%s" Has Database Value',
$key))
->setSummary($summary)
->setMessage($message)
->addCommand($command)
->addPhabricatorConfig($key);
}
if (PhabricatorEnv::getEnvConfig('feed.http-hooks')) {
$this->newIssue('config.deprecated.feed.http-hooks')
->setShortName(pht('Feed Hooks Deprecated'))
->setName(pht('Migrate From "feed.http-hooks" to Webhooks'))
->addPhabricatorConfig('feed.http-hooks')
->setMessage(
pht(
'The "feed.http-hooks" option is deprecated in favor of '.
'Webhooks. This option will be removed in a future version '.
- 'of Phabricator.'.
+ 'of the software.'.
"\n\n".
'You can configure Webhooks in Herald.'.
"\n\n".
'To resolve this issue, remove all URIs from "feed.http-hooks".'));
}
}
/**
* 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 '.
'%s or %s.',
'PhabricatorRemarkupCustomInlineRule',
'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.');
+ 'Global customization of monospaced fonts is no longer supported.');
$public_mail_reason = pht(
'Inbound mail addresses are now configured for each application '.
'in the Applications tool.');
$gc_reason = pht(
'Garbage collectors are now configured with "%s".',
'bin/garbage set-policy');
$aphlict_reason = pht(
'Configuration of the notification server has changed substantially. '.
'For discussion, see T10794.');
$stale_reason = pht(
'The Differential revision list view age UI elements have been removed '.
'to simplify the interface.');
$global_settings_reason = pht(
'The "Re: Prefix" and "Vary Subjects" settings are now configured '.
'in global settings.');
$dashboard_reason = pht(
'This option has been removed, you can use Dashboards to provide '.
'homepage customization. See T11533 for more details.');
$elastic_reason = pht(
'Elasticsearch is now configured with "%s".',
'cluster.search');
$mailers_reason = pht(
'Inbound and outbound mail is now configured with "cluster.mailers".');
$prefix_reason = pht(
'Per-application mail subject prefix customization is no longer '.
'directly supported. Prefixes and other strings may be customized with '.
'"translation.override".');
$phd_reason = pht(
'Use "bin/phd debug ..." to get a detailed daemon execution log.');
$ancient_config += array(
'phid.external-loaders' =>
pht(
'External loaders have been replaced. Extend `%s` '.
'to implement new PHID and handle types.',
'PhabricatorPHIDType'),
'maniphest.custom-task-extensions-class' =>
pht(
'Maniphest fields are now loaded automatically. '.
'You can configure them with `%s`.',
'maniphest.fields'),
'maniphest.custom-fields' =>
pht(
'Maniphest fields are now defined in `%s`. '.
'Existing definitions have been migrated.',
'maniphest.custom-field-definitions'),
'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 `%s`.',
+ 'Global access controls now exist, see `%s`.',
'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.'),
+ 'Typeahead strategies are now managed 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 `%s` to emphasize the '.
'unfinished nature of many prototype applications. '.
'Your existing setting has been migrated.',
'phabricator.show-prototypes'),
'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 `%s` instead of this option.',
'config.hide'),
'phd.start-taskmasters' => pht(
'Taskmasters now use an autoscaling pool. You can configure the '.
'pool size with `%s`.',
'phd.taskmasters'),
'storage.engine-selector' => pht(
- 'Phabricator now automatically discovers available storage engines '.
- 'at runtime.'),
+ 'Storage engines are now discovered automatically at runtime.'),
'storage.upload-size-limit' => pht(
- 'Phabricator now supports arbitrarily large files. Consult the '.
+ 'Arbitrarily large files are now supported. Consult the '.
'documentation for configuration details.'),
'security.allow-outbound-http' => pht(
'This option has been replaced with the more granular option `%s`.',
'security.outbound-blacklist'),
'metamta.reply.show-hints' => pht(
- 'Phabricator no longer shows reply hints in mail.'),
+ 'Reply hints are no longer shown 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.'),
+ 'Transaction mail is now always sent with "Precedence: bulk" to '.
+ 'improve deliverability.'),
'style.monospace' => $monospace_reason,
'style.monospace.windows' => $monospace_reason,
'search.engine-selector' => pht(
- 'Phabricator now automatically discovers available search engines '.
- 'at runtime.'),
+ 'Available search engines are now automatically discovered at '.
+ 'runtime.'),
'metamta.files.public-create-email' => $public_mail_reason,
'metamta.maniphest.public-create-email' => $public_mail_reason,
'metamta.maniphest.default-public-author' => $public_mail_reason,
'metamta.paste.public-create-email' => $public_mail_reason,
'security.allow-conduit-act-as-user' => pht(
'Impersonating users over the API is no longer supported.'),
'feed.public' => pht('The framable public feed is no longer supported.'),
'auth.login-message' => pht(
'This configuration option has been replaced with a modular '.
'handler. See T9346.'),
'gcdaemon.ttl.herald-transcripts' => $gc_reason,
'gcdaemon.ttl.daemon-logs' => $gc_reason,
'gcdaemon.ttl.differential-parse-cache' => $gc_reason,
'gcdaemon.ttl.markup-cache' => $gc_reason,
'gcdaemon.ttl.task-archive' => $gc_reason,
'gcdaemon.ttl.general-cache' => $gc_reason,
'gcdaemon.ttl.conduit-logs' => $gc_reason,
'phd.variant-config' => pht(
'This configuration is no longer relevant because daemons '.
'restart automatically on configuration changes.'),
'notification.ssl-cert' => $aphlict_reason,
'notification.ssl-key' => $aphlict_reason,
'notification.pidfile' => $aphlict_reason,
'notification.log' => $aphlict_reason,
'notification.enabled' => $aphlict_reason,
'notification.client-uri' => $aphlict_reason,
'notification.server-uri' => $aphlict_reason,
'metamta.differential.unified-comment-context' => pht(
'Inline comments are now always rendered with a limited amount '.
'of context.'),
'differential.days-fresh' => $stale_reason,
'differential.days-stale' => $stale_reason,
'metamta.re-prefix' => $global_settings_reason,
'metamta.vary-subjects' => $global_settings_reason,
'ui.custom-header' => pht(
'This option has been replaced with `ui.logo`, which provides more '.
'flexible configuration options.'),
'welcome.html' => $dashboard_reason,
'maniphest.priorities.unbreak-now' => $dashboard_reason,
'maniphest.priorities.needs-triage' => $dashboard_reason,
'mysql.implementation' => pht(
- 'Phabricator now automatically selects the best available '.
- 'MySQL implementation.'),
+ 'The best available MYSQL implementation is now selected '.
+ 'automatically.'),
'mysql.configuration-provider' => pht(
- 'Phabricator now has application-level management of partitioning '.
- 'and replicas.'),
+ 'Partitioning and replication are now managed in primary '.
+ 'configuration.'),
'search.elastic.host' => $elastic_reason,
'search.elastic.namespace' => $elastic_reason,
'metamta.mail-adapter' => $mailers_reason,
'amazon-ses.access-key' => $mailers_reason,
'amazon-ses.secret-key' => $mailers_reason,
'amazon-ses.endpoint' => $mailers_reason,
'mailgun.domain' => $mailers_reason,
'mailgun.api-key' => $mailers_reason,
'phpmailer.mailer' => $mailers_reason,
'phpmailer.smtp-host' => $mailers_reason,
'phpmailer.smtp-port' => $mailers_reason,
'phpmailer.smtp-protocol' => $mailers_reason,
'phpmailer.smtp-user' => $mailers_reason,
'phpmailer.smtp-password' => $mailers_reason,
'phpmailer.smtp-encoding' => $mailers_reason,
'sendgrid.api-user' => $mailers_reason,
'sendgrid.api-key' => $mailers_reason,
'celerity.resource-hash' => pht(
'This option generally did not prove useful. Resource hash keys '.
'are now managed automatically.'),
'celerity.enable-deflate' => pht(
'Resource deflation is now managed automatically.'),
'celerity.minify' => pht(
'Resource minification is now managed automatically.'),
'metamta.domain' => pht(
'Mail thread IDs are now generated automatically.'),
'metamta.placeholder-to-recipient' => pht(
'Placeholder recipients are now generated automatically.'),
'metamta.mail-key' => pht(
'Mail object address hash keys are now generated automatically.'),
'phabricator.csrf-key' => pht(
'CSRF HMAC keys are now managed automatically.'),
'metamta.insecure-auth-with-reply-to' => pht(
'Authenticating users based on "Reply-To" is no longer supported.'),
'phabricator.allow-email-users' => pht(
'Public email is now accepted if the associated address has a '.
'default author, and rejected otherwise.'),
'metamta.conpherence.subject-prefix' => $prefix_reason,
'metamta.differential.subject-prefix' => $prefix_reason,
'metamta.diffusion.subject-prefix' => $prefix_reason,
'metamta.files.subject-prefix' => $prefix_reason,
'metamta.legalpad.subject-prefix' => $prefix_reason,
'metamta.macro.subject-prefix' => $prefix_reason,
'metamta.maniphest.subject-prefix' => $prefix_reason,
'metamta.package.subject-prefix' => $prefix_reason,
'metamta.paste.subject-prefix' => $prefix_reason,
'metamta.pholio.subject-prefix' => $prefix_reason,
'metamta.phriction.subject-prefix' => $prefix_reason,
'aphront.default-application-configuration-class' => pht(
'This ancient extension point has been replaced with other '.
'mechanisms, including "AphrontSite".'),
'differential.whitespace-matters' => pht(
'Whitespace rendering is now handled automatically.'),
'phd.pid-directory' => pht(
- 'Phabricator daemons no longer use PID files.'),
+ 'Daemons no longer use PID files.'),
'phd.trace' => $phd_reason,
'phd.verbose' => $phd_reason,
);
return $ancient_config;
}
}
diff --git a/src/applications/config/check/PhabricatorManualActivitySetupCheck.php b/src/applications/config/check/PhabricatorManualActivitySetupCheck.php
index 04457cb12a..660792ca53 100644
--- a/src/applications/config/check/PhabricatorManualActivitySetupCheck.php
+++ b/src/applications/config/check/PhabricatorManualActivitySetupCheck.php
@@ -1,147 +1,153 @@
<?php
final class PhabricatorManualActivitySetupCheck
extends PhabricatorSetupCheck {
public function getDefaultGroup() {
return self::GROUP_OTHER;
}
protected function executeChecks() {
$activities = id(new PhabricatorConfigManualActivity())->loadAll();
foreach ($activities as $activity) {
$type = $activity->getActivityType();
switch ($type) {
case PhabricatorConfigManualActivity::TYPE_REINDEX:
$this->raiseSearchReindexIssue();
break;
case PhabricatorConfigManualActivity::TYPE_IDENTITIES:
$this->raiseRebuildIdentitiesIssue();
break;
default:
}
}
}
private function raiseSearchReindexIssue() {
$activity_name = pht('Rebuild Search Index');
$activity_summary = pht(
'The search index algorithm has been updated and the index needs '.
'be rebuilt.');
$message = array();
$message[] = pht(
'The indexing algorithm for the fulltext search index has been '.
'updated and the index needs to be rebuilt. Until you rebuild the '.
'index, global search (and other fulltext search) will not '.
'function correctly.');
$message[] = pht(
- 'You can rebuild the search index while Phabricator is running.');
+ 'You can rebuild the search index while the server is running.');
$message[] = pht(
'To rebuild the index, run this command:');
$message[] = phutil_tag(
'pre',
array(),
(string)csprintf(
- 'phabricator/ $ ./bin/search index --all --force --background'));
+ '$ ./bin/search index --all --force --background'));
$message[] = pht(
'You can find more information about rebuilding the search '.
'index here: %s',
phutil_tag(
'a',
array(
'href' => 'https://phurl.io/u/reindex',
'target' => '_blank',
),
'https://phurl.io/u/reindex'));
$message[] = pht(
'After rebuilding the index, run this command to clear this setup '.
'warning:');
$message[] = phutil_tag(
'pre',
array(),
- 'phabricator/ $ ./bin/config done reindex');
+ '$ ./bin/config done reindex');
$activity_message = phutil_implode_html("\n\n", $message);
$this->newIssue('manual.reindex')
->setName($activity_name)
->setSummary($activity_summary)
->setMessage($activity_message);
}
private function raiseRebuildIdentitiesIssue() {
$activity_name = pht('Rebuild Repository Identities');
$activity_summary = pht(
- 'The mapping from VCS users to Phabricator users has changed '.
- 'and must be rebuilt.');
+ 'The mapping from VCS users to %s users has changed '.
+ 'and must be rebuilt.',
+ PlatformSymbols::getPlatformServerName());
$message = array();
$message[] = pht(
- 'The way Phabricator attributes VCS activity to Phabricator users '.
- 'has changed. There is a new indirection layer between the strings '.
- 'that appear as VCS authors and committers (such as "John Developer '.
- '<johnd@bigcorp.com>") and the Phabricator user that gets associated '.
- 'with VCS commits. This is to support situations where users '.
- 'are incorrectly associated with commits by Phabricator making bad '.
- 'guesses about the identity of the corresponding Phabricator user. '.
+ 'The way VCS activity is attributed %s user accounts has changed.',
+ PlatformSymbols::getPlatformServerName());
+
+ $message[] = pht(
+ 'There is a new indirection layer between the strings that appear as '.
+ 'VCS authors and committers (such as "John Developer '.
+ '<johnd@bigcorp.com>") and the user account that gets associated '.
+ 'with VCS commits.');
+
+ $message[] = pht(
+ 'This change supports situations where users are incorrectly '.
+ 'associated with commits because the software makes a bad guess '.
+ 'about how the VCS string maps to a user account. '.
'This also helps with situations where existing repositories are '.
'imported without having created accounts for all the committers to '.
'that repository. Until you rebuild these repository identities, you '.
- 'are likely to encounter problems with future Phabricator features '.
- 'which will rely on the existence of these identities.');
+ 'are likely to encounter problems with features which rely on the '.
+ 'existence of these identities.');
$message[] = pht(
- 'You can rebuild repository identities while Phabricator is running.');
+ 'You can rebuild repository identities while the server is running.');
$message[] = pht(
'To rebuild identities, run this command:');
$message[] = phutil_tag(
'pre',
array(),
(string)csprintf(
- 'phabricator/ $ '.
- './bin/repository rebuild-identities --all-repositories'));
+ '$ ./bin/repository rebuild-identities --all-repositories'));
$message[] = pht(
'You can find more information about this new identity mapping '.
'here: %s',
phutil_tag(
'a',
array(
'href' => 'https://phurl.io/u/repoIdentities',
'target' => '_blank',
),
'https://phurl.io/u/repoIdentities'));
$message[] = pht(
'After rebuilding repository identities, run this command to clear '.
'this setup warning:');
$message[] = phutil_tag(
'pre',
array(),
- 'phabricator/ $ ./bin/config done identities');
+ '$ ./bin/config done identities');
$activity_message = phutil_implode_html("\n\n", $message);
$this->newIssue('manual.identities')
->setName($activity_name)
->setSummary($activity_summary)
->setMessage($activity_message);
}
}
diff --git a/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php b/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php
index 30c6036c8f..02c1134dc3 100644
--- a/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php
+++ b/src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php
@@ -1,147 +1,150 @@
<?php
final class PhabricatorPHPPreflightSetupCheck extends PhabricatorSetupCheck {
public function getDefaultGroup() {
return self::GROUP_PHP;
}
public function isPreflightCheck() {
return true;
}
protected function executeChecks() {
$version = phpversion();
if (version_compare($version, 7, '>=') &&
version_compare($version, 7.1, '<')) {
$message = pht(
- 'You are running PHP version %s. Phabricator does not support PHP '.
- 'versions between 7.0 and 7.1.'.
+ 'You are running PHP version %s. PHP versions between 7.0 and 7.1 '.
+ 'are not supported'.
"\n\n".
- 'PHP removed signal handling features that Phabricator requires in '.
- 'PHP 7.0, and did not restore them until PHP 7.1.'.
+ 'PHP removed reqiured signal handling features in '.
+ 'PHP 7.0, and did not restore an equivalent mechanism until PHP 7.1.'.
"\n\n".
'Upgrade to PHP 7.1 or newer (recommended) or downgrade to an older '.
'version of PHP 5 (discouraged).',
$version);
$this->newIssue('php.version7')
->setIsFatal(true)
->setName(pht('PHP 7.0-7.1 Not Supported'))
->setMessage($message)
->addLink(
'https://phurl.io/u/php7',
- pht('Phabricator PHP 7 Compatibility Information'));
+ pht('PHP 7 Compatibility Information'));
return;
}
+ // TODO: This can be removed entirely because the minimum PHP version is
+ // now PHP 5.5, which does not have safe mode.
+
$safe_mode = ini_get('safe_mode');
if ($safe_mode) {
$message = pht(
- "You have '%s' enabled in your PHP configuration, but Phabricator ".
+ "You have '%s' enabled in your PHP configuration, but this software ".
"will not run in safe mode. Safe mode has been deprecated in PHP 5.3 ".
"and removed in PHP 5.4.\n\nDisable safe mode to continue.",
'safe_mode');
$this->newIssue('php.safe_mode')
->setIsFatal(true)
->setName(pht('Disable PHP %s', 'safe_mode'))
->setMessage($message)
->addPHPConfig('safe_mode');
return;
}
// Check for `disable_functions` or `disable_classes`. Although it's
// possible to disable a bunch of functions (say, `array_change_key_case()`)
// and classes and still have Phabricator work fine, it's unreasonably
// difficult for us to be sure we'll even survive setup if these options
// are enabled. Phabricator needs access to the most dangerous functions,
// so there is no reasonable configuration value here which actually
// provides a benefit while guaranteeing Phabricator will run properly.
$disable_options = array('disable_functions', 'disable_classes');
foreach ($disable_options as $disable_option) {
$disable_value = ini_get($disable_option);
if ($disable_value) {
// By default Debian installs the pcntl extension but disables all of
// its functions using configuration. Whitelist disabling these
// functions so that Debian PHP works out of the box (we do not need to
// call these functions from the web UI). This is pretty ridiculous but
// it's not the users' fault and they haven't done anything crazy to
// get here, so don't make them pay for Debian's unusual choices.
// See: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=605571
$fatal = true;
if ($disable_option == 'disable_functions') {
$functions = preg_split('/[, ]+/', $disable_value);
$functions = array_filter($functions);
foreach ($functions as $k => $function) {
if (preg_match('/^pcntl_/', $function)) {
unset($functions[$k]);
}
}
if (!$functions) {
$fatal = false;
}
}
if ($fatal) {
$message = pht(
"You have '%s' enabled in your PHP configuration.\n\n".
- "This option is not compatible with Phabricator. Remove ".
+ "This option is not compatible with this software. Remove ".
"'%s' from your configuration to continue.",
$disable_option,
$disable_option);
$this->newIssue('php.'.$disable_option)
->setIsFatal(true)
->setName(pht('Remove PHP %s', $disable_option))
->setMessage($message)
->addPHPConfig($disable_option);
}
}
}
$overload_option = 'mbstring.func_overload';
$func_overload = ini_get($overload_option);
if ($func_overload) {
$message = pht(
"You have '%s' enabled in your PHP configuration.\n\n".
- "This option is not compatible with Phabricator. Disable ".
+ "This option is not compatible with this software. Disable ".
"'%s' in your PHP configuration to continue.",
$overload_option,
$overload_option);
$this->newIssue('php'.$overload_option)
->setIsFatal(true)
->setName(pht('Disable PHP %s', $overload_option))
->setMessage($message)
->addPHPConfig($overload_option);
}
$open_basedir = ini_get('open_basedir');
if (strlen($open_basedir)) {
// If `open_basedir` is set, just fatal. It's technically possible for
// us to run with certain values of `open_basedir`, but: we can only
// raise fatal errors from preflight steps, so we'd have to do this check
// in two parts to support fatal and advisory versions; it's much simpler
// to just fatal instead of trying to test all the different things we
// may need to access in the filesystem; and use of this option seems
// rare (particularly in supported environments).
$message = pht(
- "Your server is configured with '%s', which prevents Phabricator ".
+ "Your server is configured with '%s', which prevents this software ".
"from opening files it requires access to.\n\n".
"Disable this setting to continue.",
'open_basedir');
$issue = $this->newIssue('php.open_basedir')
->setName(pht('Disable PHP %s', 'open_basedir'))
->addPHPConfig('open_basedir')
->setIsFatal(true)
->setMessage($message);
}
}
}
diff --git a/src/applications/config/check/PhabricatorWebServerSetupCheck.php b/src/applications/config/check/PhabricatorWebServerSetupCheck.php
index 46255629e7..cc9660325c 100644
--- a/src/applications/config/check/PhabricatorWebServerSetupCheck.php
+++ b/src/applications/config/check/PhabricatorWebServerSetupCheck.php
@@ -1,384 +1,384 @@
<?php
final class PhabricatorWebServerSetupCheck extends PhabricatorSetupCheck {
public function getDefaultGroup() {
return self::GROUP_OTHER;
}
protected function executeChecks() {
// The documentation says these headers exist, but it's not clear if they
// are entirely reliable in practice.
if (isset($_SERVER['HTTP_X_MOD_PAGESPEED']) ||
isset($_SERVER['HTTP_X_PAGE_SPEED'])) {
$this->newIssue('webserver.pagespeed')
->setName(pht('Disable Pagespeed'))
->setSummary(pht('Pagespeed is enabled, but should be disabled.'))
->setMessage(
pht(
- 'Phabricator received an "X-Mod-Pagespeed" or "X-Page-Speed" '.
+ 'This server received an "X-Mod-Pagespeed" or "X-Page-Speed" '.
'HTTP header on this request, which indicates that you have '.
'enabled "mod_pagespeed" on this server. This module is not '.
- 'compatible with Phabricator. You should disable it.'));
+ 'compatible with this software. You should disable the module.'));
}
$base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
if (!strlen($base_uri)) {
// If `phabricator.base-uri` is not set then we can't really do
// anything.
return;
}
$expect_user = 'alincoln';
$expect_pass = 'hunter2';
$send_path = '/test-%252A/';
$expect_path = '/test-%2A/';
$expect_key = 'duck-sound';
$expect_value = 'quack';
$base_uri = id(new PhutilURI($base_uri))
->setPath($send_path)
->replaceQueryParam($expect_key, $expect_value);
$self_future = id(new HTTPSFuture($base_uri))
- ->addHeader('X-Phabricator-SelfCheck', 1)
+ ->addHeader('X-Setup-SelfCheck', 1)
->addHeader('Accept-Encoding', 'gzip')
->setDisableContentDecoding(true)
->setHTTPBasicAuthCredentials(
$expect_user,
new PhutilOpaqueEnvelope($expect_pass))
->setTimeout(5);
if (AphrontRequestStream::supportsGzip()) {
$gzip_uncompressed = str_repeat('Quack! ', 128);
$gzip_compressed = gzencode($gzip_uncompressed);
$gzip_future = id(new HTTPSFuture($base_uri))
- ->addHeader('X-Phabricator-SelfCheck', 1)
+ ->addHeader('X-Setup-SelfCheck', 1)
->addHeader('Content-Encoding', 'gzip')
->setTimeout(5)
->setData($gzip_compressed);
} else {
$gzip_future = null;
}
// Make a request to the metadata service available on EC2 instances,
// to test if we're running on a T2 instance in AWS so we can warn that
// this is a bad idea. Outside of AWS, this request will just fail.
$ec2_uri = 'http://169.254.169.254/latest/meta-data/instance-type';
$ec2_future = id(new HTTPSFuture($ec2_uri))
->setTimeout(1);
$futures = array(
$self_future,
$ec2_future,
);
if ($gzip_future) {
$futures[] = $gzip_future;
}
$futures = new FutureIterator($futures);
foreach ($futures as $future) {
// Just resolve the futures here.
}
try {
list($body) = $ec2_future->resolvex();
$body = trim($body);
if (preg_match('/^t2/', $body)) {
$message = pht(
- 'Phabricator appears to be installed on a very small EC2 instance '.
+ 'This software appears to be installed on a very small EC2 instance '.
'(of class "%s") with burstable CPU. This is strongly discouraged. '.
- 'Phabricator regularly needs CPU, and these instances are often '.
+ 'This software regularly needs CPU, and these instances are often '.
'choked to death by CPU throttling. Use an instance with a normal '.
'CPU instead.',
$body);
$this->newIssue('ec2.burstable')
->setName(pht('Installed on Burstable CPU Instance'))
->setSummary(
pht(
- 'Do not install Phabricator on an instance class with '.
+ 'Do not install this software on an instance class with '.
'burstable CPU.'))
->setMessage($message);
}
} catch (Exception $ex) {
// If this fails, just continue. We're probably not running in EC2.
}
try {
list($body, $headers) = $self_future->resolvex();
} catch (Exception $ex) {
// If this fails for whatever reason, just ignore it. Hopefully, the
// error is obvious and the user can correct it on their own, but we
// can't do much to offer diagnostic advice.
return;
}
if (BaseHTTPFuture::getHeader($headers, 'Content-Encoding') != 'gzip') {
$message = pht(
- 'Phabricator sent itself a request with "Accept-Encoding: gzip", '.
+ 'This software sent itself a request with "Accept-Encoding: gzip", '.
'but received an uncompressed response.'.
"\n\n".
'This may indicate that your webserver is not configured to '.
'compress responses. If so, you should enable compression. '.
'Compression can dramatically improve performance, especially '.
'for clients with less bandwidth.');
$this->newIssue('webserver.gzip')
->setName(pht('GZip Compression May Not Be Enabled'))
->setSummary(pht('Your webserver may have compression disabled.'))
->setMessage($message);
} else {
if (function_exists('gzdecode')) {
$body = @gzdecode($body);
} else {
$body = null;
}
if (!$body) {
// For now, just bail if we can't decode the response.
// This might need to use the stronger magic in "AphrontRequestStream"
// to decode more reliably.
return;
}
}
$structure = null;
$extra_whitespace = ($body !== trim($body));
try {
$structure = phutil_json_decode(trim($body));
} catch (Exception $ex) {
// Ignore the exception, we only care if the decode worked or not.
}
if (!$structure || $extra_whitespace) {
if (!$structure) {
$short = id(new PhutilUTF8StringTruncator())
->setMaximumGlyphs(1024)
->truncateString($body);
$message = pht(
- 'Phabricator sent itself a test request with the '.
- '"X-Phabricator-SelfCheck" header and expected to get a valid JSON '.
+ 'This software sent itself a test request with the '.
+ '"X-Setup-SelfCheck" header and expected to get a valid JSON '.
'response back. Instead, the response begins:'.
"\n\n".
'%s'.
"\n\n".
'Something is misconfigured or otherwise mangling responses.',
phutil_tag('pre', array(), $short));
} else {
$message = pht(
- 'Phabricator sent itself a test request and expected to get a bare '.
- 'JSON response back. It received a JSON response, but the response '.
- 'had extra whitespace at the beginning or end.'.
+ 'This software sent itself a test request and expected to get a '.
+ 'bare JSON response back. It received a JSON response, but the '.
+ 'response had extra whitespace at the beginning or end.'.
"\n\n".
'This usually means you have edited a file and left whitespace '.
'characters before the opening %s tag, or after a closing %s tag. '.
'Remove any leading whitespace, and prefer to omit closing tags.',
phutil_tag('tt', array(), '<?php'),
phutil_tag('tt', array(), '?>'));
}
$this->newIssue('webserver.mangle')
->setName(pht('Mangled Webserver Response'))
->setSummary(pht('Your webserver produced an unexpected response.'))
->setMessage($message);
// We can't run the other checks if we could not decode the response.
if (!$structure) {
return;
}
}
$actual_user = idx($structure, 'user');
$actual_pass = idx($structure, 'pass');
if (($expect_user != $actual_user) || ($actual_pass != $expect_pass)) {
$message = pht(
- 'Phabricator sent itself a test request with an "Authorization" HTTP '.
- 'header, and expected those credentials to be transmitted. However, '.
- 'they were absent or incorrect when received. Phabricator sent '.
- 'username "%s" with password "%s"; received username "%s" and '.
- 'password "%s".'.
+ 'This software sent itself a test request with an "Authorization" '.
+ 'HTTP header, and expected those credentials to be transmitted. '.
+ 'However, they were absent or incorrect when received. This '.
+ 'software sent username "%s" with password "%s"; received '.
+ 'username "%s" and password "%s".'.
"\n\n".
'Your webserver may not be configured to forward HTTP basic '.
'authentication. If you plan to use basic authentication (for '.
'example, to access repositories) you should reconfigure it.',
$expect_user,
$expect_pass,
$actual_user,
$actual_pass);
$this->newIssue('webserver.basic-auth')
->setName(pht('HTTP Basic Auth Not Configured'))
->setSummary(pht('Your webserver is not forwarding credentials.'))
->setMessage($message);
}
$actual_path = idx($structure, 'path');
if ($expect_path != $actual_path) {
$message = pht(
- 'Phabricator sent itself a test request with an unusual path, to '.
+ 'This software sent itself a test request with an unusual path, to '.
'test if your webserver is rewriting paths correctly. The path was '.
'not transmitted correctly.'.
"\n\n".
- 'Phabricator sent a request to path "%s", and expected the webserver '.
- 'to decode and rewrite that path so that it received a request for '.
- '"%s". However, it received a request for "%s" instead.'.
+ 'This software sent a request to path "%s", and expected the '.
+ 'webserver to decode and rewrite that path so that it received a '.
+ 'request for "%s". However, it received a request for "%s" instead.'.
"\n\n".
'Verify that your rewrite rules are configured correctly, following '.
'the instructions in the documentation. If path encoding is not '.
'working properly you will be unable to access files with unusual '.
'names in repositories, among other issues.'.
"\n\n".
'(This problem can be caused by a missing "B" in your RewriteRule.)',
$send_path,
$expect_path,
$actual_path);
$this->newIssue('webserver.rewrites')
->setName(pht('HTTP Path Rewriting Incorrect'))
->setSummary(pht('Your webserver is rewriting paths improperly.'))
->setMessage($message);
}
$actual_key = pht('<none>');
$actual_value = pht('<none>');
foreach (idx($structure, 'params', array()) as $pair) {
if (idx($pair, 'name') == $expect_key) {
$actual_key = idx($pair, 'name');
$actual_value = idx($pair, 'value');
break;
}
}
if (($expect_key !== $actual_key) || ($expect_value !== $actual_value)) {
$message = pht(
- 'Phabricator sent itself a test request with an HTTP GET parameter, '.
+ 'This software sent itself a test request with an HTTP GET parameter, '.
'but the parameter was not transmitted. Sent "%s" with value "%s", '.
'got "%s" with value "%s".'.
"\n\n".
'Your webserver is configured incorrectly and large parts of '.
- 'Phabricator will not work until this issue is corrected.'.
+ 'this software will not work until this issue is corrected.'.
"\n\n".
'(This problem can be caused by a missing "QSA" in your RewriteRule.)',
$expect_key,
$expect_value,
$actual_key,
$actual_value);
$this->newIssue('webserver.parameters')
->setName(pht('HTTP Parameters Not Transmitting'))
->setSummary(
pht('Your webserver is not handling GET parameters properly.'))
->setMessage($message);
}
if ($gzip_future) {
$this->checkGzipResponse(
$gzip_future,
$gzip_uncompressed,
$gzip_compressed);
}
}
private function checkGzipResponse(
Future $future,
$uncompressed,
$compressed) {
try {
list($body, $headers) = $future->resolvex();
} catch (Exception $ex) {
return;
}
try {
$structure = phutil_json_decode(trim($body));
} catch (Exception $ex) {
return;
}
$raw_body = idx($structure, 'raw.base64');
$raw_body = @base64_decode($raw_body);
// The server received the exact compressed bytes we expected it to, so
// everything is working great.
if ($raw_body === $compressed) {
return;
}
// If the server received a prefix of the raw uncompressed string, it
// is almost certainly configured to decompress responses inline. Guide
// users to this problem narrowly.
// Otherwise, something is wrong but we don't have much of a clue what.
$message = array();
$message[] = pht(
- 'Phabricator sent itself a test request that was compressed with '.
+ 'This software sent itself a test request that was compressed with '.
'"Content-Encoding: gzip", but received different bytes than it '.
'sent.');
$prefix_len = min(strlen($raw_body), strlen($uncompressed));
if ($prefix_len > 16 && !strncmp($raw_body, $uncompressed, $prefix_len)) {
$message[] = pht(
'The request body that the server received had already been '.
'decompressed. This strongly suggests your webserver is configured '.
'to decompress requests inline, before they reach PHP.');
$message[] = pht(
'If you are using Apache, your server may be configured with '.
'"SetInputFilter DEFLATE". This directive destructively mangles '.
'requests and emits them with "Content-Length" and '.
'"Content-Encoding" headers that no longer match the data in the '.
'request body.');
} else {
$message[] = pht(
'This suggests your webserver is configured to decompress or mangle '.
'compressed requests.');
$message[] = pht(
- 'The request body Phabricator sent began:');
+ 'The request body that was sent began:');
$message[] = $this->snipBytes($compressed);
$message[] = pht(
- 'The request body Phabricator received began:');
+ 'The request body that was received began:');
$message[] = $this->snipBytes($raw_body);
}
$message[] = pht(
'Identify the component in your webserver configuration which is '.
- 'decompressing or mangling requests and disable it. Phabricator '.
+ 'decompressing or mangling requests and disable it. This software '.
'will not work properly until you do.');
$message = phutil_implode_html("\n\n", $message);
$this->newIssue('webserver.accept-gzip')
->setName(pht('Compressed Requests Not Received Properly'))
->setSummary(
pht(
'Your webserver is not handling compressed request bodies '.
'properly.'))
->setMessage($message);
}
private function snipBytes($raw) {
if (!strlen($raw)) {
$display = pht('<empty>');
} else {
$snip = substr($raw, 0, 24);
$display = phutil_loggable_string($snip);
if (strlen($snip) < strlen($raw)) {
$display .= '...';
}
}
return phutil_tag('tt', array(), $display);
}
}
diff --git a/src/applications/config/controller/PhabricatorConfigConsoleController.php b/src/applications/config/controller/PhabricatorConfigConsoleController.php
index 0528be347a..bd23d6dde5 100644
--- a/src/applications/config/controller/PhabricatorConfigConsoleController.php
+++ b/src/applications/config/controller/PhabricatorConfigConsoleController.php
@@ -1,336 +1,336 @@
<?php
final class PhabricatorConfigConsoleController
extends PhabricatorConfigController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$menu = id(new PHUIObjectItemListView())
->setViewer($viewer)
->setBig(true);
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Settings'))
->setHref($this->getApplicationURI('settings/'))
->setImageIcon('fa-wrench')
->setClickable(true)
->addAttribute(
pht(
'Review and modify configuration settings.')));
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Setup Issues'))
->setHref($this->getApplicationURI('issue/'))
->setImageIcon('fa-exclamation-triangle')
->setClickable(true)
->addAttribute(
pht(
'Show unresolved issues with setup and configuration.')));
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Services'))
->setHref($this->getApplicationURI('cluster/databases/'))
->setImageIcon('fa-server')
->setClickable(true)
->addAttribute(
pht(
'View status information for databases, caches, repositories, '.
'and other services.')));
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Extensions/Modules'))
->setHref($this->getApplicationURI('module/'))
->setImageIcon('fa-gear')
->setClickable(true)
->addAttribute(
pht(
'Show installed extensions and modules.')));
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb(pht('Console'))
->setBorder(true);
$box = id(new PHUIObjectBoxView())
- ->setHeaderText(pht('Phabricator Configuration'))
+ ->setHeaderText(pht('Configuration'))
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
->setObjectList($menu);
$versions = $this->newLibraryVersionTable($viewer);
$binary_versions = $this->newBinaryVersionTable();
$launcher_view = id(new PHUILauncherView())
->appendChild($box)
->appendChild($versions)
->appendChild($binary_versions);
$view = id(new PHUITwoColumnView())
->setFooter($launcher_view);
return $this->newPage()
- ->setTitle(pht('Phabricator Configuration'))
+ ->setTitle(pht('Configuration'))
->setCrumbs($crumbs)
->appendChild($view);
}
public function newLibraryVersionTable() {
$viewer = $this->getViewer();
$versions = $this->loadVersions($viewer);
$rows = array();
foreach ($versions as $name => $info) {
$branchpoint = $info['branchpoint'];
if (strlen($branchpoint)) {
$branchpoint = substr($branchpoint, 0, 12);
} else {
$branchpoint = null;
}
$version = $info['hash'];
if (strlen($version)) {
$version = substr($version, 0, 12);
} else {
$version = pht('Unknown');
}
$epoch = $info['epoch'];
if ($epoch) {
$epoch = phabricator_date($epoch, $viewer);
} else {
$epoch = null;
}
$rows[] = array(
$name,
$version,
$epoch,
$branchpoint,
);
}
$table_view = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Library'),
pht('Version'),
pht('Date'),
pht('Branchpoint'),
))
->setColumnClasses(
array(
'pri',
null,
null,
'wide',
));
return id(new PHUIObjectBoxView())
- ->setHeaderText(pht('Phabricator Version Information'))
+ ->setHeaderText(pht('Version Information'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($table_view);
}
private function loadVersions(PhabricatorUser $viewer) {
$specs = array(
'phabricator',
'arcanist',
);
$all_libraries = PhutilBootloader::getInstance()->getAllLibraries();
// This puts the core libraries at the top:
$other_libraries = array_diff($all_libraries, $specs);
$specs = array_merge($specs, $other_libraries);
$log_futures = array();
$remote_futures = array();
foreach ($specs as $lib) {
$root = dirname(phutil_get_library_root($lib));
$log_command = csprintf(
'git log --format=%s -n 1 --',
'%H %ct');
$remote_command = csprintf(
'git remote -v');
$log_futures[$lib] = id(new ExecFuture('%C', $log_command))
->setCWD($root);
$remote_futures[$lib] = id(new ExecFuture('%C', $remote_command))
->setCWD($root);
}
$all_futures = array_merge($log_futures, $remote_futures);
id(new FutureIterator($all_futures))
->resolveAll();
// A repository may have a bunch of remotes, but we're only going to look
// for remotes we host to try to figure out where this repository branched.
$upstream_pattern = '(github\.com/phacility/|secure\.phabricator\.com/)';
$upstream_futures = array();
$lib_upstreams = array();
foreach ($specs as $lib) {
$remote_future = $remote_futures[$lib];
list($err, $stdout) = $remote_future->resolve();
if ($err) {
// If this fails for whatever reason, just move on.
continue;
}
// These look like this, with a tab separating the first two fields:
// remote-name http://remote.uri/ (push)
$upstreams = array();
$remotes = phutil_split_lines($stdout, false);
foreach ($remotes as $remote) {
$remote_pattern = '/^([^\t]+)\t([^ ]+) \(([^)]+)\)\z/';
$matches = null;
if (!preg_match($remote_pattern, $remote, $matches)) {
continue;
}
// Remote URIs are either "push" or "fetch": we only care about "fetch"
// URIs.
$type = $matches[3];
if ($type != 'fetch') {
continue;
}
$uri = $matches[2];
$is_upstream = preg_match($upstream_pattern, $uri);
if (!$is_upstream) {
continue;
}
$name = $matches[1];
$upstreams[$name] = $name;
}
// If we have several suitable upstreams, try to pick the one named
// "origin", if it exists. Otherwise, just pick the first one.
if (isset($upstreams['origin'])) {
$upstream = $upstreams['origin'];
} else if ($upstreams) {
$upstream = head($upstreams);
} else {
$upstream = null;
}
if (!$upstream) {
continue;
}
$lib_upstreams[$lib] = $upstream;
$merge_base_command = csprintf(
'git merge-base HEAD %s/master --',
$upstream);
$root = dirname(phutil_get_library_root($lib));
$upstream_futures[$lib] = id(new ExecFuture('%C', $merge_base_command))
->setCWD($root);
}
if ($upstream_futures) {
id(new FutureIterator($upstream_futures))
->resolveAll();
}
$results = array();
foreach ($log_futures as $lib => $future) {
list($err, $stdout) = $future->resolve();
if (!$err) {
list($hash, $epoch) = explode(' ', $stdout);
} else {
$hash = null;
$epoch = null;
}
$result = array(
'hash' => $hash,
'epoch' => $epoch,
'upstream' => null,
'branchpoint' => null,
);
$upstream_future = idx($upstream_futures, $lib);
if ($upstream_future) {
list($err, $stdout) = $upstream_future->resolve();
if (!$err) {
$branchpoint = trim($stdout);
if (strlen($branchpoint)) {
// We only list a branchpoint if it differs from HEAD.
if ($branchpoint != $hash) {
$result['upstream'] = $lib_upstreams[$lib];
$result['branchpoint'] = trim($stdout);
}
}
}
}
$results[$lib] = $result;
}
return $results;
}
private function newBinaryVersionTable() {
$rows = array();
$rows[] = array(
'php',
phpversion(),
php_sapi_name(),
);
$binaries = PhutilBinaryAnalyzer::getAllBinaries();
foreach ($binaries as $binary) {
if (!$binary->isBinaryAvailable()) {
$binary_version = pht('Not Available');
$binary_path = null;
} else {
$binary_version = $binary->getBinaryVersion();
$binary_path = $binary->getBinaryPath();
}
$rows[] = array(
$binary->getBinaryName(),
$binary_version,
$binary_path,
);
}
$table_view = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Binary'),
pht('Version'),
pht('Path'),
))
->setColumnClasses(
array(
'pri',
null,
'wide',
));
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Other Version Information'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($table_view);
}
}
diff --git a/src/applications/config/controller/services/PhabricatorConfigClusterDatabasesController.php b/src/applications/config/controller/services/PhabricatorConfigClusterDatabasesController.php
index ca22698212..096495cc84 100644
--- a/src/applications/config/controller/services/PhabricatorConfigClusterDatabasesController.php
+++ b/src/applications/config/controller/services/PhabricatorConfigClusterDatabasesController.php
@@ -1,239 +1,239 @@
<?php
final class PhabricatorConfigClusterDatabasesController
extends PhabricatorConfigServicesController {
public function handleRequest(AphrontRequest $request) {
$nav = $this->newNavigation('database-servers');
$title = pht('Database Servers');
$doc_href = PhabricatorEnv::getDoclink('Cluster: Databases');
$button = id(new PHUIButtonView())
->setIcon('fa-book')
->setHref($doc_href)
->setTag('a')
->setText(pht('Documentation'));
$header = $this->buildHeaderView($title, $button);
$database_status = $this->buildClusterDatabaseStatus();
$status = $this->buildConfigBoxView(pht('Status'), $database_status);
$crumbs = $this->newCrumbs()
->addTextCrumb($title);
$content = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($status);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content);
}
private function buildClusterDatabaseStatus() {
$viewer = $this->getViewer();
$databases = PhabricatorDatabaseRef::queryAll();
$connection_map = PhabricatorDatabaseRef::getConnectionStatusMap();
$replica_map = PhabricatorDatabaseRef::getReplicaStatusMap();
Javelin::initBehavior('phabricator-tooltips');
$rows = array();
foreach ($databases as $database) {
$messages = array();
if ($database->getIsMaster()) {
$role_icon = id(new PHUIIconView())
->setIcon('fa-database sky')
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => pht('Master'),
));
} else {
$role_icon = id(new PHUIIconView())
->setIcon('fa-download')
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => pht('Replica'),
));
}
if ($database->getDisabled()) {
$conn_icon = 'fa-times';
$conn_color = 'grey';
$conn_label = pht('Disabled');
} else {
$status = $database->getConnectionStatus();
$info = idx($connection_map, $status, array());
$conn_icon = idx($info, 'icon');
$conn_color = idx($info, 'color');
$conn_label = idx($info, 'label');
if ($status === PhabricatorDatabaseRef::STATUS_OKAY) {
$latency = $database->getConnectionLatency();
$latency = (int)(1000000 * $latency);
$conn_label = pht('%s us', new PhutilNumber($latency));
}
}
$connection = array(
id(new PHUIIconView())->setIcon("{$conn_icon} {$conn_color}"),
' ',
$conn_label,
);
if ($database->getDisabled()) {
$replica_icon = 'fa-times';
$replica_color = 'grey';
$replica_label = pht('Disabled');
} else {
$status = $database->getReplicaStatus();
$info = idx($replica_map, $status, array());
$replica_icon = idx($info, 'icon');
$replica_color = idx($info, 'color');
$replica_label = idx($info, 'label');
if ($database->getIsMaster()) {
if ($status === PhabricatorDatabaseRef::REPLICATION_OKAY) {
$replica_icon = 'fa-database';
}
} else {
switch ($status) {
case PhabricatorDatabaseRef::REPLICATION_OKAY:
case PhabricatorDatabaseRef::REPLICATION_SLOW:
$delay = $database->getReplicaDelay();
if ($delay) {
$replica_label = pht('%ss Behind', new PhutilNumber($delay));
} else {
$replica_label = pht('Up to Date');
}
break;
}
}
}
$replication = array(
id(new PHUIIconView())->setIcon("{$replica_icon} {$replica_color}"),
' ',
$replica_label,
);
$health = $database->getHealthRecord();
$health_up = $health->getUpEventCount();
$health_down = $health->getDownEventCount();
if ($health->getIsHealthy()) {
$health_icon = id(new PHUIIconView())
->setIcon('fa-plus green');
} else {
$health_icon = id(new PHUIIconView())
->setIcon('fa-times red');
$messages[] = pht(
'UNHEALTHY: This database has failed recent health checks. Traffic '.
'will not be sent to it until it recovers.');
}
$health_count = pht(
'%s / %s',
new PhutilNumber($health_up),
new PhutilNumber($health_up + $health_down));
$health_status = array(
$health_icon,
' ',
$health_count,
);
$conn_message = $database->getConnectionMessage();
if ($conn_message) {
$messages[] = $conn_message;
}
$replica_message = $database->getReplicaMessage();
if ($replica_message) {
$messages[] = $replica_message;
}
$messages = phutil_implode_html(phutil_tag('br'), $messages);
$partition = null;
if ($database->getIsMaster()) {
if ($database->getIsDefaultPartition()) {
$partition = id(new PHUIIconView())
->setIcon('fa-circle sky')
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => pht('Default Partition'),
));
} else {
$map = $database->getApplicationMap();
if ($map) {
$list = implode(', ', $map);
} else {
$list = pht('Empty');
}
$partition = id(new PHUIIconView())
->setIcon('fa-adjust sky')
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => pht('Partition: %s', $list),
));
}
}
$rows[] = array(
$role_icon,
$partition,
$database->getHost(),
$database->getPort(),
$database->getUser(),
$connection,
$replication,
$health_status,
$messages,
);
}
$table = id(new AphrontTableView($rows))
->setNoDataString(
- pht('Phabricator is not configured in cluster mode.'))
+ pht('This server is not configured in cluster mode.'))
->setHeaders(
array(
null,
null,
pht('Host'),
pht('Port'),
pht('User'),
pht('Connection'),
pht('Replication'),
pht('Health'),
pht('Messages'),
))
->setColumnClasses(
array(
null,
null,
null,
null,
null,
null,
null,
null,
'wide',
));
return $table;
}
}
diff --git a/src/applications/config/custom/PhabricatorCustomLogoConfigType.php b/src/applications/config/custom/PhabricatorCustomLogoConfigType.php
index ff29050602..cc20768119 100644
--- a/src/applications/config/custom/PhabricatorCustomLogoConfigType.php
+++ b/src/applications/config/custom/PhabricatorCustomLogoConfigType.php
@@ -1,118 +1,118 @@
<?php
final class PhabricatorCustomLogoConfigType
extends PhabricatorConfigOptionType {
public static function getLogoImagePHID() {
$logo = PhabricatorEnv::getEnvConfig('ui.logo');
return idx($logo, 'logoImagePHID');
}
public static function getLogoWordmark() {
$logo = PhabricatorEnv::getEnvConfig('ui.logo');
return idx($logo, 'wordmarkText');
}
public function validateOption(PhabricatorConfigOption $option, $value) {
if (!is_array($value)) {
throw new Exception(
pht(
'Logo configuration is not valid: value must be a dictionary.'));
}
PhutilTypeSpec::checkMap(
$value,
array(
'logoImagePHID' => 'optional string|null',
'wordmarkText' => 'optional string|null',
));
}
public function readRequest(
PhabricatorConfigOption $option,
AphrontRequest $request) {
$viewer = $request->getViewer();
$view_policy = PhabricatorPolicies::POLICY_PUBLIC;
if ($request->getBool('removeLogo')) {
$logo_image_phid = null;
} else if ($request->getFileExists('logoImage')) {
$logo_image = PhabricatorFile::newFromPHPUpload(
idx($_FILES, 'logoImage'),
array(
'name' => 'logo',
'authorPHID' => $viewer->getPHID(),
'viewPolicy' => $view_policy,
'canCDN' => true,
'isExplicitUpload' => true,
));
$logo_image_phid = $logo_image->getPHID();
} else {
$logo_image_phid = self::getLogoImagePHID();
}
$wordmark_text = $request->getStr('wordmarkText');
$value = array(
'logoImagePHID' => $logo_image_phid,
'wordmarkText' => $wordmark_text,
);
$errors = array();
$e_value = null;
try {
$this->validateOption($option, $value);
} catch (Exception $ex) {
$e_value = pht('Invalid');
$errors[] = $ex->getMessage();
$value = array();
}
return array($e_value, $errors, $value, phutil_json_encode($value));
}
public function renderControls(
PhabricatorConfigOption $option,
$display_value,
$e_value) {
try {
$value = phutil_json_decode($display_value);
} catch (Exception $ex) {
$value = array();
}
$logo_image_phid = idx($value, 'logoImagePHID');
$wordmark_text = idx($value, 'wordmarkText');
$controls = array();
// TODO: This should be a PHUIFormFileControl, but that currently only
// works in "workflow" forms. It isn't trivial to convert this form into
// a workflow form, nor is it trivial to make the newer control work
// in non-workflow forms.
$controls[] = id(new AphrontFormFileControl())
->setName('logoImage')
->setLabel(pht('Logo Image'));
if ($logo_image_phid) {
$controls[] = id(new AphrontFormCheckboxControl())
->addCheckbox(
'removeLogo',
1,
pht('Remove Custom Logo'));
}
$controls[] = id(new AphrontFormTextControl())
->setName('wordmarkText')
->setLabel(pht('Wordmark'))
- ->setPlaceholder(pht('Phabricator'))
+ ->setPlaceholder(PlatformSymbols::getPlatformServerName())
->setValue($wordmark_text);
return $controls;
}
}
diff --git a/src/applications/config/editor/PhabricatorConfigEditor.php b/src/applications/config/editor/PhabricatorConfigEditor.php
index deccf1ef5c..7a8833c84e 100644
--- a/src/applications/config/editor/PhabricatorConfigEditor.php
+++ b/src/applications/config/editor/PhabricatorConfigEditor.php
@@ -1,165 +1,165 @@
<?php
final class PhabricatorConfigEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorConfigApplication';
}
public function getEditorObjectsDescription() {
- return pht('Phabricator Configuration');
+ return pht('Configuration');
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorConfigTransaction::TYPE_EDIT;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorConfigTransaction::TYPE_EDIT:
return array(
'deleted' => (int)$object->getIsDeleted(),
'value' => $object->getValue(),
);
}
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorConfigTransaction::TYPE_EDIT:
return $xaction->getNewValue();
}
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorConfigTransaction::TYPE_EDIT:
$v = $xaction->getNewValue();
// If this is a defined configuration option (vs a straggler from an
// old version of Phabricator or a configuration file misspelling)
// submit it to the validation gauntlet.
$key = $object->getConfigKey();
$all_options = PhabricatorApplicationConfigOptions::loadAllOptions();
$option = idx($all_options, $key);
if ($option) {
$option->getGroup()->validateOption(
$option,
$v['value']);
}
$object->setIsDeleted((int)$v['deleted']);
$object->setValue($v['value']);
break;
}
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
return;
}
protected function mergeTransactions(
PhabricatorApplicationTransaction $u,
PhabricatorApplicationTransaction $v) {
$type = $u->getTransactionType();
switch ($type) {
case PhabricatorConfigTransaction::TYPE_EDIT:
return $v;
}
return parent::mergeTransactions($u, $v);
}
protected function transactionHasEffect(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
$type = $xaction->getTransactionType();
switch ($type) {
case PhabricatorConfigTransaction::TYPE_EDIT:
// If an edit deletes an already-deleted entry, no-op it.
if (idx($old, 'deleted') && idx($new, 'deleted')) {
return false;
}
break;
}
return parent::transactionHasEffect($object, $xaction);
}
protected function didApplyTransactions($object, array $xactions) {
// Force all the setup checks to run on the next page load.
PhabricatorSetupCheck::deleteSetupCheckCache();
return $xactions;
}
public static function storeNewValue(
PhabricatorUser $user,
PhabricatorConfigEntry $config_entry,
$value,
PhabricatorContentSource $source,
$acting_as_phid = null) {
$xaction = id(new PhabricatorConfigTransaction())
->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT)
->setNewValue(
array(
'deleted' => false,
'value' => $value,
));
$editor = id(new PhabricatorConfigEditor())
->setActor($user)
->setContinueOnNoEffect(true)
->setContentSource($source);
if ($acting_as_phid) {
$editor->setActingAsPHID($acting_as_phid);
}
$editor->applyTransactions($config_entry, array($xaction));
}
public static function deleteConfig(
PhabricatorUser $user,
PhabricatorConfigEntry $config_entry,
PhabricatorContentSource $source) {
$xaction = id(new PhabricatorConfigTransaction())
->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT)
->setNewValue(
array(
'deleted' => true,
'value' => null,
));
$editor = id(new PhabricatorConfigEditor())
->setActor($user)
->setContinueOnNoEffect(true)
->setContentSource($source);
$editor->applyTransactions($config_entry, array($xaction));
}
}
diff --git a/src/applications/config/option/PhabricatorClusterConfigOptions.php b/src/applications/config/option/PhabricatorClusterConfigOptions.php
index ca4153a8b2..3cbdf6706f 100644
--- a/src/applications/config/option/PhabricatorClusterConfigOptions.php
+++ b/src/applications/config/option/PhabricatorClusterConfigOptions.php
@@ -1,146 +1,146 @@
<?php
final class PhabricatorClusterConfigOptions
extends PhabricatorApplicationConfigOptions {
public function getName() {
return pht('Cluster Setup');
}
public function getDescription() {
- return pht('Configure Phabricator to run on a cluster of hosts.');
+ return pht('Configure services to run on a cluster of hosts.');
}
public function getIcon() {
return 'fa-sitemap';
}
public function getGroup() {
return 'core';
}
public function getOptions() {
$databases_type = 'cluster.databases';
$databases_help = $this->deformat(pht(<<<EOTEXT
WARNING: This is a prototype option and the description below is currently pure
fantasy.
-This option allows you to make Phabricator aware of database read replicas so
+This option allows you to make this service aware of database read replicas so
it can monitor database health, spread load, and degrade gracefully to
read-only mode in the event of a failure on the primary host. For help with
configuring cluster databases, see **[[ %s | %s ]]** in the documentation.
EOTEXT
,
PhabricatorEnv::getDoclink('Cluster: Databases'),
pht('Cluster: Databases')));
$intro_href = PhabricatorEnv::getDoclink('Clustering Introduction');
$intro_name = pht('Clustering Introduction');
$search_type = 'cluster.search';
$search_help = $this->deformat(pht(<<<EOTEXT
Define one or more fulltext storage services. Here you can configure which
hosts will handle fulltext search queries and indexing. For help with
configuring fulltext search clusters, see **[[ %s | %s ]]** in the
documentation.
EOTEXT
,
PhabricatorEnv::getDoclink('Cluster: Search'),
pht('Cluster: Search')));
return array(
$this->newOption('cluster.addresses', 'list<string>', array())
->setLocked(true)
->setSummary(pht('Address ranges of cluster hosts.'))
->setDescription(
pht(
- 'Define a Phabricator cluster by providing a whitelist of host '.
+ 'Define a cluster by providing a whitelist of host '.
'addresses that are part of the cluster.'.
"\n\n".
'Hosts on this whitelist have special powers. These hosts are '.
'permitted to bend security rules, and misconfiguring this list '.
'can make your install less secure. For more information, '.
'see **[[ %s | %s ]]**.'.
"\n\n".
'Define a list of CIDR blocks which whitelist all hosts in the '.
'cluster and no additional hosts. See the examples below for '.
'details.'.
"\n\n".
- 'When cluster addresses are defined, Phabricator hosts will also '.
+ 'When cluster addresses are defined, hosts will also '.
'reject requests to interfaces which are not whitelisted.',
$intro_href,
$intro_name))
->addExample(
array(
'23.24.25.80/32',
'23.24.25.81/32',
),
pht('Whitelist Specific Addresses'))
->addExample(
array(
'1.2.3.0/24',
),
pht('Whitelist 1.2.3.*'))
->addExample(
array(
'1.2.0.0/16',
),
pht('Whitelist 1.2.*.*'))
->addExample(
array(
'0.0.0.0/0',
),
pht('Allow Any Host (Insecure!)')),
$this->newOption('cluster.instance', 'string', null)
->setLocked(true)
->setSummary(pht('Instance identifier for multi-tenant clusters.'))
->setDescription(
pht(
'WARNING: This is a very advanced option, and only useful for '.
'hosting providers running multi-tenant clusters.'.
"\n\n".
'If you provide an instance identifier here (normally by '.
- 'injecting it with a `%s`), Phabricator will pass it to '.
+ 'injecting it with a `%s`), the server will pass it to '.
'subprocesses and commit hooks in the `%s` environmental variable.',
'PhabricatorConfigSiteSource',
'PHABRICATOR_INSTANCE')),
$this->newOption('cluster.read-only', 'bool', false)
->setLocked(true)
->setSummary(
pht(
'Activate read-only mode for maintenance or disaster recovery.'))
->setDescription(
pht(
'WARNING: This is a prototype option and the description below '.
'is currently pure fantasy.'.
"\n\n".
- 'Switch Phabricator to read-only mode. In this mode, users will '.
+ 'Switch the service to read-only mode. In this mode, users will '.
'be unable to write new data. Normally, the cluster degrades '.
'into this mode automatically when it detects that the database '.
'master is unreachable, but you can activate it manually in '.
'order to perform maintenance or test configuration.')),
$this->newOption('cluster.databases', $databases_type, array())
->setHidden(true)
->setSummary(
pht('Configure database read replicas.'))
->setDescription($databases_help),
$this->newOption('cluster.search', $search_type, array())
->setLocked(true)
->setSummary(
pht('Configure full-text search services.'))
->setDescription($search_help)
->setDefault(
array(
array(
'type' => 'mysql',
'roles' => array(
'read' => true,
'write' => true,
),
),
)),
);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Sep 7, 9:49 AM (1 d, 21 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
222975
Default Alt Text
(114 KB)

Event Timeline