Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/config/controller/PhabricatorConfigEditController.php b/src/applications/config/controller/PhabricatorConfigEditController.php
index 5a6f737ea5..c0753e25f1 100644
--- a/src/applications/config/controller/PhabricatorConfigEditController.php
+++ b/src/applications/config/controller/PhabricatorConfigEditController.php
@@ -1,422 +1,439 @@
<?php
final class PhabricatorConfigEditController
extends PhabricatorConfigController {
private $key;
public function willProcessRequest(array $data) {
$this->key = $data['key'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$options = PhabricatorApplicationConfigOptions::loadAllOptions();
if (empty($options[$this->key])) {
// This may be a dead config entry, which existed in the past but no
// longer exists. Allow it to be edited so it can be reviewed and
// deleted.
$option = id(new PhabricatorConfigOption())
->setKey($this->key)
->setType('wild')
->setDefault(null)
->setDescription(
pht(
"This configuration option is unknown. It may be misspelled, ".
"or have existed in a previous version of Phabricator."));
$group = null;
$group_uri = $this->getApplicationURI();
} else {
$option = $options[$this->key];
$group = $option->getGroup();
$group_uri = $this->getApplicationURI('group/'.$group->getKey().'/');
}
$issue = $request->getStr('issue');
if ($issue) {
// If the user came here from an open setup issue, send them back.
$done_uri = $this->getApplicationURI('issue/'.$issue.'/');
} else {
$done_uri = $group_uri;
}
// Check if the config key is already stored in the database.
// Grab the value if it is.
$config_entry = id(new PhabricatorConfigEntry())
->loadOneWhere(
'configKey = %s AND namespace = %s',
$this->key,
'default');
if (!$config_entry) {
$config_entry = id(new PhabricatorConfigEntry())
->setConfigKey($this->key)
->setNamespace('default')
->setIsDeleted(true);
}
$e_value = null;
$errors = array();
- if ($request->isFormPost()) {
+ if ($request->isFormPost() && !$option->getLocked()) {
$result = $this->readRequest(
$option,
$request);
list($e_value, $value_errors, $display_value, $xaction) = $result;
$errors = array_merge($errors, $value_errors);
if (!$errors) {
$editor = id(new PhabricatorConfigEditor())
->setActor($user)
->setContinueOnNoEffect(true)
->setContentSource(
PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_WEB,
array(
'ip' => $request->getRemoteAddr(),
)));
try {
$editor->applyTransactions($config_entry, array($xaction));
return id(new AphrontRedirectResponse())->setURI($done_uri);
} catch (PhabricatorConfigValidationException $ex) {
$e_value = pht('Invalid');
$errors[] = $ex->getMessage();
}
}
} else {
$display_value = $this->getDisplayValue($option, $config_entry);
}
$form = new AphrontFormView();
$form->setFlexible(true);
$error_view = null;
if ($errors) {
$error_view = id(new AphrontErrorView())
->setTitle(pht('You broke everything!'))
->setErrors($errors);
+ } else if ($option->getLocked()) {
+ $msg = pht(
+ "This configuration is locked and can not be edited from the web ".
+ "interface.");
+
+ $error_view = id(new AphrontErrorView())
+ ->setTitle(pht('Configuration Locked'))
+ ->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
+ ->appendChild('<p>'.phutil_escape_html($msg).'</p>');
}
$control = $this->renderControl(
$option,
$display_value,
$e_value);
$engine = new PhabricatorMarkupEngine();
$engine->addObject($option, 'description');
$engine->process();
$description = phutil_render_tag(
'div',
array(
'class' => 'phabricator-remarkup',
),
$engine->getOutput($option, 'description'));
$form
->setUser($user)
->addHiddenInput('issue', $request->getStr('issue'))
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Description'))
->setValue($description))
- ->appendChild($control)
- ->appendChild(
- id(new AphrontFormSubmitControl())
- ->addCancelButton($done_uri)
- ->setValue(pht('Save Config Entry')));
+ ->appendChild($control);
+
+ if (!$option->getLocked()) {
+ $form
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->addCancelButton($done_uri)
+ ->setValue(pht('Save Config Entry')));
+ }
$examples = $this->renderExamples($option);
if ($examples) {
$form->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Examples'))
->setValue($examples));
}
$form->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Default'))
->setValue($this->renderDefaults($option)));
$title = pht('Edit %s', $this->key);
$short = pht('Edit');
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht('Config'))
->setHref($this->getApplicationURI()));
if ($group) {
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName($group->getName())
->setHref($group_uri));
}
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName($this->key)
->setHref('/config/edit/'.$this->key));
$xactions = id(new PhabricatorConfigTransactionQuery())
->withObjectPHIDs(array($config_entry->getPHID()))
->setViewer($user)
->execute();
$xaction_view = id(new PhabricatorApplicationTransactionView())
->setUser($user)
->setTransactions($xactions);
return $this->buildApplicationPage(
array(
$crumbs,
id(new PhabricatorHeaderView())->setHeader($title),
$error_view,
$form,
$xaction_view,
),
array(
'title' => $title,
'device' => true,
));
}
private function readRequest(
PhabricatorConfigOption $option,
AphrontRequest $request) {
$xaction = new PhabricatorConfigTransaction();
$xaction->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT);
$e_value = null;
$errors = array();
$value = $request->getStr('value');
if (!strlen($value)) {
$value = null;
$xaction->setNewValue(
array(
'deleted' => true,
'value' => null,
));
return array($e_value, $errors, $value, $xaction);
}
$type = $option->getType();
$set_value = null;
switch ($type) {
case 'int':
if (preg_match('/^-?[0-9]+$/', trim($value))) {
$set_value = (int)$value;
} else {
$e_value = pht('Invalid');
$errors[] = pht('Value must be an integer.');
}
break;
case 'string':
$set_value = (string)$value;
break;
case 'list<string>':
$set_value = $request->getStrList('value');
break;
case 'bool':
switch ($value) {
case 'true':
$set_value = true;
break;
case 'false':
$set_value = false;
break;
default:
$e_value = pht('Invalid');
$errors[] = pht('Value must be boolean, "true" or "false".');
break;
}
break;
default:
$json = json_decode($value, true);
if ($json === null && strtolower($value) != 'null') {
$e_value = pht('Invalid');
$errors[] = pht(
'The given value must be valid JSON. This means, among '.
'other things, that you must wrap strings in double-quotes.');
$set_value = $json;
}
break;
}
if (!$errors) {
$xaction->setNewValue(
array(
'deleted' => false,
'value' => $set_value,
));
} else {
$xaction = null;
}
return array($e_value, $errors, $value, $xaction);
}
private function getDisplayValue(
PhabricatorConfigOption $option,
PhabricatorConfigEntry $entry) {
if ($entry->getIsDeleted()) {
return null;
}
$type = $option->getType();
$value = $entry->getValue();
switch ($type) {
case 'int':
case 'string':
return $value;
case 'bool':
return $value ? 'true' : 'false';
case 'list<string>':
return implode("\n", nonempty($value, array()));
default:
return $this->prettyPrintJSON($value);
}
}
private function renderControl(
PhabricatorConfigOption $option,
$display_value,
$e_value) {
$type = $option->getType();
switch ($type) {
case 'int':
case 'string':
$control = id(new AphrontFormTextControl());
break;
case 'bool':
$control = id(new AphrontFormSelectControl())
->setOptions(
array(
'' => '(Use Default)',
'true' => idx($option->getOptions(), 0),
'false' => idx($option->getOptions(), 1),
));
break;
case 'list<string>':
$control = id(new AphrontFormTextAreaControl())
->setCaption(pht('Separate values with newlines or commas.'));
break;
default:
$control = id(new AphrontFormTextAreaControl())
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
->setCustomClass('PhabricatorMonospaced')
->setCaption(pht('Enter value in JSON.'));
break;
}
$control
->setLabel('Value')
->setError($e_value)
->setValue($display_value)
->setName('value');
+ if ($option->getLocked()) {
+ $control->setDisabled(true);
+ }
+
return $control;
}
private function renderExamples(PhabricatorConfigOption $option) {
$examples = $option->getExamples();
if (!$examples) {
return null;
}
$table = array();
$table[] = '<tr class="column-labels">';
$table[] = '<th>'.pht('Example').'</th>';
$table[] = '<th>'.pht('Value').'</th>';
$table[] = '</tr>';
foreach ($examples as $example) {
list($value, $description) = $example;
if ($value === null) {
$value = '<em>'.pht('(empty)').'</em>';
} else {
$value = nl2br(phutil_escape_html($value));
}
$table[] = '<tr>';
$table[] = '<th>'.phutil_escape_html($description).'</th>';
$table[] = '<td>'.$value.'</td>';
$table[] = '</tr>';
}
require_celerity_resource('config-options-css');
return phutil_render_tag(
'table',
array(
'class' => 'config-option-table',
),
implode("\n", $table));
}
private function renderDefaults(PhabricatorConfigOption $option) {
$stack = PhabricatorEnv::getConfigSourceStack();
$stack = $stack->getStack();
/*
TODO: Once DatabaseSource lands, do this:
foreach ($stack as $key => $source) {
unset($stack[$key]);
if ($source instanceof PhabricatorConfigDatabaseSource) {
break;
}
}
*/
$table = array();
$table[] = '<tr class="column-labels">';
$table[] = '<th>'.pht('Source').'</th>';
$table[] = '<th>'.pht('Value').'</th>';
$table[] = '</tr>';
foreach ($stack as $key => $source) {
$value = $source->getKeys(
array(
$option->getKey(),
));
if (!array_key_exists($option->getKey(), $value)) {
$value = '<em>'.pht('(empty)').'</em>';
} else {
$value = $this->prettyPrintJSON($value[$option->getKey()]);
}
$table[] = '<tr>';
$table[] = '<th>'.phutil_escape_html($source->getName()).'</th>';
$table[] = '<td>'.$value.'</td>';
$table[] = '</tr>';
}
require_celerity_resource('config-options-css');
return phutil_render_tag(
'table',
array(
'class' => 'config-option-table',
),
implode("\n", $table));
}
}
diff --git a/src/applications/config/controller/PhabricatorConfigGroupController.php b/src/applications/config/controller/PhabricatorConfigGroupController.php
index 0239457729..def0510ca4 100644
--- a/src/applications/config/controller/PhabricatorConfigGroupController.php
+++ b/src/applications/config/controller/PhabricatorConfigGroupController.php
@@ -1,100 +1,104 @@
<?php
final class PhabricatorConfigGroupController
extends PhabricatorConfigController {
private $groupKey;
public function willProcessRequest(array $data) {
$this->groupKey = $data['key'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$groups = PhabricatorApplicationConfigOptions::loadAll();
$options = idx($groups, $this->groupKey);
if (!$options) {
return new Aphront404Response();
}
$title = pht('%s Configuration', $options->getName());
$header = id(new PhabricatorHeaderView())
->setHeader($title);
$list = $this->buildOptionList($options->getOptions());
$crumbs = $this
->buildApplicationCrumbs()
->addCrumb(
id(new PhabricatorCrumbView())
->setName(pht('Config'))
->setHref($this->getApplicationURI()))
->addCrumb(
id(new PhabricatorCrumbView())
->setName($options->getName())
->setHref($this->getApplicationURI()));
return $this->buildApplicationPage(
array(
$crumbs,
$header,
$list,
),
array(
'title' => $title,
'device' => true,
)
);
}
private function buildOptionList(array $options) {
assert_instances_of($options, 'PhabricatorConfigOption');
require_celerity_resource('config-options-css');
$db_values = array();
if ($options) {
$db_values = id(new PhabricatorConfigEntry())->loadAllWhere(
'configKey IN (%Ls) AND namespace = %s',
mpull($options, 'getKey'),
'default');
$db_values = mpull($db_values, null, 'getConfigKey');
}
$list = new PhabricatorObjectItemListView();
foreach ($options as $option) {
$current_value = PhabricatorEnv::getEnvConfig($option->getKey());
$current_value = $this->prettyPrintJSON($current_value);
$current_value = phutil_render_tag(
'div',
array(
'class' => 'config-options-current-value',
),
'<span>'.pht('Current Value:').'</span> '.
phutil_escape_html($current_value));
$item = id(new PhabricatorObjectItemView())
->setHeader($option->getKey())
->setHref('/config/edit/'.$option->getKey().'/')
->addAttribute(phutil_escape_html($option->getSummary()))
->appendChild($current_value);
$db_value = idx($db_values, $option->getKey());
if ($db_value && !$db_value->getIsDeleted()) {
$item->addIcon('edit', pht('Customized'));
} else {
$item->addIcon('edit-grey', pht('Default'));
}
+ if ($option->getLocked()) {
+ $item->addIcon('lock', pht('Locked'));
+ }
+
$list->addItem($item);
}
return $list;
}
}
diff --git a/src/applications/config/option/PhabricatorConfigOption.php b/src/applications/config/option/PhabricatorConfigOption.php
index c7673001bf..7827626e36 100644
--- a/src/applications/config/option/PhabricatorConfigOption.php
+++ b/src/applications/config/option/PhabricatorConfigOption.php
@@ -1,113 +1,123 @@
<?php
final class PhabricatorConfigOption
extends Phobject
implements PhabricatorMarkupInterface{
private $key;
private $default;
private $summary;
private $description;
private $type;
private $options;
private $group;
private $examples;
+ private $locked;
+
+ public function setLocked($locked) {
+ $this->locked = $locked;
+ return $this;
+ }
+
+ public function getLocked() {
+ return $this->locked;
+ }
public function addExample($value, $description) {
$this->examples[] = array($value, $description);
return $this;
}
public function getExamples() {
return $this->examples;
}
public function setGroup(PhabricatorApplicationConfigOptions $group) {
$this->group = $group;
return $this;
}
public function getGroup() {
return $this->group;
}
public function setOptions(array $options) {
$this->options = $options;
return $this;
}
public function getOptions() {
return $this->options;
}
public function setKey($key) {
$this->key = $key;
return $this;
}
public function getKey() {
return $this->key;
}
public function setDefault($default) {
$this->default = $default;
return $this;
}
public function getDefault() {
return $this->default;
}
public function setSummary($summary) {
$this->summary = $summary;
return $this;
}
public function getSummary() {
if (empty($this->summary)) {
return $this->getDescription();
}
return $this->summary;
}
public function setDescription($description) {
$this->description = $description;
return $this;
}
public function getDescription() {
return $this->description;
}
public function setType($type) {
$this->type = $type;
return $this;
}
public function getType() {
return $this->type;
}
/* -( PhabricatorMarkupInterface )----------------------------------------- */
public function getMarkupFieldKey($field) {
return $this->getKey().':'.$field;
}
public function newMarkupEngine($field) {
return PhabricatorMarkupEngine::newMarkupEngine(array());
}
public function getMarkupText($field) {
return $this->getDescription();
}
public function didMarkupText($field, $output, PhutilMarkupEngine $engine) {
return $output;
}
public function shouldUseMarkupCache($field) {
return false;
}
}
diff --git a/src/applications/config/option/PhabricatorCoreConfigOptions.php b/src/applications/config/option/PhabricatorCoreConfigOptions.php
index 301c8b50e6..068dcdbb14 100644
--- a/src/applications/config/option/PhabricatorCoreConfigOptions.php
+++ b/src/applications/config/option/PhabricatorCoreConfigOptions.php
@@ -1,163 +1,164 @@
<?php
final class PhabricatorCoreConfigOptions
extends PhabricatorApplicationConfigOptions {
public function getName() {
return pht("Core");
}
public function getDescription() {
return pht("Configure core options, including URIs.");
}
public function getOptions() {
return array(
$this->newOption('phabricator.base-uri', 'string', null)
->setSummary(pht("URI where Phabricator is installed."))
->setDescription(
pht(
"Set the URI where Phabricator is installed. Setting this ".
"improves security by preventing cookies from being set on other ".
"domains, and allows daemons to send emails with links that have ".
"the correct domain."))
->addExample('http://phabricator.example.com/', 'Valid Setting'),
$this->newOption('phabricator.production-uri', 'string', null)
->setSummary(
pht("Primary install URI, for multi-environment installs."))
->setDescription(
pht(
"If you have multiple Phabricator environments (like a ".
"development/staging environment for working on testing ".
"Phabricator, and a production environment for deploying it), ".
"set the production environment URI here so that emails and other ".
"durable URIs will always generate with links pointing at the ".
"production environment. If unset, defaults to ".
"{{phabricator.base-uri}}. Most installs do not need to set ".
"this option."))
->addExample('http://phabricator.example.com/', 'Valid Setting'),
$this->newOption('phabricator.timezone', 'string', null)
->setSummary(
pht("The timezone Phabricator should use."))
->setDescription(
pht(
"PHP requires that you set a timezone in your php.ini before ".
"using date functions, or it will emit a warning. If this isn't ".
"possible (for instance, because you are using HPHP) you can set ".
"some valid constant for date_default_timezone_set() here and ".
"Phabricator will set it on your behalf, silencing the warning."))
->addExample('America/New_York', pht('US East (EDT)'))
->addExample('America/Chicago', pht('US Central (CDT)'))
->addExample('America/Boise', pht('US Mountain (MDT)'))
->addExample('America/Los_Angeles', pht('US West (PDT)')),
$this->newOption('phabricator.serious-business', 'bool', false)
->setOptions(
array(
pht('Shenanigans'), // That should be interesting to translate. :P
pht('Serious business'),
))
->setSummary(
pht("Should Phabricator be serious?"))
->setDescription(
pht(
"By default, Phabricator includes some silly nonsense in the UI, ".
"such as a submit button called 'Clowncopterize' in Differential ".
"and a call to 'Leap Into Action'. If you'd prefer more ".
"traditional UI strings like 'Submit', you can set this flag to ".
"disable most of the jokes and easter eggs.")),
$this->newOption('storage.default-namespace', 'string', 'phabricator')
+ // NOTE: Lock this, since editing it from the web torpedoes an install.
+ ->setLocked(true)
->setSummary(
pht("The namespace that Phabricator databases should use."))
->setDescription(
pht(
"Phabricator puts databases in a namespace, which defualts to ".
"'phabricator' -- for instance, the Differential database is ".
"named 'phabricator_differential' by default. You can change ".
"this namespace if you want. Normally, you should not do this ".
"unless you are developing Phabricator and using namespaces to ".
- "separate multiple sandbox datasets."))
- ->addExample('phabricator', 'Valid Setting'),
+ "separate multiple sandbox datasets.")),
$this->newOption('environment.append-paths', 'list<string>', null)
->setSummary(
pht("These paths get appended to your \$PATH envrionment variable."))
->setDescription(
pht(
"Phabricator occasionally shells out to other binaries on the ".
"server. An example of this is the \"pygmentize\" command, used ".
"to syntax-highlight code written in languages other than PHP. ".
"By default, it is assumed that these binaries are in the \$PATH ".
"of the user running Phabricator (normally 'apache', 'httpd', or ".
"'nobody'). Here you can add extra directories to the \$PATH ".
"environment variable, for when these binaries are in ".
"non-standard locations."))
->addExample('/usr/local/bin', pht('Add One Path'))
->addExample("/usr/bin\n/usr/local/bin", pht('Add Multiple Paths')),
);
}
protected function didValidateOption(
PhabricatorConfigOption $option,
$value) {
$key = $option->getKey();
if ($key == 'phabricator.base-uri' ||
$key == 'phabricator.production-uri') {
$uri = new PhutilURI($value);
$protocol = $uri->getProtocol();
if ($protocol !== 'http' && $protocol !== 'https') {
throw new PhabricatorConfigValidationException(
pht(
"Config option '%s' is invalid. The URI must start with ".
"'http://' or 'https://'.",
$key));
}
$domain = $uri->getDomain();
if (strpos($domain, '.') === false) {
throw new PhabricatorConfigValidationException(
pht(
"Config option '%s' is invalid. The URI must contain a dot ('.'), ".
"like 'http://example.com/', not just a bare name like ".
"'http://example/'. Some web browsers will not set cookies on ".
"domains with no TLD.",
$key));
}
$path = $uri->getPath();
if ($path !== '' && $path !== '/') {
throw new PhabricatorConfigValidationException(
pht(
"Config option '%s' is invalid. The URI must NOT have a path, ".
"e.g. 'http://phabricator.example.com/' is OK, but ".
"'http://example.com/phabricator/' is not. Phabricator must be ".
"installed on an entire domain; it can not be installed on a ".
"path.",
$key));
}
}
if ($key === 'phabricator.timezone') {
$old = date_default_timezone_get();
$ok = @date_default_timezone_set($value);
@date_default_timezone_set($old);
if (!$ok) {
throw new PhabricatorConfigValidationException(
pht(
"Config option '%s' is invalid. The timezone identifier must ".
"be a valid timezone identifier recognized by PHP, like ".
"'America/Los_Angeles'. You can find a list of valid identifiers ".
"here: %s",
$key,
'http://php.net/manual/timezones.php'));
}
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Dec 3, 3:49 AM (6 h, 38 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
433110
Default Alt Text
(25 KB)

Event Timeline