2012-12-28 00:20:09 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class PhabricatorConfigEditController
|
|
|
|
extends PhabricatorConfigController {
|
|
|
|
|
|
|
|
private $key;
|
|
|
|
|
|
|
|
public function willProcessRequest(array $data) {
|
2012-12-31 00:36:06 +01:00
|
|
|
$this->key = $data['key'];
|
2012-12-28 00:20:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function processRequest() {
|
|
|
|
$request = $this->getRequest();
|
|
|
|
$user = $request->getUser();
|
|
|
|
|
2012-12-31 00:36:06 +01:00
|
|
|
|
|
|
|
$options = PhabricatorApplicationConfigOptions::loadAllOptions();
|
|
|
|
if (empty($options[$this->key])) {
|
2012-12-28 00:20:09 +01:00
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
2012-12-31 00:36:06 +01:00
|
|
|
$option = $options[$this->key];
|
|
|
|
$group = $option->getGroup();
|
|
|
|
$group_uri = $this->getApplicationURI('group/'.$group->getKey().'/');
|
|
|
|
|
2012-12-31 02:04:38 +01:00
|
|
|
$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;
|
|
|
|
}
|
|
|
|
|
2012-12-28 00:20:09 +01:00
|
|
|
// Check if the config key is already stored in the database.
|
|
|
|
// Grab the value if it is.
|
|
|
|
$config_entry = id(new PhabricatorConfigEntry())
|
|
|
|
->loadOneWhere(
|
2012-12-31 00:36:06 +01:00
|
|
|
'configKey = %s AND namespace = %s',
|
2012-12-28 00:20:09 +01:00
|
|
|
$this->key,
|
|
|
|
'default');
|
2013-01-01 23:09:17 +01:00
|
|
|
if (!$config_entry) {
|
2012-12-28 00:20:09 +01:00
|
|
|
$config_entry = id(new PhabricatorConfigEntry())
|
2013-01-01 23:09:17 +01:00
|
|
|
->setConfigKey($this->key)
|
|
|
|
->setNamespace('default')
|
|
|
|
->setIsDeleted(true);
|
2012-12-28 00:20:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$e_value = null;
|
|
|
|
$errors = array();
|
|
|
|
if ($request->isFormPost()) {
|
2012-12-31 00:36:06 +01:00
|
|
|
|
2013-01-01 23:09:17 +01:00
|
|
|
list($e_value, $value_errors, $display_value) = $this->readRequest(
|
|
|
|
$option,
|
|
|
|
$config_entry,
|
|
|
|
$request);
|
2012-12-28 00:20:09 +01:00
|
|
|
|
2013-01-01 23:09:17 +01:00
|
|
|
$errors = array_merge($errors, $value_errors);
|
2012-12-28 00:20:09 +01:00
|
|
|
|
|
|
|
if (!$errors) {
|
|
|
|
$config_entry->save();
|
2012-12-31 02:04:38 +01:00
|
|
|
return id(new AphrontRedirectResponse())->setURI($done_uri);
|
2012-12-28 00:20:09 +01:00
|
|
|
}
|
2013-01-01 23:09:17 +01:00
|
|
|
} else {
|
|
|
|
$display_value = $this->getDisplayValue($option, $config_entry);
|
2012-12-28 00:20:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$form = new AphrontFormView();
|
|
|
|
$form->setFlexible(true);
|
|
|
|
|
|
|
|
$error_view = null;
|
|
|
|
if ($errors) {
|
|
|
|
$error_view = id(new AphrontErrorView())
|
2013-01-01 23:09:17 +01:00
|
|
|
->setTitle(pht('You broke everything!'))
|
2012-12-28 00:20:09 +01:00
|
|
|
->setErrors($errors);
|
|
|
|
}
|
|
|
|
|
2013-01-01 23:09:17 +01:00
|
|
|
$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'));
|
|
|
|
|
2012-12-28 00:20:09 +01:00
|
|
|
$form
|
|
|
|
->setUser($user)
|
2012-12-31 02:04:38 +01:00
|
|
|
->addHiddenInput('issue', $request->getStr('issue'))
|
2012-12-28 00:20:09 +01:00
|
|
|
->appendChild(
|
2013-01-01 23:09:17 +01:00
|
|
|
id(new AphrontFormMarkupControl())
|
|
|
|
->setLabel('Description')
|
|
|
|
->setValue($description))
|
|
|
|
->appendChild($control)
|
2012-12-28 00:20:09 +01:00
|
|
|
->appendChild(
|
|
|
|
id(new AphrontFormSubmitControl())
|
2012-12-31 02:04:38 +01:00
|
|
|
->addCancelButton($done_uri)
|
2013-01-01 23:09:17 +01:00
|
|
|
->setValue(pht('Save Config Entry')));
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: This isn't quite correct -- we should read from the entire
|
|
|
|
// configuration stack, ignoring database configuration. For now, though,
|
|
|
|
// it's a reasonable approximation.
|
|
|
|
$default = $this->prettyPrintJSON($option->getDefault());
|
|
|
|
$form
|
2012-12-28 20:37:55 +01:00
|
|
|
->appendChild(
|
|
|
|
phutil_render_tag(
|
|
|
|
'p',
|
|
|
|
array(
|
|
|
|
'class' => 'aphront-form-input',
|
|
|
|
),
|
|
|
|
'If left blank, the setting will return to its default value. '.
|
|
|
|
'Its default value is:'))
|
|
|
|
->appendChild(
|
|
|
|
phutil_render_tag(
|
|
|
|
'pre',
|
|
|
|
array(
|
|
|
|
'class' => 'aphront-form-input',
|
|
|
|
),
|
|
|
|
phutil_escape_html($default)));
|
2012-12-28 00:20:09 +01:00
|
|
|
|
|
|
|
$title = pht('Edit %s', $this->key);
|
|
|
|
$short = pht('Edit');
|
|
|
|
|
2012-12-31 00:36:06 +01:00
|
|
|
$crumbs = $this->buildApplicationCrumbs();
|
|
|
|
$crumbs
|
|
|
|
->addCrumb(
|
|
|
|
id(new PhabricatorCrumbView())
|
|
|
|
->setName(pht('Config'))
|
|
|
|
->setHref($this->getApplicationURI()))
|
|
|
|
->addCrumb(
|
|
|
|
id(new PhabricatorCrumbView())
|
|
|
|
->setName($group->getName())
|
|
|
|
->setHref($group_uri))
|
|
|
|
->addCrumb(
|
|
|
|
id(new PhabricatorCrumbView())
|
|
|
|
->setName($this->key)
|
|
|
|
->setHref('/config/edit/'.$this->key));
|
2012-12-28 00:20:09 +01:00
|
|
|
|
|
|
|
return $this->buildApplicationPage(
|
|
|
|
array(
|
|
|
|
$crumbs,
|
|
|
|
id(new PhabricatorHeaderView())->setHeader($title),
|
|
|
|
$error_view,
|
|
|
|
$form,
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
'title' => $title,
|
|
|
|
'device' => true,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2013-01-01 23:09:17 +01:00
|
|
|
private function readRequest(
|
|
|
|
PhabricatorConfigOption $option,
|
|
|
|
PhabricatorConfigEntry $entry,
|
|
|
|
AphrontRequest $request) {
|
|
|
|
|
|
|
|
$e_value = null;
|
|
|
|
$errors = array();
|
|
|
|
|
|
|
|
$value = $request->getStr('value');
|
|
|
|
if (!strlen($value)) {
|
|
|
|
$value = null;
|
|
|
|
$entry->setValue($value);
|
|
|
|
$entry->setIsDeleted(true);
|
|
|
|
return array($e_value, $errors, $value);
|
|
|
|
} else {
|
|
|
|
$entry->setIsDeleted(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
$type = $option->getType();
|
|
|
|
switch ($type) {
|
|
|
|
case 'int':
|
|
|
|
if (preg_match('/^-?[0-9]+$/', trim($value))) {
|
|
|
|
$entry->setValue((int)$value);
|
|
|
|
} else {
|
|
|
|
$e_value = pht('Invalid');
|
|
|
|
$errors[] = pht('Value must be an integer.');
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'string':
|
|
|
|
break;
|
|
|
|
case 'bool':
|
|
|
|
switch ($value) {
|
|
|
|
case 'true':
|
|
|
|
$entry->setValue(true);
|
|
|
|
break;
|
|
|
|
case 'false':
|
|
|
|
$entry->setValue(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.');
|
|
|
|
$entry->setValue($json);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return array($e_value, $errors, $value);
|
|
|
|
}
|
|
|
|
|
|
|
|
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';
|
|
|
|
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;
|
|
|
|
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');
|
|
|
|
|
|
|
|
return $control;
|
|
|
|
}
|
|
|
|
|
2012-12-28 00:20:09 +01:00
|
|
|
}
|