diff --git a/conf/default.conf.php b/conf/default.conf.php
index 8b81ceb8a3..37d1db1f6a 100644
--- a/conf/default.conf.php
+++ b/conf/default.conf.php
@@ -180,9 +180,6 @@ return array(
// (e.g., db.example.com:1234).
'mysql.host' => 'localhost',
- // The number of times to try reconnecting to the MySQL database
- 'mysql.connection-retries' => 3,
-
// Phabricator supports PHP extensions MySQL and MySQLi. It is possible to
// implement also other access mechanism (e.g. PDO_MySQL). The class must
// extend AphrontMySQLDatabaseConnectionBase.
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index 55bbad9e3d..9c12935261 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -938,6 +938,7 @@ phutil_register_library_map(array(
'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/PhabricatorMetaMTAViewController.php',
'PhabricatorMetaMTAWorker' => 'applications/metamta/PhabricatorMetaMTAWorker.php',
'PhabricatorMustVerifyEmailController' => 'applications/auth/controller/PhabricatorMustVerifyEmailController.php',
+ 'PhabricatorMySQLConfigOptions' => 'applications/config/option/PhabricatorMySQLConfigOptions.php',
'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/PhabricatorMySQLFileStorageEngine.php',
'PhabricatorNotificationBuilder' => 'applications/notification/builder/PhabricatorNotificationBuilder.php',
'PhabricatorNotificationClearController' => 'applications/notification/controller/PhabricatorNotificationClearController.php',
@@ -2262,6 +2263,7 @@ phutil_register_library_map(array(
'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController',
'PhabricatorMetaMTAWorker' => 'PhabricatorWorker',
'PhabricatorMustVerifyEmailController' => 'PhabricatorAuthController',
+ 'PhabricatorMySQLConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine',
'PhabricatorNotificationClearController' => 'PhabricatorNotificationController',
'PhabricatorNotificationController' => 'PhabricatorController',
diff --git a/src/applications/config/controller/PhabricatorConfigEditController.php b/src/applications/config/controller/PhabricatorConfigEditController.php
index c0753e25f1..14c88b2c9a 100644
--- a/src/applications/config/controller/PhabricatorConfigEditController.php
+++ b/src/applications/config/controller/PhabricatorConfigEditController.php
@@ -100,6 +100,15 @@ final class PhabricatorConfigEditController
$error_view = id(new AphrontErrorView())
->setTitle(pht('You broke everything!'))
->setErrors($errors);
+ } else if ($option->getHidden()) {
+ $msg = pht(
+ "This configuration is hidden and can not be edited or viewed from ".
+ "the web interface.");
+
+ $error_view = id(new AphrontErrorView())
+ ->setTitle(pht('Configuration Hidden'))
+ ->setSeverity(AphrontErrorView::SEVERITY_WARNING)
+ ->appendChild('
'.phutil_escape_html($msg).'
');
} else if ($option->getLocked()) {
$msg = pht(
"This configuration is locked and can not be edited from the web ".
@@ -111,10 +120,14 @@ final class PhabricatorConfigEditController
->appendChild(''.phutil_escape_html($msg).'
');
}
- $control = $this->renderControl(
- $option,
- $display_value,
- $e_value);
+ if ($option->getHidden()) {
+ $control = null;
+ } else {
+ $control = $this->renderControl(
+ $option,
+ $display_value,
+ $e_value);
+ }
$engine = new PhabricatorMarkupEngine();
$engine->addObject($option, 'description');
@@ -135,14 +148,15 @@ final class PhabricatorConfigEditController
->setValue($description))
->appendChild($control);
+ $submit_control = id(new AphrontFormSubmitControl())
+ ->addCancelButton($done_uri);
+
if (!$option->getLocked()) {
- $form
- ->appendChild(
- id(new AphrontFormSubmitControl())
- ->addCancelButton($done_uri)
- ->setValue(pht('Save Config Entry')));
+ $submit_control->setValue(pht('Save Config Entry'));
}
+ $form->appendChild($submit_control);
+
$examples = $this->renderExamples($option);
if ($examples) {
$form->appendChild(
@@ -151,10 +165,12 @@ final class PhabricatorConfigEditController
->setValue($examples));
}
- $form->appendChild(
- id(new AphrontFormMarkupControl())
- ->setLabel(pht('Default'))
- ->setValue($this->renderDefaults($option)));
+ if (!$option->getHidden()) {
+ $form->appendChild(
+ id(new AphrontFormMarkupControl())
+ ->setLabel(pht('Default'))
+ ->setValue($this->renderDefaults($option)));
+ }
$title = pht('Edit %s', $this->key);
$short = pht('Edit');
@@ -256,6 +272,20 @@ final class PhabricatorConfigEditController
break;
}
break;
+ case 'class':
+ if (!class_exists($value)) {
+ $e_value = pht('Invalid');
+ $errors[] = pht('Class does not exist.');
+ } else {
+ $base = $option->getBaseClass();
+ if (!is_subclass_of($value, $base)) {
+ $e_value = pht('Invalid');
+ $errors[] = pht('Class is not of valid type.');
+ } else {
+ $set_value = $value;
+ }
+ }
+ break;
default:
$json = json_decode($value, true);
if ($json === null && strtolower($value) != 'null') {
@@ -319,11 +349,26 @@ final class PhabricatorConfigEditController
$control = id(new AphrontFormSelectControl())
->setOptions(
array(
- '' => '(Use Default)',
+ '' => pht('(Use Default)'),
'true' => idx($option->getOptions(), 0),
'false' => idx($option->getOptions(), 1),
));
break;
+ case 'class':
+ $symbols = id(new PhutilSymbolLoader())
+ ->setType('class')
+ ->setAncestorClass($option->getBaseClass())
+ ->setConcreteOnly(true)
+ ->selectSymbolsWithoutLoading();
+ $names = ipull($symbols, 'name', 'name');
+ sort($names);
+ $names = array(
+ '' => pht('(Use Default)'),
+ ) + $names;
+
+ $control = id(new AphrontFormSelectControl())
+ ->setOptions($names);
+ break;
case 'list':
$control = id(new AphrontFormTextAreaControl())
->setCaption(pht('Separate values with newlines or commas.'));
diff --git a/src/applications/config/controller/PhabricatorConfigGroupController.php b/src/applications/config/controller/PhabricatorConfigGroupController.php
index def0510ca4..1d5042b2fd 100644
--- a/src/applications/config/controller/PhabricatorConfigGroupController.php
+++ b/src/applications/config/controller/PhabricatorConfigGroupController.php
@@ -67,22 +67,24 @@ final class PhabricatorConfigGroupController
$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',
- ),
- ''.pht('Current Value:').' '.
- 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);
+ ->addAttribute(phutil_escape_html($option->getSummary()));
+
+ if (!$option->getHidden()) {
+ $current_value = PhabricatorEnv::getEnvConfig($option->getKey());
+ $current_value = $this->prettyPrintJSON($current_value);
+ $current_value = phutil_render_tag(
+ 'div',
+ array(
+ 'class' => 'config-options-current-value',
+ ),
+ ''.pht('Current Value:').' '.
+ phutil_escape_html($current_value));
+
+ $item->appendChild($current_value);
+ }
$db_value = idx($db_values, $option->getKey());
if ($db_value && !$db_value->getIsDeleted()) {
@@ -91,7 +93,9 @@ final class PhabricatorConfigGroupController
$item->addIcon('edit-grey', pht('Default'));
}
- if ($option->getLocked()) {
+ if ($option->getHidden()) {
+ $item->addIcon('unpublish', pht('Hidden'));
+ } else if ($option->getLocked()) {
$item->addIcon('lock', pht('Locked'));
}
diff --git a/src/applications/config/option/PhabricatorApplicationConfigOptions.php b/src/applications/config/option/PhabricatorApplicationConfigOptions.php
index 21e23b782b..9bd6d402db 100644
--- a/src/applications/config/option/PhabricatorApplicationConfigOptions.php
+++ b/src/applications/config/option/PhabricatorApplicationConfigOptions.php
@@ -41,6 +41,21 @@ abstract class PhabricatorApplicationConfigOptions extends Phobject {
$option->getKey()));
}
break;
+ case 'class':
+ $symbols = id(new PhutilSymbolLoader())
+ ->setType('class')
+ ->setAncestorClass($option->getBaseClass())
+ ->setConcreteOnly(true)
+ ->selectSymbolsWithoutLoading();
+ $names = ipull($symbols, 'name', 'name');
+ if (empty($names[$value])) {
+ throw new PhabricatorConfigValidationException(
+ pht(
+ "Option '%s' value must name a class extending '%s'.",
+ $option->getKey(),
+ $option->getBaseClass()));
+ }
+ break;
case 'list':
$valid = true;
if (!is_array($value)) {
diff --git a/src/applications/config/option/PhabricatorConfigOption.php b/src/applications/config/option/PhabricatorConfigOption.php
index 7827626e36..e80a92ac83 100644
--- a/src/applications/config/option/PhabricatorConfigOption.php
+++ b/src/applications/config/option/PhabricatorConfigOption.php
@@ -13,6 +13,39 @@ final class PhabricatorConfigOption
private $group;
private $examples;
private $locked;
+ private $hidden;
+ private $masked;
+ private $baseClass;
+
+ public function setBaseClass($base_class) {
+ $this->baseClass = $base_class;
+ return $this;
+ }
+
+ public function getBaseClass() {
+ return $this->baseClass;
+ }
+
+ public function setMasked($masked) {
+ $this->masked = $masked;
+ return $this;
+ }
+
+ public function getMasked() {
+ if ($this->getHidden()) {
+ return true;
+ }
+ return $this->masked;
+ }
+
+ public function setHidden($hidden) {
+ $this->hidden = $hidden;
+ return $this;
+ }
+
+ public function getHidden() {
+ return $this->hidden;
+ }
public function setLocked($locked) {
$this->locked = $locked;
@@ -20,6 +53,9 @@ final class PhabricatorConfigOption
}
public function getLocked() {
+ if ($this->getHidden()) {
+ return true;
+ }
return $this->locked;
}
diff --git a/src/applications/config/option/PhabricatorCoreConfigOptions.php b/src/applications/config/option/PhabricatorCoreConfigOptions.php
index d8490dcf6d..d00656a27b 100644
--- a/src/applications/config/option/PhabricatorCoreConfigOptions.php
+++ b/src/applications/config/option/PhabricatorCoreConfigOptions.php
@@ -65,19 +65,6 @@ final class PhabricatorCoreConfigOptions
"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.")),
$this->newOption('environment.append-paths', 'list', null)
->setSummary(
pht("These paths get appended to your \$PATH envrionment variable."))
diff --git a/src/applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php b/src/applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php
index 235b231ab5..d168b4d240 100644
--- a/src/applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php
+++ b/src/applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php
@@ -15,7 +15,7 @@ final class PhabricatorExtendingPhabricatorConfigOptions
return array(
$this->newOption('load-libraries', 'list', null)
->setSummary(pht("Paths to additional phutil libraries to load."))
- ->addExample('/srv/our-sekrit-libs/sekrit-phutil', 'Valid Setting'),
+ ->addExample('/srv/our-libs/sekrit-phutil', pht('Valid Setting')),
$this->newOption('events.listeners', 'list', null)
->setSummary(
pht("Listeners receive callbacks when interesting things occur."))
@@ -25,7 +25,7 @@ final class PhabricatorExtendingPhabricatorConfigOptions
"listeners, which will receive callbacks when interesting things ".
"occur. Specify a list of classes which extend ".
"PhabricatorEventListener here."))
- ->addExample('MyEventListener', 'Valid Setting'),
+ ->addExample('MyEventListener', pht('Valid Setting')),
$this->newOption(
'celerity.resource-path',
'string',
@@ -36,7 +36,7 @@ final class PhabricatorExtendingPhabricatorConfigOptions
pht(
"Path to custom celerity resource map relative to ".
"'phabricator/src'. See also `scripts/celerity_mapper.php`."))
- ->addExample('local/my_celerity_map.php', 'Valid Setting'),
+ ->addExample('local/my_celerity_map.php', pht('Valid Setting')),
);
}
diff --git a/src/applications/config/option/PhabricatorMySQLConfigOptions.php b/src/applications/config/option/PhabricatorMySQLConfigOptions.php
new file mode 100644
index 0000000000..05741f4ba6
--- /dev/null
+++ b/src/applications/config/option/PhabricatorMySQLConfigOptions.php
@@ -0,0 +1,73 @@
+newOption('mysql.host', 'string', 'localhost')
+ ->setLocked(true)
+ ->setDescription(
+ pht("MySQL database hostname."))
+ ->addExample('localhost', pht('MySQL on this machine'))
+ ->addExample('db.example.com:3300', pht('Nonstandard port')),
+ $this->newOption('mysql.user', 'string', 'root')
+ ->setLocked(true)
+ ->setDescription(
+ pht("MySQL username to use when connecting to the database.")),
+ $this->newOption('mysql.pass', 'string', null)
+ ->setHidden(true)
+ ->setDescription(
+ pht("MySQL password to use when connecting to the database.")),
+ $this->newOption(
+ 'mysql.configuration-provider',
+ 'class',
+ 'DefaultDatabaseConfigurationProvider')
+ ->setLocked(true)
+ ->setBaseClass('DatabaseConfigurationProvider')
+ ->setSummary(
+ pht('Configure database configuration class.'))
+ ->setDescription(
+ pht(
+ "Phabricator chooses which database to connect to through a ".
+ "swappable configuration provider. You almost certainly do not ".
+ "need to change this.")),
+ $this->newOption(
+ 'mysql.implementation',
+ 'class',
+ 'AphrontMySQLDatabaseConnection')
+ ->setLocked(true)
+ ->setBaseClass('AphrontMySQLDatabaseConnectionBase')
+ ->setSummary(
+ pht('Configure database connection class.'))
+ ->setDescription(
+ pht(
+ "Phabricator connects to MySQL through a swappable abstraction ".
+ "layer. You can choose an alternate implementation by setting ".
+ "this option. To provide your own implementation, extend ".
+ "`AphrontMySQLDatabaseConnectionBase`. It is very unlikely that ".
+ "you need to change this.")),
+ $this->newOption('storage.default-namespace', 'string', 'phabricator')
+ ->setLocked(true)
+ ->setSummary(
+ pht("The namespace that Phabricator databases should use."))
+ ->setDescription(
+ pht(
+ "Phabricator puts databases in a namespace, which defaults 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.")),
+ );
+ }
+
+}
diff --git a/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php b/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php
index 13092ca3fc..0158d18b10 100644
--- a/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php
+++ b/src/infrastructure/storage/lisk/PhabricatorLiskDAO.php
@@ -103,7 +103,6 @@ abstract class PhabricatorLiskDAO extends LiskDAO {
'mysql.configuration-provider',
array($this, $mode, $namespace));
- $retries = PhabricatorEnv::getEnvConfig('mysql.connection-retries');
return PhabricatorEnv::newObjectFromConfig(
'mysql.implementation',
array(
@@ -112,7 +111,7 @@ abstract class PhabricatorLiskDAO extends LiskDAO {
'pass' => $conf->getPassword(),
'host' => $conf->getHost(),
'database' => $conf->getDatabase(),
- 'retries' => $retries,
+ 'retries' => 3,
),
));
}