Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php b/src/applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php
index d21e2105fb..fafb4d35af 100644
--- a/src/applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php
+++ b/src/applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php
@@ -1,326 +1,326 @@
<?php
/**
* Renders the "HTTP Parameters" help page for edit engines.
*
* This page has a ton of text and specialized rendering on it, this class
* just pulls it out of the main @{class:PhabricatorEditEngine}.
*/
final class PhabricatorApplicationEditHTTPParameterHelpView
extends AphrontView {
private $object;
private $fields;
public function setObject($object) {
$this->object = $object;
return $this;
}
public function getObject() {
return $this->object;
}
public function setFields(array $fields) {
$this->fields = $fields;
return $this;
}
public function getFields() {
return $this->fields;
}
public function render() {
$object = $this->getObject();
$fields = $this->getFields();
$uri = 'https://your.install.com/application/edit/';
// Remove fields which do not expose an HTTP parameter type.
$types = array();
foreach ($fields as $key => $field) {
if (!$field->shouldGenerateTransactionsFromSubmit()) {
unset($fields[$key]);
continue;
}
$type = $field->getHTTPParameterType();
if ($type === null) {
unset($fields[$key]);
continue;
}
$types[$type->getTypeName()] = $type;
}
$intro = pht(<<<EOTEXT
When creating objects in the web interface, you can use HTTP parameters to
prefill fields in the form. This allows you to quickly create a link to a
form with some of the fields already filled in with default values.
To prefill a form, start by finding the URI for the form you want to prefill.
Do this by navigating to the relevant application, clicking the "Create" button
for the type of object you want to create, and then copying the URI out of your
browser's address bar. It will usually look something like this:
```
%s
```
-However, `your.install.com` will be the domain where your copy of Phabricator
+However, `your.install.com` will be the domain where your copy of this software
is installed, and `application/` will be the URI for an application. Some
applications have multiple forms for creating objects or URIs that look a little
different than this example, so the URI may not look exactly like this.
To prefill the form, add properly encoded HTTP parameters to the URI. You
should end up with something like this:
```
%s?title=Platyplus&body=Ornithopter
```
If the form has `title` and `body` fields of the correct types, visiting this
link will prefill those fields with the values "Platypus" and "Ornithopter"
respectively.
The rest of this document shows which parameters you can add to this form and
how to format them.
Supported Fields
----------------
This form supports these fields:
EOTEXT
,
$uri,
$uri);
$rows = array();
foreach ($fields as $field) {
$rows[] = array(
$field->getLabel(),
head($field->getAllReadValueFromRequestKeys()),
$field->getHTTPParameterType()->getTypeName(),
$field->getDescription(),
);
}
$main_table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Label'),
pht('Key'),
pht('Type'),
pht('Description'),
))
->setColumnClasses(
array(
'pri',
null,
null,
'wide',
));
$aliases_text = pht(<<<EOTEXT
Aliases
-------
Aliases are alternate recognized keys for a field. For example, a field with
a complex key like `examplePHIDs` might be have a simple version of that key
as an alias, like `example`.
Aliases work just like the primary key when prefilling forms. They make it
easier to remember and use HTTP parameters by providing more natural ways to do
some prefilling.
For example, if a field has `examplePHIDs` as a key but has aliases `example`
and `examples`, these three URIs will all do the same thing:
```
%s?examplePHIDs=...
%s?examples=...
%s?example=...
```
If a URI specifies multiple default values for a field, the value using the
primary key has precedence. Generally, you can not mix different aliases in
a single URI.
EOTEXT
,
$uri,
$uri,
$uri);
$rows = array();
foreach ($fields as $field) {
$aliases = array_slice($field->getAllReadValueFromRequestKeys(), 1);
if (!$aliases) {
continue;
}
$rows[] = array(
$field->getLabel(),
$field->getKey(),
implode(', ', $aliases),
);
}
$alias_table = id(new AphrontTableView($rows))
->setNoDataString(pht('This object has no fields with aliases.'))
->setHeaders(
array(
pht('Label'),
pht('Key'),
pht('Aliases'),
))
->setColumnClasses(
array(
'pri',
null,
'wide',
));
$template_text = pht(<<<EOTEXT
Template Objects
----------------
Instead of specifying each field value individually, you can specify another
object to use as a template. Some of the initial fields will be copied from the
template object.
Specify a template object with the `template` parameter. You can use an ID,
PHID, or monogram (for objects which have monograms). For example, you might
use URIs like these:
```
%s?template=123
%s?template=PHID-WXYZ-abcdef...
%s?template=T123
```
You can combine the `template` parameter with HTTP parameters: the template
object will be copied first, then any HTTP parameters will be read.
When using `template`, these fields will be copied:
EOTEXT
,
$uri,
$uri,
$uri);
$yes = id(new PHUIIconView())->setIcon('fa-check-circle green');
$no = id(new PHUIIconView())->setIcon('fa-times grey');
$rows = array();
foreach ($fields as $field) {
$rows[] = array(
$field->getLabel(),
$field->getIsCopyable() ? $yes : $no,
);
}
$template_table = id(new AphrontTableView($rows))
->setNoDataString(
pht('None of the fields on this object support templating.'))
->setHeaders(
array(
pht('Field'),
pht('Will Copy'),
))
->setColumnClasses(
array(
'pri',
'wide',
));
$select_text = pht(<<<EOTEXT
Select Fields
-------------
Some fields support selection from a specific set of values. When prefilling
these fields, use the value in the **Value** column to select the appropriate
setting.
EOTEXT
);
$rows = array();
foreach ($fields as $field) {
if (!($field instanceof PhabricatorSelectEditField)) {
continue;
}
$options = $field->getOptions();
$label = $field->getLabel();
foreach ($options as $option_key => $option_value) {
if (strlen($option_key)) {
$option_display = $option_key;
} else {
$option_display = phutil_tag('em', array(), pht('<empty>'));
}
$rows[] = array(
$label,
$option_display,
$option_value,
);
$label = null;
}
}
$select_table = id(new AphrontTableView($rows))
->setNoDataString(pht('This object has no select fields.'))
->setHeaders(
array(
pht('Field'),
pht('Value'),
pht('Label'),
))
->setColumnClasses(
array(
'pri',
null,
'wide',
));
$types_text = pht(<<<EOTEXT
Field Types
-----------
Fields in this form have the types described in the table below. This table
shows how to format values for each field type.
EOTEXT
);
$types_table = id(new PhabricatorHTTPParameterTypeTableView())
->setHTTPParameterTypes($types);
return array(
$this->renderInstructions($intro),
$main_table,
$this->renderInstructions($aliases_text),
$alias_table,
$this->renderInstructions($template_text),
$template_table,
$this->renderInstructions($select_text),
$select_table,
$this->renderInstructions($types_text),
$types_table,
);
}
protected function renderInstructions($corpus) {
$viewer = $this->getUser();
$view = new PHUIRemarkupView($viewer, $corpus);
$view->setRemarkupOptions(
array(
PHUIRemarkupView::OPTION_PRESERVE_LINEBREAKS => false,
));
return $view;
}
}
diff --git a/src/applications/uiexample/examples/PHUIBadgeExample.php b/src/applications/uiexample/examples/PHUIBadgeExample.php
index c001a3c751..40f549e305 100644
--- a/src/applications/uiexample/examples/PHUIBadgeExample.php
+++ b/src/applications/uiexample/examples/PHUIBadgeExample.php
@@ -1,173 +1,173 @@
<?php
final class PHUIBadgeExample extends PhabricatorUIExample {
public function getName() {
return pht('Badge');
}
public function getDescription() {
return pht('Celebrate the moments of your life.');
}
public function getCategory() {
return pht('Single Use');
}
public function renderExample() {
$badges1 = array();
$badges1[] = id(new PHUIBadgeView())
->setIcon('fa-users')
->setHeader(pht('High Command'))
->setHref('/')
->setSource('Projects (automatic)')
->addByline(pht('Dec 31, 1969'))
->addByline('3 Members');
$badges1[] = id(new PHUIBadgeView())
->setIcon('fa-lock')
->setHeader(pht('Blessed Committers'))
->setSource('Projects (automatic)')
->addByline(pht('Dec 31, 1969'))
->addByline('12 Members');
$badges1[] = id(new PHUIBadgeView())
->setIcon('fa-camera-retro')
->setHeader(pht('Design'))
->setSource('Projects (automatic)')
->addByline(pht('Dec 31, 1969'))
->addByline('2 Members');
$badges1[] = id(new PHUIBadgeView())
->setIcon('fa-lock')
->setHeader(pht('Blessed Reviewers'))
->setSource('Projects (automatic)')
->addByline(pht('Dec 31, 1969'))
->addByline('3 Members');
$badges1[] = id(new PHUIBadgeView())
->setIcon('fa-umbrella')
->setHeader(pht('Wikipedia'))
->setSource('Projects (automatic)')
->addByline(pht('Dec 31, 1969'))
->addByline('22 Members');
$badges2 = array();
$badges2[] = id(new PHUIBadgeView())
->setIcon('fa-user')
- ->setHeader(pht('Phabricator User'))
+ ->setHeader(pht('User'))
->setSubhead(pht('Confirmed your account.'))
->setQuality(PhabricatorBadgesQuality::POOR)
->setSource(pht('People (automatic)'))
->addByline(pht('Dec 31, 1969'))
->addByline('212 Issued (100%)');
$badges2[] = id(new PHUIBadgeView())
->setIcon('fa-code')
->setHeader(pht('Code Contributor'))
->setSubhead(pht('Wrote code that was acceptable'))
->setQuality(PhabricatorBadgesQuality::COMMON)
->setSource('Diffusion (automatic)')
->addByline(pht('Dec 31, 1969'))
->addByline('200 Awarded (98%)');
$badges2[] = id(new PHUIBadgeView())
->setIcon('fa-bug')
->setHeader(pht('Task Master'))
->setSubhead(pht('Closed over 100 tasks'))
->setQuality(PhabricatorBadgesQuality::UNCOMMON)
->setSource('Maniphest (automatic)')
->addByline(pht('Dec 31, 1969'))
->addByline('56 Awarded (43%)');
$badges2[] = id(new PHUIBadgeView())
->setIcon('fa-star')
->setHeader(pht('Code Weaver'))
->setSubhead(pht('Landed 1,000 Commits'))
->setQuality(PhabricatorBadgesQuality::RARE)
->setSource('Diffusion (automatic)')
->addByline(pht('Dec 31, 1969'))
->addByline('42 Awarded (20%)');
$badges2[] = id(new PHUIBadgeView())
->setIcon('fa-users')
->setHeader(pht('Security Team'))
->setSubhead(pht('<script>alert(1);</script>'))
->setQuality(PhabricatorBadgesQuality::EPIC)
->setSource('Projects (automatic)')
->addByline(pht('Dec 31, 1969'))
->addByline('21 Awarded (10%)');
$badges2[] = id(new PHUIBadgeView())
->setIcon('fa-user')
->setHeader(pht('Administrator'))
->setSubhead(pht('Drew the short stick'))
->setQuality(PhabricatorBadgesQuality::LEGENDARY)
->setSource(pht('People (automatic)'))
->addByline(pht('Dec 31, 1969'))
->addByline('3 Awarded (1.4%)');
$badges2[] = id(new PHUIBadgeView())
->setIcon('fa-compass')
->setHeader(pht('Lead Developer'))
- ->setSubhead(pht('Lead Developer of Phabricator'))
+ ->setSubhead(pht('Lead Developer of Software'))
->setQuality(PhabricatorBadgesQuality::HEIRLOOM)
->setSource(pht('Direct Award'))
->addByline(pht('Dec 31, 1969'))
->addByline('1 Awarded (0.4%)');
$badges3 = array();
$badges3[] = id(new PHUIBadgeMiniView())
->setIcon('fa-book')
->setHeader(pht('Documenter'));
$badges3[] = id(new PHUIBadgeMiniView())
->setIcon('fa-star')
->setHeader(pht('Contributor'));
$badges3[] = id(new PHUIBadgeMiniView())
->setIcon('fa-bug')
->setHeader(pht('Bugmeister'));
$badges3[] = id(new PHUIBadgeMiniView())
->setIcon('fa-heart')
->setHeader(pht('Funder'))
->setQuality(PhabricatorBadgesQuality::UNCOMMON);
$badges3[] = id(new PHUIBadgeMiniView())
->setIcon('fa-user')
->setHeader(pht('Administrator'))
->setQuality(PhabricatorBadgesQuality::RARE);
$badges3[] = id(new PHUIBadgeMiniView())
->setIcon('fa-camera-retro')
->setHeader(pht('Designer'))
->setQuality(PhabricatorBadgesQuality::EPIC);
$flex1 = new PHUIBadgeBoxView();
$flex1->addItems($badges1);
$box1 = id(new PHUIObjectBoxView())
->setHeaderText(pht('Project Membership'))
->appendChild($flex1);
$flex2 = new PHUIBadgeBoxView();
$flex2->addItems($badges2);
$box2 = id(new PHUIObjectBoxView())
->setHeaderText(pht('Achievements'))
->appendChild($flex2);
$flex3 = new PHUIBadgeBoxView();
$flex3->addItems($badges3);
$flex3->setCollapsed(true);
$flex3->addClass('ml');
$box3 = id(new PHUIObjectBoxView())
->setHeaderText(pht('PHUIBadgeMiniView'))
->appendChild($flex3);
return array($box1, $box2, $box3);
}
}
diff --git a/src/applications/uiexample/examples/PHUIHovercardUIExample.php b/src/applications/uiexample/examples/PHUIHovercardUIExample.php
index b88fdc6639..2e68976ae0 100644
--- a/src/applications/uiexample/examples/PHUIHovercardUIExample.php
+++ b/src/applications/uiexample/examples/PHUIHovercardUIExample.php
@@ -1,83 +1,83 @@
<?php
final class PHUIHovercardUIExample extends PhabricatorUIExample {
public function getName() {
return pht('Hovercard');
}
public function getDescription() {
return pht(
'Use %s to render hovercards.',
phutil_tag('tt', array(), 'PHUIHovercardView'));
}
public function getCategory() {
return pht('Single Use');
}
public function renderExample() {
$request = $this->getRequest();
$user = $request->getUser();
$elements = array();
$diff_handle = $this->createBasicDummyHandle(
'D123',
DifferentialRevisionPHIDType::TYPECONST,
pht('Introduce cooler Differential Revisions'));
$panel = $this->createPanel(pht('Differential Hovercard'));
$panel->appendChild(id(new PHUIHovercardView())
->setObjectHandle($diff_handle)
->addField(pht('Author'), $user->getUsername())
->addField(pht('Updated'), phabricator_datetime(time(), $user))
->addAction(pht('Subscribe'), '/dev/random')
->setUser($user));
$elements[] = $panel;
$task_handle = $this->createBasicDummyHandle(
'T123',
ManiphestTaskPHIDType::TYPECONST,
- pht('Improve Mobile Experience for Phabricator'));
+ pht('Improve Mobile Experience'));
$tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_STATE)
->setName(pht('Closed, Resolved'));
$panel = $this->createPanel(pht('Maniphest Hovercard'));
$panel->appendChild(id(new PHUIHovercardView())
->setObjectHandle($task_handle)
->setUser($user)
->addField(pht('Assigned to'), $user->getUsername())
->addField(pht('Dependent Tasks'), 'T123, T124, T125')
->addAction(pht('Subscribe'), '/dev/random')
->addAction(pht('Create Subtask'), '/dev/urandom')
->addTag($tag));
$elements[] = $panel;
$user_handle = $this->createBasicDummyHandle(
'gwashington',
PhabricatorPeopleUserPHIDType::TYPECONST,
'George Washington');
$user_handle->setImageURI(
celerity_get_resource_uri('/rsrc/image/people/washington.png'));
$panel = $this->createPanel(pht('Whatevery Hovercard'));
$panel->appendChild(id(new PHUIHovercardView())
->setObjectHandle($user_handle)
->addField(pht('Status'), pht('Available'))
->addField(pht('Member since'), '30. February 1750')
->addAction(pht('Send a Message'), '/dev/null')
->setUser($user));
$elements[] = $panel;
return phutil_implode_html('', $elements);
}
private function createPanel($header) {
$panel = new PHUIBoxView();
$panel->addClass('grouped');
$panel->addClass('ml');
return $panel;
}
}
diff --git a/src/applications/uiexample/examples/PhabricatorProjectBuiltinsExample.php b/src/applications/uiexample/examples/PhabricatorProjectBuiltinsExample.php
index 2cc89b56d5..9eb90f6de1 100644
--- a/src/applications/uiexample/examples/PhabricatorProjectBuiltinsExample.php
+++ b/src/applications/uiexample/examples/PhabricatorProjectBuiltinsExample.php
@@ -1,71 +1,71 @@
<?php
final class PhabricatorProjectBuiltinsExample extends PhabricatorUIExample {
public function getName() {
return pht('Project Builtin Images');
}
public function getDescription() {
- return pht('Builtin Project Images that ship with Phabricator.');
+ return pht('Builtin Project Images.');
}
public function getCategory() {
return pht('Catalogs');
}
public function renderExample() {
$viewer = $this->getRequest()->getUser();
$root = dirname(phutil_get_library_root('phabricator'));
$root = $root.'/resources/builtin/projects/v3/';
Javelin::initBehavior('phabricator-tooltips', array());
$map = array();
$builtin_map = id(new FileFinder($root))
->withType('f')
->withFollowSymlinks(true)
->find();
$images = array();
foreach ($builtin_map as $image) {
$file = PhabricatorFile::loadBuiltin($viewer, 'projects/v3/'.$image);
$images[$file->getPHID()] = array(
'uri' => $file->getBestURI(),
'tip' => 'v3/'.$image,
);
}
$buttons = array();
foreach ($images as $phid => $spec) {
$button = javelin_tag(
'img',
array(
'height' => 100,
'width' => 100,
'src' => $spec['uri'],
'style' => 'float: left; padding: 4px;',
'sigil' => 'has-tooltip',
'meta' => array(
'tip' => $spec['tip'],
'size' => 300,
),
));
$buttons[] = $button;
}
$wrap1 = id(new PHUIObjectBoxView())
->setHeaderText(pht('Images'))
->appendChild($buttons)
->addClass('grouped');
return phutil_tag(
'div',
array(),
array(
$wrap1,
));
}
}
diff --git a/src/infrastructure/contentsource/PhabricatorContentSource.php b/src/infrastructure/contentsource/PhabricatorContentSource.php
index 3a5ea19f57..ee77052113 100644
--- a/src/infrastructure/contentsource/PhabricatorContentSource.php
+++ b/src/infrastructure/contentsource/PhabricatorContentSource.php
@@ -1,92 +1,92 @@
<?php
abstract class PhabricatorContentSource extends Phobject {
private $source;
private $params = array();
abstract public function getSourceName();
abstract public function getSourceDescription();
final public function getSourceTypeConstant() {
return $this->getPhobjectClassConstant('SOURCECONST', 32);
}
final public static function getAllContentSources() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getSourceTypeConstant')
->execute();
}
/**
* Construct a new content source object.
*
* @param const The source type constant to build a source for.
* @param array Source parameters.
* @param bool True to suppress errors and force construction of a source
* even if the source type is not valid.
* @return PhabricatorContentSource New source object.
*/
final public static function newForSource(
$source,
array $params = array(),
$force = false) {
$map = self::getAllContentSources();
if (isset($map[$source])) {
$obj = clone $map[$source];
} else {
if ($force) {
$obj = new PhabricatorUnknownContentSource();
} else {
throw new Exception(
pht(
- 'Content source type "%s" is not known to Phabricator!',
+ 'Content source type "%s" is unknown.',
$source));
}
}
$obj->source = $source;
$obj->params = $params;
return $obj;
}
public static function newFromSerialized($serialized) {
$dict = json_decode($serialized, true);
if (!is_array($dict)) {
$dict = array();
}
$source = idx($dict, 'source');
$params = idx($dict, 'params');
if (!is_array($params)) {
$params = array();
}
return self::newForSource($source, $params, true);
}
public static function newFromRequest(AphrontRequest $request) {
return self::newForSource(
PhabricatorWebContentSource::SOURCECONST);
}
final public function serialize() {
return phutil_json_encode(
array(
'source' => $this->getSource(),
'params' => $this->params,
));
}
final public function getSource() {
return $this->source;
}
final public function getContentSourceParameter($key, $default = null) {
return idx($this->params, $key, $default);
}
}
diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php
index 4806289f52..352e9aeb5e 100644
--- a/src/infrastructure/env/PhabricatorEnv.php
+++ b/src/infrastructure/env/PhabricatorEnv.php
@@ -1,990 +1,990 @@
<?php
/**
* Manages the execution environment configuration, exposing APIs to read
* configuration settings and other similar values that are derived directly
* from configuration settings.
*
*
* = Reading Configuration =
*
* The primary role of this class is to provide an API for reading
* Phabricator configuration, @{method:getEnvConfig}:
*
* $value = PhabricatorEnv::getEnvConfig('some.key', $default);
*
* The class also handles some URI construction based on configuration, via
* the methods @{method:getURI}, @{method:getProductionURI},
* @{method:getCDNURI}, and @{method:getDoclink}.
*
* For configuration which allows you to choose a class to be responsible for
* some functionality (e.g., which mail adapter to use to deliver email),
* @{method:newObjectFromConfig} provides a simple interface that validates
* the configured value.
*
*
* = Unit Test Support =
*
* In unit tests, you can use @{method:beginScopedEnv} to create a temporary,
* mutable environment. The method returns a scope guard object which restores
* the environment when it is destroyed. For example:
*
* public function testExample() {
* $env = PhabricatorEnv::beginScopedEnv();
* $env->overrideEnv('some.key', 'new-value-for-this-test');
*
* // Some test which depends on the value of 'some.key'.
*
* }
*
* Your changes will persist until the `$env` object leaves scope or is
* destroyed.
*
* You should //not// use this in normal code.
*
*
* @task read Reading Configuration
* @task uri URI Validation
* @task test Unit Test Support
* @task internal Internals
*/
final class PhabricatorEnv extends Phobject {
private static $sourceStack;
private static $repairSource;
private static $overrideSource;
private static $requestBaseURI;
private static $cache;
private static $localeCode;
private static $readOnly;
private static $readOnlyReason;
const READONLY_CONFIG = 'config';
const READONLY_UNREACHABLE = 'unreachable';
const READONLY_SEVERED = 'severed';
const READONLY_MASTERLESS = 'masterless';
/**
* @phutil-external-symbol class PhabricatorStartup
*/
public static function initializeWebEnvironment() {
self::initializeCommonEnvironment(false);
}
public static function initializeScriptEnvironment($config_optional) {
self::initializeCommonEnvironment($config_optional);
// NOTE: This is dangerous in general, but we know we're in a script context
// and are not vulnerable to CSRF.
AphrontWriteGuard::allowDangerousUnguardedWrites(true);
// There are several places where we log information (about errors, events,
// service calls, etc.) for analysis via DarkConsole or similar. These are
// useful for web requests, but grow unboundedly in long-running scripts and
// daemons. Discard data as it arrives in these cases.
PhutilServiceProfiler::getInstance()->enableDiscardMode();
DarkConsoleErrorLogPluginAPI::enableDiscardMode();
DarkConsoleEventPluginAPI::enableDiscardMode();
}
private static function initializeCommonEnvironment($config_optional) {
PhutilErrorHandler::initialize();
self::resetUmask();
self::buildConfigurationSourceStack($config_optional);
// Force a valid timezone. If both PHP and Phabricator configuration are
// invalid, use UTC.
$tz = self::getEnvConfig('phabricator.timezone');
if ($tz) {
@date_default_timezone_set($tz);
}
$ok = @date_default_timezone_set(date_default_timezone_get());
if (!$ok) {
date_default_timezone_set('UTC');
}
// Prepend '/support/bin' and append any paths to $PATH if we need to.
$env_path = getenv('PATH');
$phabricator_path = dirname(phutil_get_library_root('phabricator'));
$support_path = $phabricator_path.'/support/bin';
$env_path = $support_path.PATH_SEPARATOR.$env_path;
$append_dirs = self::getEnvConfig('environment.append-paths');
if (!empty($append_dirs)) {
$append_path = implode(PATH_SEPARATOR, $append_dirs);
$env_path = $env_path.PATH_SEPARATOR.$append_path;
}
putenv('PATH='.$env_path);
// Write this back into $_ENV, too, so ExecFuture picks it up when creating
// subprocess environments.
$_ENV['PATH'] = $env_path;
// If an instance identifier is defined, write it into the environment so
// it's available to subprocesses.
$instance = self::getEnvConfig('cluster.instance');
if (strlen($instance)) {
putenv('PHABRICATOR_INSTANCE='.$instance);
$_ENV['PHABRICATOR_INSTANCE'] = $instance;
}
PhabricatorEventEngine::initialize();
// TODO: Add a "locale.default" config option once we have some reasonable
// defaults which aren't silly nonsense.
self::setLocaleCode('en_US');
// Load the preamble utility library if we haven't already. On web
// requests this loaded earlier, but we want to load it for non-web
// requests so that unit tests can call these functions.
require_once $phabricator_path.'/support/startup/preamble-utils.php';
}
public static function beginScopedLocale($locale_code) {
return new PhabricatorLocaleScopeGuard($locale_code);
}
public static function getLocaleCode() {
return self::$localeCode;
}
public static function setLocaleCode($locale_code) {
if (!$locale_code) {
return;
}
if ($locale_code == self::$localeCode) {
return;
}
try {
$locale = PhutilLocale::loadLocale($locale_code);
$translations = PhutilTranslation::getTranslationMapForLocale(
$locale_code);
$override = self::getEnvConfig('translation.override');
if (!is_array($override)) {
$override = array();
}
PhutilTranslator::getInstance()
->setLocale($locale)
->setTranslations($override + $translations);
self::$localeCode = $locale_code;
} catch (Exception $ex) {
// Just ignore this; the user likely has an out-of-date locale code.
}
}
private static function buildConfigurationSourceStack($config_optional) {
self::dropConfigCache();
$stack = new PhabricatorConfigStackSource();
self::$sourceStack = $stack;
$default_source = id(new PhabricatorConfigDefaultSource())
->setName(pht('Global Default'));
$stack->pushSource($default_source);
$env = self::getSelectedEnvironmentName();
if ($env) {
$stack->pushSource(
id(new PhabricatorConfigFileSource($env))
->setName(pht("File '%s'", $env)));
}
$stack->pushSource(
id(new PhabricatorConfigLocalSource())
->setName(pht('Local Config')));
// If the install overrides the database adapter, we might need to load
// the database adapter class before we can push on the database config.
// This config is locked and can't be edited from the web UI anyway.
foreach (self::getEnvConfig('load-libraries') as $library) {
phutil_load_library($library);
}
// Drop any class map caches, since they will have generated without
// any classes from libraries. Without this, preflight setup checks can
// cause generation of a setup check cache that omits checks defined in
// libraries, for example.
PhutilClassMapQuery::deleteCaches();
// If custom libraries specify config options, they won't get default
// values as the Default source has already been loaded, so we get it to
// pull in all options from non-phabricator libraries now they are loaded.
$default_source->loadExternalOptions();
// If this install has site config sources, load them now.
$site_sources = id(new PhutilClassMapQuery())
->setAncestorClass('PhabricatorConfigSiteSource')
->setSortMethod('getPriority')
->execute();
foreach ($site_sources as $site_source) {
$stack->pushSource($site_source);
// If the site source did anything which reads config, throw it away
// to make sure any additional site sources get clean reads.
self::dropConfigCache();
}
$masters = PhabricatorDatabaseRef::getMasterDatabaseRefs();
if (!$masters) {
self::setReadOnly(true, self::READONLY_MASTERLESS);
} else {
// If any master is severed, we drop to readonly mode. In theory we
// could try to continue if we're only missing some applications, but
// this is very complex and we're unlikely to get it right.
foreach ($masters as $master) {
// Give severed masters one last chance to get healthy.
if ($master->isSevered()) {
$master->checkHealth();
}
if ($master->isSevered()) {
self::setReadOnly(true, self::READONLY_SEVERED);
break;
}
}
}
try {
// See T13403. If we're starting up in "config optional" mode, suppress
// messages about connection retries.
if ($config_optional) {
$database_source = @new PhabricatorConfigDatabaseSource('default');
} else {
$database_source = new PhabricatorConfigDatabaseSource('default');
}
$database_source->setName(pht('Database'));
$stack->pushSource($database_source);
} catch (AphrontSchemaQueryException $exception) {
// If the database is not available, just skip this configuration
// source. This happens during `bin/storage upgrade`, `bin/conf` before
// schema setup, etc.
} catch (PhabricatorClusterStrandedException $ex) {
// This means we can't connect to any database host. That's fine as
// long as we're running a setup script like `bin/storage`.
if (!$config_optional) {
throw $ex;
}
}
// Drop the config cache one final time to make sure we're getting clean
// reads now that we've finished building the stack.
self::dropConfigCache();
}
public static function repairConfig($key, $value) {
if (!self::$repairSource) {
self::$repairSource = id(new PhabricatorConfigDictionarySource(array()))
->setName(pht('Repaired Config'));
self::$sourceStack->pushSource(self::$repairSource);
}
self::$repairSource->setKeys(array($key => $value));
self::dropConfigCache();
}
public static function overrideConfig($key, $value) {
if (!self::$overrideSource) {
self::$overrideSource = id(new PhabricatorConfigDictionarySource(array()))
->setName(pht('Overridden Config'));
self::$sourceStack->pushSource(self::$overrideSource);
}
self::$overrideSource->setKeys(array($key => $value));
self::dropConfigCache();
}
public static function getUnrepairedEnvConfig($key, $default = null) {
foreach (self::$sourceStack->getStack() as $source) {
if ($source === self::$repairSource) {
continue;
}
$result = $source->getKeys(array($key));
if ($result) {
return $result[$key];
}
}
return $default;
}
public static function getSelectedEnvironmentName() {
$env_var = 'PHABRICATOR_ENV';
$env = idx($_SERVER, $env_var);
if (!$env) {
$env = getenv($env_var);
}
if (!$env) {
$env = idx($_ENV, $env_var);
}
if (!$env) {
$root = dirname(phutil_get_library_root('phabricator'));
$path = $root.'/conf/local/ENVIRONMENT';
if (Filesystem::pathExists($path)) {
$env = trim(Filesystem::readFile($path));
}
}
return $env;
}
/* -( Reading Configuration )---------------------------------------------- */
/**
* Get the current configuration setting for a given key.
*
* If the key is not found, then throw an Exception.
*
* @task read
*/
public static function getEnvConfig($key) {
if (!self::$sourceStack) {
throw new Exception(
pht(
'Trying to read configuration "%s" before configuration has been '.
'initialized.',
$key));
}
if (isset(self::$cache[$key])) {
return self::$cache[$key];
}
if (array_key_exists($key, self::$cache)) {
return self::$cache[$key];
}
$result = self::$sourceStack->getKeys(array($key));
if (array_key_exists($key, $result)) {
self::$cache[$key] = $result[$key];
return $result[$key];
} else {
throw new Exception(
pht(
"No config value specified for key '%s'.",
$key));
}
}
/**
* Get the current configuration setting for a given key. If the key
* does not exist, return a default value instead of throwing. This is
* primarily useful for migrations involving keys which are slated for
* removal.
*
* @task read
*/
public static function getEnvConfigIfExists($key, $default = null) {
try {
return self::getEnvConfig($key);
} catch (Exception $ex) {
return $default;
}
}
/**
* Get the fully-qualified URI for a path.
*
* @task read
*/
public static function getURI($path) {
return rtrim(self::getAnyBaseURI(), '/').$path;
}
/**
* Get the fully-qualified production URI for a path.
*
* @task read
*/
public static function getProductionURI($path) {
// If we're passed a URI which already has a domain, simply return it
// unmodified. In particular, files may have URIs which point to a CDN
// domain.
$uri = new PhutilURI($path);
if ($uri->getDomain()) {
return $path;
}
$production_domain = self::getEnvConfig('phabricator.production-uri');
if (!$production_domain) {
$production_domain = self::getAnyBaseURI();
}
return rtrim($production_domain, '/').$path;
}
public static function isSelfURI($raw_uri) {
$uri = new PhutilURI($raw_uri);
$host = $uri->getDomain();
if (!strlen($host)) {
return false;
}
$host = phutil_utf8_strtolower($host);
$self_map = self::getSelfURIMap();
return isset($self_map[$host]);
}
private static function getSelfURIMap() {
$self_uris = array();
$self_uris[] = self::getProductionURI('/');
$self_uris[] = self::getURI('/');
$allowed_uris = self::getEnvConfig('phabricator.allowed-uris');
foreach ($allowed_uris as $allowed_uri) {
$self_uris[] = $allowed_uri;
}
$self_map = array();
foreach ($self_uris as $self_uri) {
$host = id(new PhutilURI($self_uri))->getDomain();
if (!strlen($host)) {
continue;
}
$host = phutil_utf8_strtolower($host);
$self_map[$host] = $host;
}
return $self_map;
}
/**
* Get the fully-qualified production URI for a static resource path.
*
* @task read
*/
public static function getCDNURI($path) {
$alt = self::getEnvConfig('security.alternate-file-domain');
if (!$alt) {
$alt = self::getAnyBaseURI();
}
$uri = new PhutilURI($alt);
$uri->setPath($path);
return (string)$uri;
}
/**
* Get the fully-qualified production URI for a documentation resource.
*
* @task read
*/
public static function getDoclink($resource, $type = 'article') {
$params = array(
'name' => $resource,
'type' => $type,
'jump' => true,
);
$uri = new PhutilURI(
'https://secure.phabricator.com/diviner/find/',
$params);
return phutil_string_cast($uri);
}
/**
* Build a concrete object from a configuration key.
*
* @task read
*/
public static function newObjectFromConfig($key, $args = array()) {
$class = self::getEnvConfig($key);
return newv($class, $args);
}
public static function getAnyBaseURI() {
$base_uri = self::getEnvConfig('phabricator.base-uri');
if (!$base_uri) {
$base_uri = self::getRequestBaseURI();
}
if (!$base_uri) {
throw new Exception(
pht(
"Define '%s' in your configuration to continue.",
'phabricator.base-uri'));
}
return $base_uri;
}
public static function getRequestBaseURI() {
return self::$requestBaseURI;
}
public static function setRequestBaseURI($uri) {
self::$requestBaseURI = $uri;
}
public static function isReadOnly() {
if (self::$readOnly !== null) {
return self::$readOnly;
}
return self::getEnvConfig('cluster.read-only');
}
public static function setReadOnly($read_only, $reason) {
self::$readOnly = $read_only;
self::$readOnlyReason = $reason;
}
public static function getReadOnlyMessage() {
$reason = self::getReadOnlyReason();
switch ($reason) {
case self::READONLY_MASTERLESS:
return pht(
- 'Phabricator is in read-only mode (no writable database '.
+ 'This server is in read-only mode (no writable database '.
'is configured).');
case self::READONLY_UNREACHABLE:
return pht(
- 'Phabricator is in read-only mode (unreachable master).');
+ 'This server is in read-only mode (unreachable master).');
case self::READONLY_SEVERED:
return pht(
- 'Phabricator is in read-only mode (major interruption).');
+ 'This server is in read-only mode (major interruption).');
}
- return pht('Phabricator is in read-only mode.');
+ return pht('This server is in read-only mode.');
}
public static function getReadOnlyURI() {
return urisprintf(
'/readonly/%s/',
self::getReadOnlyReason());
}
public static function getReadOnlyReason() {
if (!self::isReadOnly()) {
return null;
}
if (self::$readOnlyReason !== null) {
return self::$readOnlyReason;
}
return self::READONLY_CONFIG;
}
/* -( Unit Test Support )-------------------------------------------------- */
/**
* @task test
*/
public static function beginScopedEnv() {
return new PhabricatorScopedEnv(self::pushTestEnvironment());
}
/**
* @task test
*/
private static function pushTestEnvironment() {
self::dropConfigCache();
$source = new PhabricatorConfigDictionarySource(array());
self::$sourceStack->pushSource($source);
return spl_object_hash($source);
}
/**
* @task test
*/
public static function popTestEnvironment($key) {
self::dropConfigCache();
$source = self::$sourceStack->popSource();
$stack_key = spl_object_hash($source);
if ($stack_key !== $key) {
self::$sourceStack->pushSource($source);
throw new Exception(
pht(
'Scoped environments were destroyed in a different order than they '.
'were initialized.'));
}
}
/* -( URI Validation )----------------------------------------------------- */
/**
* Detect if a URI satisfies either @{method:isValidLocalURIForLink} or
* @{method:isValidRemoteURIForLink}, i.e. is a page on this server or the
* URI of some other resource which has a valid protocol. This rejects
* garbage URIs and URIs with protocols which do not appear in the
* `uri.allowed-protocols` configuration, notably 'javascript:' URIs.
*
* NOTE: This method is generally intended to reject URIs which it may be
* unsafe to put in an "href" link attribute.
*
* @param string URI to test.
* @return bool True if the URI identifies a web resource.
* @task uri
*/
public static function isValidURIForLink($uri) {
return self::isValidLocalURIForLink($uri) ||
self::isValidRemoteURIForLink($uri);
}
/**
* Detect if a URI identifies some page on this server.
*
* NOTE: This method is generally intended to reject URIs which it may be
* unsafe to issue a "Location:" redirect to.
*
* @param string URI to test.
* @return bool True if the URI identifies a local page.
* @task uri
*/
public static function isValidLocalURIForLink($uri) {
$uri = (string)$uri;
if (!strlen($uri)) {
return false;
}
if (preg_match('/\s/', $uri)) {
// PHP hasn't been vulnerable to header injection attacks for a bunch of
// years, but we can safely reject these anyway since they're never valid.
return false;
}
// Chrome (at a minimum) interprets backslashes in Location headers and the
// URL bar as forward slashes. This is probably intended to reduce user
// error caused by confusion over which key is "forward slash" vs "back
// slash".
//
// However, it means a URI like "/\evil.com" is interpreted like
// "//evil.com", which is a protocol relative remote URI.
//
// Since we currently never generate URIs with backslashes in them, reject
// these unconditionally rather than trying to figure out how browsers will
// interpret them.
if (preg_match('/\\\\/', $uri)) {
return false;
}
// Valid URIs must begin with '/', followed by the end of the string or some
// other non-'/' character. This rejects protocol-relative URIs like
// "//evil.com/evil_stuff/".
return (bool)preg_match('@^/([^/]|$)@', $uri);
}
/**
* Detect if a URI identifies some valid linkable remote resource.
*
* @param string URI to test.
* @return bool True if a URI identifies a remote resource with an allowed
* protocol.
* @task uri
*/
public static function isValidRemoteURIForLink($uri) {
try {
self::requireValidRemoteURIForLink($uri);
return true;
} catch (Exception $ex) {
return false;
}
}
/**
* Detect if a URI identifies a valid linkable remote resource, throwing a
* detailed message if it does not.
*
* A valid linkable remote resource can be safely linked or redirected to.
* This is primarily a protocol whitelist check.
*
* @param string URI to test.
* @return void
* @task uri
*/
public static function requireValidRemoteURIForLink($raw_uri) {
$uri = new PhutilURI($raw_uri);
$proto = $uri->getProtocol();
if (!strlen($proto)) {
throw new Exception(
pht(
'URI "%s" is not a valid linkable resource. A valid linkable '.
'resource URI must specify a protocol.',
$raw_uri));
}
$protocols = self::getEnvConfig('uri.allowed-protocols');
if (!isset($protocols[$proto])) {
throw new Exception(
pht(
'URI "%s" is not a valid linkable resource. A valid linkable '.
'resource URI must use one of these protocols: %s.',
$raw_uri,
implode(', ', array_keys($protocols))));
}
$domain = $uri->getDomain();
if (!strlen($domain)) {
throw new Exception(
pht(
'URI "%s" is not a valid linkable resource. A valid linkable '.
'resource URI must specify a domain.',
$raw_uri));
}
}
/**
* Detect if a URI identifies a valid fetchable remote resource.
*
* @param string URI to test.
* @param list<string> Allowed protocols.
* @return bool True if the URI is a valid fetchable remote resource.
* @task uri
*/
public static function isValidRemoteURIForFetch($uri, array $protocols) {
try {
self::requireValidRemoteURIForFetch($uri, $protocols);
return true;
} catch (Exception $ex) {
return false;
}
}
/**
* Detect if a URI identifies a valid fetchable remote resource, throwing
* a detailed message if it does not.
*
* A valid fetchable remote resource can be safely fetched using a request
* originating on this server. This is a primarily an address check against
* the outbound address blacklist.
*
* @param string URI to test.
* @param list<string> Allowed protocols.
* @return pair<string, string> Pre-resolved URI and domain.
* @task uri
*/
public static function requireValidRemoteURIForFetch(
$raw_uri,
array $protocols) {
$uri = new PhutilURI($raw_uri);
$proto = $uri->getProtocol();
if (!strlen($proto)) {
throw new Exception(
pht(
'URI "%s" is not a valid fetchable resource. A valid fetchable '.
'resource URI must specify a protocol.',
$raw_uri));
}
$protocols = array_fuse($protocols);
if (!isset($protocols[$proto])) {
throw new Exception(
pht(
'URI "%s" is not a valid fetchable resource. A valid fetchable '.
'resource URI must use one of these protocols: %s.',
$raw_uri,
implode(', ', array_keys($protocols))));
}
$domain = $uri->getDomain();
if (!strlen($domain)) {
throw new Exception(
pht(
'URI "%s" is not a valid fetchable resource. A valid fetchable '.
'resource URI must specify a domain.',
$raw_uri));
}
$addresses = gethostbynamel($domain);
if (!$addresses) {
throw new Exception(
pht(
'URI "%s" is not a valid fetchable resource. The domain "%s" could '.
'not be resolved.',
$raw_uri,
$domain));
}
foreach ($addresses as $address) {
if (self::isBlacklistedOutboundAddress($address)) {
throw new Exception(
pht(
'URI "%s" is not a valid fetchable resource. The domain "%s" '.
'resolves to the address "%s", which is blacklisted for '.
'outbound requests.',
$raw_uri,
$domain,
$address));
}
}
$resolved_uri = clone $uri;
$resolved_uri->setDomain(head($addresses));
return array($resolved_uri, $domain);
}
/**
* Determine if an IP address is in the outbound address blacklist.
*
* @param string IP address.
* @return bool True if the address is blacklisted.
*/
public static function isBlacklistedOutboundAddress($address) {
$blacklist = self::getEnvConfig('security.outbound-blacklist');
return PhutilCIDRList::newList($blacklist)->containsAddress($address);
}
public static function isClusterRemoteAddress() {
$cluster_addresses = self::getEnvConfig('cluster.addresses');
if (!$cluster_addresses) {
return false;
}
$address = self::getRemoteAddress();
if (!$address) {
throw new Exception(
pht(
'Unable to test remote address against cluster whitelist: '.
'REMOTE_ADDR is not defined or not valid.'));
}
return self::isClusterAddress($address);
}
public static function isClusterAddress($address) {
$cluster_addresses = self::getEnvConfig('cluster.addresses');
if (!$cluster_addresses) {
throw new Exception(
pht(
- 'Phabricator is not configured to serve cluster requests. '.
+ 'This server is not configured to serve cluster requests. '.
'Set `cluster.addresses` in the configuration to whitelist '.
'cluster hosts before sending requests that use a cluster '.
'authentication mechanism.'));
}
return PhutilCIDRList::newList($cluster_addresses)
->containsAddress($address);
}
public static function getRemoteAddress() {
$address = idx($_SERVER, 'REMOTE_ADDR');
if (!$address) {
return null;
}
try {
return PhutilIPAddress::newAddress($address);
} catch (Exception $ex) {
return null;
}
}
/* -( Internals )---------------------------------------------------------- */
/**
* @task internal
*/
public static function envConfigExists($key) {
return array_key_exists($key, self::$sourceStack->getKeys(array($key)));
}
/**
* @task internal
*/
public static function getAllConfigKeys() {
return self::$sourceStack->getAllKeys();
}
public static function getConfigSourceStack() {
return self::$sourceStack;
}
/**
* @task internal
*/
public static function overrideTestEnvConfig($stack_key, $key, $value) {
$tmp = array();
// If we don't have the right key, we'll throw when popping the last
// source off the stack.
do {
$source = self::$sourceStack->popSource();
array_unshift($tmp, $source);
if (spl_object_hash($source) == $stack_key) {
$source->setKeys(array($key => $value));
break;
}
} while (true);
foreach ($tmp as $source) {
self::$sourceStack->pushSource($source);
}
self::dropConfigCache();
}
private static function dropConfigCache() {
self::$cache = array();
}
private static function resetUmask() {
// Reset the umask to the common standard umask. The umask controls default
// permissions when files are created and propagates to subprocesses.
// "022" is the most common umask, but sometimes it is set to something
// unusual by the calling environment.
// Since various things rely on this umask to work properly and we are
// not aware of any legitimate reasons to adjust it, unconditionally
// normalize it until such reasons arise. See T7475 for discussion.
umask(022);
}
/**
* Get the path to an empty directory which is readable by all of the system
* user accounts that Phabricator acts as.
*
* In some cases, a binary needs some valid HOME or CWD to continue, but not
* all user accounts have valid home directories and even if they do they
* may not be readable after a `sudo` operation.
*
* @return string Path to an empty directory suitable for use as a CWD.
*/
public static function getEmptyCWD() {
$root = dirname(phutil_get_library_root('phabricator'));
return $root.'/support/empty/';
}
}
diff --git a/src/infrastructure/export/format/PhabricatorExcelExportFormat.php b/src/infrastructure/export/format/PhabricatorExcelExportFormat.php
index e7135bd9db..dd33813384 100644
--- a/src/infrastructure/export/format/PhabricatorExcelExportFormat.php
+++ b/src/infrastructure/export/format/PhabricatorExcelExportFormat.php
@@ -1,183 +1,182 @@
<?php
final class PhabricatorExcelExportFormat
extends PhabricatorExportFormat {
const EXPORTKEY = 'excel';
private $workbook;
private $sheet;
private $rowCursor;
public function getExportFormatName() {
return pht('Excel (.xlsx)');
}
public function isExportFormatEnabled() {
if (!extension_loaded('zip')) {
return false;
}
return @include_once 'PHPExcel.php';
}
public function getInstallInstructions() {
if (!extension_loaded('zip')) {
return pht(<<<EOHELP
Data can not be exported to Excel because the "zip" PHP extension is not
installed. Consult the setup issue in the Config application for guidance on
installing the extension.
EOHELP
);
}
return pht(<<<EOHELP
Data can not be exported to Excel because the PHPExcel library is not
-installed. This software component is required for Phabricator to create
-Excel files.
+installed. This software component is required to create Excel files.
You can install PHPExcel from GitHub:
> https://github.com/PHPOffice/PHPExcel
Briefly:
- Clone that repository somewhere on the sever
(like `/path/to/example/PHPExcel`).
- Update your PHP `%s` setting (in `php.ini`) to include the PHPExcel
`Classes` directory (like `/path/to/example/PHPExcel/Classes`).
EOHELP
,
'include_path');
}
public function getFileExtension() {
return 'xlsx';
}
public function getMIMEContentType() {
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
}
/**
* @phutil-external-symbol class PHPExcel_Cell_DataType
*/
public function addHeaders(array $fields) {
$sheet = $this->getSheet();
$header_format = array(
'font' => array(
'bold' => true,
),
);
$row = 1;
$col = 0;
foreach ($fields as $field) {
$cell_value = $field->getLabel();
$cell_name = $this->getCellName($col, $row);
$cell = $sheet->setCellValue(
$cell_name,
$cell_value,
$return_cell = true);
$sheet->getStyle($cell_name)->applyFromArray($header_format);
$cell->setDataType(PHPExcel_Cell_DataType::TYPE_STRING);
$width = $field->getCharacterWidth();
if ($width !== null) {
$col_name = $this->getCellName($col);
$sheet->getColumnDimension($col_name)
->setWidth($width);
}
$col++;
}
}
public function addObject($object, array $fields, array $map) {
$sheet = $this->getSheet();
$col = 0;
foreach ($fields as $key => $field) {
$cell_value = $map[$key];
$cell_value = $field->getPHPExcelValue($cell_value);
$cell_name = $this->getCellName($col, $this->rowCursor);
$cell = $sheet->setCellValue(
$cell_name,
$cell_value,
$return_cell = true);
$style = $sheet->getStyle($cell_name);
$field->formatPHPExcelCell($cell, $style);
$col++;
}
$this->rowCursor++;
}
/**
* @phutil-external-symbol class PHPExcel_IOFactory
*/
public function newFileData() {
$workbook = $this->getWorkbook();
$writer = PHPExcel_IOFactory::createWriter($workbook, 'Excel2007');
ob_start();
$writer->save('php://output');
$data = ob_get_clean();
return $data;
}
private function getWorkbook() {
if (!$this->workbook) {
$this->workbook = $this->newWorkbook();
}
return $this->workbook;
}
/**
* @phutil-external-symbol class PHPExcel
*/
private function newWorkbook() {
include_once 'PHPExcel.php';
return new PHPExcel();
}
private function getSheet() {
if (!$this->sheet) {
$workbook = $this->getWorkbook();
$sheet = $workbook->setActiveSheetIndex(0);
$sheet->setTitle($this->getTitle());
$this->sheet = $sheet;
// The row cursor starts on the second row, after the header row.
$this->rowCursor = 2;
}
return $this->sheet;
}
/**
* @phutil-external-symbol class PHPExcel_Cell
*/
private function getCellName($col, $row = null) {
$col_name = PHPExcel_Cell::stringFromColumnIndex($col);
if ($row === null) {
return $col_name;
}
return $col_name.$row;
}
}
diff --git a/src/infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php b/src/infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php
index ea20dfedef..cb67041c62 100644
--- a/src/infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php
+++ b/src/infrastructure/markup/markuprule/PhutilRemarkupEvalRule.php
@@ -1,100 +1,100 @@
<?php
final class PhutilRemarkupEvalRule extends PhutilRemarkupRule {
const KEY_EVAL = 'eval';
public function getPriority() {
return 50;
}
public function apply($text) {
return preg_replace_callback(
'/\${{{(.+?)}}}/',
array($this, 'newExpressionToken'),
$text);
}
public function newExpressionToken(array $matches) {
$expression = $matches[1];
if (!$this->isFlatText($expression)) {
return $matches[0];
}
$engine = $this->getEngine();
$token = $engine->storeText($expression);
$list_key = self::KEY_EVAL;
$expression_list = $engine->getTextMetadata($list_key, array());
$expression_list[] = array(
'token' => $token,
'expression' => $expression,
'original' => $matches[0],
);
$engine->setTextMetadata($list_key, $expression_list);
return $token;
}
public function didMarkupText() {
$engine = $this->getEngine();
$list_key = self::KEY_EVAL;
$expression_list = $engine->getTextMetadata($list_key, array());
foreach ($expression_list as $expression_item) {
$token = $expression_item['token'];
$expression = $expression_item['expression'];
$result = $this->evaluateExpression($expression);
if ($result === null) {
$result = $expression_item['original'];
}
$engine->overwriteStoredText($token, $result);
}
}
private function evaluateExpression($expression) {
static $string_map;
if ($string_map === null) {
$string_map = array(
'strings' => array(
'platform' => array(
'server' => array(
- 'name' => pht('Phabricator'),
+ 'name' => PlatformSymbols::getPlatformServerName(),
'path' => pht('phabricator/'),
),
'client' => array(
- 'name' => pht('Arcanist'),
+ 'name' => PlatformSymbols::getPlatformClientName(),
'path' => pht('arcanist/'),
),
),
),
);
}
$parts = explode('.', $expression);
$cursor = $string_map;
foreach ($parts as $part) {
if (isset($cursor[$part])) {
$cursor = $cursor[$part];
} else {
break;
}
}
if (is_string($cursor)) {
return $cursor;
}
return null;
}
}
diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDatabasesWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDatabasesWorkflow.php
index ef6735ec8f..00689ab5fe 100644
--- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDatabasesWorkflow.php
+++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDatabasesWorkflow.php
@@ -1,27 +1,27 @@
<?php
final class PhabricatorStorageManagementDatabasesWorkflow
extends PhabricatorStorageManagementWorkflow {
protected function didConstruct() {
$this
->setName('databases')
->setExamples('**databases** [__options__]')
- ->setSynopsis(pht('List Phabricator databases.'));
+ ->setSynopsis(pht('List databases.'));
}
protected function isReadOnlyWorkflow() {
return true;
}
public function didExecute(PhutilArgumentParser $args) {
$api = $this->getAnyAPI();
$patches = $this->getPatches();
$databases = $api->getDatabaseList($patches, true);
echo implode("\n", $databases)."\n";
return 0;
}
}
diff --git a/src/infrastructure/util/PhabricatorHash.php b/src/infrastructure/util/PhabricatorHash.php
index fdd6609288..8916b37a0e 100644
--- a/src/infrastructure/util/PhabricatorHash.php
+++ b/src/infrastructure/util/PhabricatorHash.php
@@ -1,281 +1,281 @@
<?php
final class PhabricatorHash extends Phobject {
const INDEX_DIGEST_LENGTH = 12;
const ANCHOR_DIGEST_LENGTH = 12;
/**
* Digest a string using HMAC+SHA1.
*
* Because a SHA1 collision is now known, this method should be considered
* weak. Callers should prefer @{method:digestWithNamedKey}.
*
* @param string Input string.
* @return string 32-byte hexadecimal SHA1+HMAC hash.
*/
public static function weakDigest($string, $key = null) {
if ($key === null) {
$key = PhabricatorEnv::getEnvConfig('security.hmac-key');
}
if (!$key) {
throw new Exception(
pht(
- "Set a '%s' in your Phabricator configuration!",
+ "Set a '%s' in your configuration!",
'security.hmac-key'));
}
return hash_hmac('sha1', $string, $key);
}
/**
* Digest a string for use in, e.g., a MySQL index. This produces a short
* (12-byte), case-sensitive alphanumeric string with 72 bits of entropy,
* which is generally safe in most contexts (notably, URLs).
*
* This method emphasizes compactness, and should not be used for security
* related hashing (for general purpose hashing, see @{method:digest}).
*
* @param string Input string.
* @return string 12-byte, case-sensitive, mostly-alphanumeric hash of
* the string.
*/
public static function digestForIndex($string) {
$hash = sha1($string, $raw_output = true);
static $map;
if ($map === null) {
$map = '0123456789'.
'abcdefghij'.
'klmnopqrst'.
'uvwxyzABCD'.
'EFGHIJKLMN'.
'OPQRSTUVWX'.
'YZ._';
}
$result = '';
for ($ii = 0; $ii < self::INDEX_DIGEST_LENGTH; $ii++) {
$result .= $map[(ord($hash[$ii]) & 0x3F)];
}
return $result;
}
/**
* Digest a string for use in HTML page anchors. This is similar to
* @{method:digestForIndex} but produces purely alphanumeric output.
*
* This tries to be mostly compatible with the index digest to limit how
* much stuff we're breaking by switching to it. For additional discussion,
* see T13045.
*
* @param string Input string.
* @return string 12-byte, case-sensitive, purely-alphanumeric hash of
* the string.
*/
public static function digestForAnchor($string) {
$hash = sha1($string, $raw_output = true);
static $map;
if ($map === null) {
$map = '0123456789'.
'abcdefghij'.
'klmnopqrst'.
'uvwxyzABCD'.
'EFGHIJKLMN'.
'OPQRSTUVWX'.
'YZ';
}
$result = '';
$accum = 0;
$map_size = strlen($map);
for ($ii = 0; $ii < self::ANCHOR_DIGEST_LENGTH; $ii++) {
$byte = ord($hash[$ii]);
$low_bits = ($byte & 0x3F);
$accum = ($accum + $byte) % $map_size;
if ($low_bits < $map_size) {
// If an index digest would produce any alphanumeric character, just
// use that character. This means that these digests are the same as
// digests created with "digestForIndex()" in all positions where the
// output character is some character other than "." or "_".
$result .= $map[$low_bits];
} else {
// If an index digest would produce a non-alphumeric character ("." or
// "_"), pick an alphanumeric character instead. We accumulate an
// index into the alphanumeric character list to try to preserve
// entropy here. We could use this strategy for all bytes instead,
// but then these digests would differ from digests created with
// "digestForIndex()" in all positions, instead of just a small number
// of positions.
$result .= $map[$accum];
}
}
return $result;
}
public static function digestToRange($string, $min, $max) {
if ($min > $max) {
throw new Exception(pht('Maximum must be larger than minimum.'));
}
if ($min == $max) {
return $min;
}
$hash = sha1($string, $raw_output = true);
// Make sure this ends up positive, even on 32-bit machines.
$value = head(unpack('L', $hash)) & 0x7FFFFFFF;
return $min + ($value % (1 + $max - $min));
}
/**
* Shorten a string to a maximum byte length in a collision-resistant way
* while retaining some degree of human-readability.
*
* This function converts an input string into a prefix plus a hash. For
* example, a very long string beginning with "crabapplepie..." might be
* digested to something like "crabapp-N1wM1Nz3U84k".
*
* This allows the maximum length of identifiers to be fixed while
* maintaining a high degree of collision resistance and a moderate degree
* of human readability.
*
* @param string The string to shorten.
* @param int Maximum length of the result.
* @return string String shortened in a collision-resistant way.
*/
public static function digestToLength($string, $length) {
// We need at least two more characters than the hash length to fit in a
// a 1-character prefix and a separator.
$min_length = self::INDEX_DIGEST_LENGTH + 2;
if ($length < $min_length) {
throw new Exception(
pht(
'Length parameter in %s must be at least %s, '.
'but %s was provided.',
'digestToLength()',
new PhutilNumber($min_length),
new PhutilNumber($length)));
}
// We could conceivably return the string unmodified if it's shorter than
// the specified length. Instead, always hash it. This makes the output of
// the method more recognizable and consistent (no surprising new behavior
// once you hit a string longer than `$length`) and prevents an attacker
// who can control the inputs from intentionally using the hashed form
// of a string to cause a collision.
$hash = self::digestForIndex($string);
$prefix = substr($string, 0, ($length - ($min_length - 1)));
return $prefix.'-'.$hash;
}
public static function digestWithNamedKey($message, $key_name) {
$key_bytes = self::getNamedHMACKey($key_name);
return self::digestHMACSHA256($message, $key_bytes);
}
public static function digestHMACSHA256($message, $key) {
if (!is_string($message)) {
throw new Exception(
pht('HMAC-SHA256 can only digest strings.'));
}
if (!is_string($key)) {
throw new Exception(
pht('HMAC-SHA256 keys must be strings.'));
}
if (!strlen($key)) {
throw new Exception(
pht('HMAC-SHA256 requires a nonempty key.'));
}
$result = hash_hmac('sha256', $message, $key, $raw_output = false);
// Although "hash_hmac()" is documented as returning `false` when it fails,
// it can also return `null` if you pass an object as the "$message".
if ($result === false || $result === null) {
throw new Exception(
pht('Unable to compute HMAC-SHA256 digest of message.'));
}
return $result;
}
/* -( HMAC Key Management )------------------------------------------------ */
private static function getNamedHMACKey($hmac_name) {
$cache = PhabricatorCaches::getImmutableCache();
$cache_key = "hmac.key({$hmac_name})";
$hmac_key = $cache->getKey($cache_key);
if (($hmac_key === null) || !strlen($hmac_key)) {
$hmac_key = self::readHMACKey($hmac_name);
if ($hmac_key === null) {
$hmac_key = self::newHMACKey($hmac_name);
self::writeHMACKey($hmac_name, $hmac_key);
}
$cache->setKey($cache_key, $hmac_key);
}
// The "hex2bin()" function doesn't exist until PHP 5.4.0 so just
// implement it inline.
$result = '';
for ($ii = 0; $ii < strlen($hmac_key); $ii += 2) {
$result .= pack('H*', substr($hmac_key, $ii, 2));
}
return $result;
}
private static function newHMACKey($hmac_name) {
$hmac_key = Filesystem::readRandomBytes(64);
return bin2hex($hmac_key);
}
private static function writeHMACKey($hmac_name, $hmac_key) {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
id(new PhabricatorAuthHMACKey())
->setKeyName($hmac_name)
->setKeyValue($hmac_key)
->save();
unset($unguarded);
}
private static function readHMACKey($hmac_name) {
$table = new PhabricatorAuthHMACKey();
$conn = $table->establishConnection('r');
$row = queryfx_one(
$conn,
'SELECT keyValue FROM %T WHERE keyName = %s',
$table->getTableName(),
$hmac_name);
if (!$row) {
return null;
}
return $row['keyValue'];
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Nov 24, 2:57 PM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1037
Default Alt Text
(69 KB)

Event Timeline