@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Port MySQL settings to PHP

Summary:
- Ports MySQL settings to PHP.
- Removes "mysql.retries" -- this existed only because Magic Numbers Are Bad, but there is no concievable reason it should ever be set to anything other than 3.
- Introduced "Hidden" config, which isn't visible from the web (for SaaS, we'll just mark anything with secret keys as "hidden").
- Introduced "Masked" config, which will be masked in darkconsole once that gets updated.
- "Hidden" implies "Masked" and "Locked".
- Moved "storage.default-namespace" here -- it probably makes more sense than core; this was my bad in T2255.
- Put cancel button back for hidden/locked config.
- Introduce 'class' config type.

Test Plan: Viewed MySQL options. None are editable.

Reviewers: codeblock, btrahan

Reviewed By: codeblock

CC: aran

Maniphest Tasks: T2255

Differential Revision: https://secure.phabricator.com/D4326

+207 -49
-3
conf/default.conf.php
··· 180 180 // (e.g., db.example.com:1234). 181 181 'mysql.host' => 'localhost', 182 182 183 - // The number of times to try reconnecting to the MySQL database 184 - 'mysql.connection-retries' => 3, 185 - 186 183 // Phabricator supports PHP extensions MySQL and MySQLi. It is possible to 187 184 // implement also other access mechanism (e.g. PDO_MySQL). The class must 188 185 // extend AphrontMySQLDatabaseConnectionBase.
+2
src/__phutil_library_map__.php
··· 938 938 'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/PhabricatorMetaMTAViewController.php', 939 939 'PhabricatorMetaMTAWorker' => 'applications/metamta/PhabricatorMetaMTAWorker.php', 940 940 'PhabricatorMustVerifyEmailController' => 'applications/auth/controller/PhabricatorMustVerifyEmailController.php', 941 + 'PhabricatorMySQLConfigOptions' => 'applications/config/option/PhabricatorMySQLConfigOptions.php', 941 942 'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/PhabricatorMySQLFileStorageEngine.php', 942 943 'PhabricatorNotificationBuilder' => 'applications/notification/builder/PhabricatorNotificationBuilder.php', 943 944 'PhabricatorNotificationClearController' => 'applications/notification/controller/PhabricatorNotificationClearController.php', ··· 2262 2263 'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController', 2263 2264 'PhabricatorMetaMTAWorker' => 'PhabricatorWorker', 2264 2265 'PhabricatorMustVerifyEmailController' => 'PhabricatorAuthController', 2266 + 'PhabricatorMySQLConfigOptions' => 'PhabricatorApplicationConfigOptions', 2265 2267 'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine', 2266 2268 'PhabricatorNotificationClearController' => 'PhabricatorNotificationController', 2267 2269 'PhabricatorNotificationController' => 'PhabricatorController',
+59 -14
src/applications/config/controller/PhabricatorConfigEditController.php
··· 100 100 $error_view = id(new AphrontErrorView()) 101 101 ->setTitle(pht('You broke everything!')) 102 102 ->setErrors($errors); 103 + } else if ($option->getHidden()) { 104 + $msg = pht( 105 + "This configuration is hidden and can not be edited or viewed from ". 106 + "the web interface."); 107 + 108 + $error_view = id(new AphrontErrorView()) 109 + ->setTitle(pht('Configuration Hidden')) 110 + ->setSeverity(AphrontErrorView::SEVERITY_WARNING) 111 + ->appendChild('<p>'.phutil_escape_html($msg).'</p>'); 103 112 } else if ($option->getLocked()) { 104 113 $msg = pht( 105 114 "This configuration is locked and can not be edited from the web ". ··· 111 120 ->appendChild('<p>'.phutil_escape_html($msg).'</p>'); 112 121 } 113 122 114 - $control = $this->renderControl( 115 - $option, 116 - $display_value, 117 - $e_value); 123 + if ($option->getHidden()) { 124 + $control = null; 125 + } else { 126 + $control = $this->renderControl( 127 + $option, 128 + $display_value, 129 + $e_value); 130 + } 118 131 119 132 $engine = new PhabricatorMarkupEngine(); 120 133 $engine->addObject($option, 'description'); ··· 135 148 ->setValue($description)) 136 149 ->appendChild($control); 137 150 151 + $submit_control = id(new AphrontFormSubmitControl()) 152 + ->addCancelButton($done_uri); 153 + 138 154 if (!$option->getLocked()) { 139 - $form 140 - ->appendChild( 141 - id(new AphrontFormSubmitControl()) 142 - ->addCancelButton($done_uri) 143 - ->setValue(pht('Save Config Entry'))); 155 + $submit_control->setValue(pht('Save Config Entry')); 144 156 } 157 + 158 + $form->appendChild($submit_control); 145 159 146 160 $examples = $this->renderExamples($option); 147 161 if ($examples) { ··· 151 165 ->setValue($examples)); 152 166 } 153 167 154 - $form->appendChild( 155 - id(new AphrontFormMarkupControl()) 156 - ->setLabel(pht('Default')) 157 - ->setValue($this->renderDefaults($option))); 168 + if (!$option->getHidden()) { 169 + $form->appendChild( 170 + id(new AphrontFormMarkupControl()) 171 + ->setLabel(pht('Default')) 172 + ->setValue($this->renderDefaults($option))); 173 + } 158 174 159 175 $title = pht('Edit %s', $this->key); 160 176 $short = pht('Edit'); ··· 256 272 break; 257 273 } 258 274 break; 275 + case 'class': 276 + if (!class_exists($value)) { 277 + $e_value = pht('Invalid'); 278 + $errors[] = pht('Class does not exist.'); 279 + } else { 280 + $base = $option->getBaseClass(); 281 + if (!is_subclass_of($value, $base)) { 282 + $e_value = pht('Invalid'); 283 + $errors[] = pht('Class is not of valid type.'); 284 + } else { 285 + $set_value = $value; 286 + } 287 + } 288 + break; 259 289 default: 260 290 $json = json_decode($value, true); 261 291 if ($json === null && strtolower($value) != 'null') { ··· 319 349 $control = id(new AphrontFormSelectControl()) 320 350 ->setOptions( 321 351 array( 322 - '' => '(Use Default)', 352 + '' => pht('(Use Default)'), 323 353 'true' => idx($option->getOptions(), 0), 324 354 'false' => idx($option->getOptions(), 1), 325 355 )); 356 + break; 357 + case 'class': 358 + $symbols = id(new PhutilSymbolLoader()) 359 + ->setType('class') 360 + ->setAncestorClass($option->getBaseClass()) 361 + ->setConcreteOnly(true) 362 + ->selectSymbolsWithoutLoading(); 363 + $names = ipull($symbols, 'name', 'name'); 364 + sort($names); 365 + $names = array( 366 + '' => pht('(Use Default)'), 367 + ) + $names; 368 + 369 + $control = id(new AphrontFormSelectControl()) 370 + ->setOptions($names); 326 371 break; 327 372 case 'list<string>': 328 373 $control = id(new AphrontFormTextAreaControl())
+18 -14
src/applications/config/controller/PhabricatorConfigGroupController.php
··· 67 67 68 68 $list = new PhabricatorObjectItemListView(); 69 69 foreach ($options as $option) { 70 - $current_value = PhabricatorEnv::getEnvConfig($option->getKey()); 71 - $current_value = $this->prettyPrintJSON($current_value); 72 - $current_value = phutil_render_tag( 73 - 'div', 74 - array( 75 - 'class' => 'config-options-current-value', 76 - ), 77 - '<span>'.pht('Current Value:').'</span> '. 78 - phutil_escape_html($current_value)); 79 - 80 - 81 70 $item = id(new PhabricatorObjectItemView()) 82 71 ->setHeader($option->getKey()) 83 72 ->setHref('/config/edit/'.$option->getKey().'/') 84 - ->addAttribute(phutil_escape_html($option->getSummary())) 85 - ->appendChild($current_value); 73 + ->addAttribute(phutil_escape_html($option->getSummary())); 74 + 75 + if (!$option->getHidden()) { 76 + $current_value = PhabricatorEnv::getEnvConfig($option->getKey()); 77 + $current_value = $this->prettyPrintJSON($current_value); 78 + $current_value = phutil_render_tag( 79 + 'div', 80 + array( 81 + 'class' => 'config-options-current-value', 82 + ), 83 + '<span>'.pht('Current Value:').'</span> '. 84 + phutil_escape_html($current_value)); 85 + 86 + $item->appendChild($current_value); 87 + } 86 88 87 89 $db_value = idx($db_values, $option->getKey()); 88 90 if ($db_value && !$db_value->getIsDeleted()) { ··· 91 93 $item->addIcon('edit-grey', pht('Default')); 92 94 } 93 95 94 - if ($option->getLocked()) { 96 + if ($option->getHidden()) { 97 + $item->addIcon('unpublish', pht('Hidden')); 98 + } else if ($option->getLocked()) { 95 99 $item->addIcon('lock', pht('Locked')); 96 100 } 97 101
+15
src/applications/config/option/PhabricatorApplicationConfigOptions.php
··· 41 41 $option->getKey())); 42 42 } 43 43 break; 44 + case 'class': 45 + $symbols = id(new PhutilSymbolLoader()) 46 + ->setType('class') 47 + ->setAncestorClass($option->getBaseClass()) 48 + ->setConcreteOnly(true) 49 + ->selectSymbolsWithoutLoading(); 50 + $names = ipull($symbols, 'name', 'name'); 51 + if (empty($names[$value])) { 52 + throw new PhabricatorConfigValidationException( 53 + pht( 54 + "Option '%s' value must name a class extending '%s'.", 55 + $option->getKey(), 56 + $option->getBaseClass())); 57 + } 58 + break; 44 59 case 'list<string>': 45 60 $valid = true; 46 61 if (!is_array($value)) {
+36
src/applications/config/option/PhabricatorConfigOption.php
··· 13 13 private $group; 14 14 private $examples; 15 15 private $locked; 16 + private $hidden; 17 + private $masked; 18 + private $baseClass; 19 + 20 + public function setBaseClass($base_class) { 21 + $this->baseClass = $base_class; 22 + return $this; 23 + } 24 + 25 + public function getBaseClass() { 26 + return $this->baseClass; 27 + } 28 + 29 + public function setMasked($masked) { 30 + $this->masked = $masked; 31 + return $this; 32 + } 33 + 34 + public function getMasked() { 35 + if ($this->getHidden()) { 36 + return true; 37 + } 38 + return $this->masked; 39 + } 40 + 41 + public function setHidden($hidden) { 42 + $this->hidden = $hidden; 43 + return $this; 44 + } 45 + 46 + public function getHidden() { 47 + return $this->hidden; 48 + } 16 49 17 50 public function setLocked($locked) { 18 51 $this->locked = $locked; ··· 20 53 } 21 54 22 55 public function getLocked() { 56 + if ($this->getHidden()) { 57 + return true; 58 + } 23 59 return $this->locked; 24 60 } 25 61
-13
src/applications/config/option/PhabricatorCoreConfigOptions.php
··· 65 65 "and a call to 'Leap Into Action'. If you'd prefer more ". 66 66 "traditional UI strings like 'Submit', you can set this flag to ". 67 67 "disable most of the jokes and easter eggs.")), 68 - $this->newOption('storage.default-namespace', 'string', 'phabricator') 69 - // NOTE: Lock this, since editing it from the web torpedoes an install. 70 - ->setLocked(true) 71 - ->setSummary( 72 - pht("The namespace that Phabricator databases should use.")) 73 - ->setDescription( 74 - pht( 75 - "Phabricator puts databases in a namespace, which defualts to ". 76 - "'phabricator' -- for instance, the Differential database is ". 77 - "named 'phabricator_differential' by default. You can change ". 78 - "this namespace if you want. Normally, you should not do this ". 79 - "unless you are developing Phabricator and using namespaces to ". 80 - "separate multiple sandbox datasets.")), 81 68 $this->newOption('environment.append-paths', 'list<string>', null) 82 69 ->setSummary( 83 70 pht("These paths get appended to your \$PATH envrionment variable."))
+3 -3
src/applications/config/option/PhabricatorExtendingPhabricatorConfigOptions.php
··· 15 15 return array( 16 16 $this->newOption('load-libraries', 'list<string>', null) 17 17 ->setSummary(pht("Paths to additional phutil libraries to load.")) 18 - ->addExample('/srv/our-sekrit-libs/sekrit-phutil', 'Valid Setting'), 18 + ->addExample('/srv/our-libs/sekrit-phutil', pht('Valid Setting')), 19 19 $this->newOption('events.listeners', 'list<string>', null) 20 20 ->setSummary( 21 21 pht("Listeners receive callbacks when interesting things occur.")) ··· 25 25 "listeners, which will receive callbacks when interesting things ". 26 26 "occur. Specify a list of classes which extend ". 27 27 "PhabricatorEventListener here.")) 28 - ->addExample('MyEventListener', 'Valid Setting'), 28 + ->addExample('MyEventListener', pht('Valid Setting')), 29 29 $this->newOption( 30 30 'celerity.resource-path', 31 31 'string', ··· 36 36 pht( 37 37 "Path to custom celerity resource map relative to ". 38 38 "'phabricator/src'. See also `scripts/celerity_mapper.php`.")) 39 - ->addExample('local/my_celerity_map.php', 'Valid Setting'), 39 + ->addExample('local/my_celerity_map.php', pht('Valid Setting')), 40 40 ); 41 41 } 42 42
+73
src/applications/config/option/PhabricatorMySQLConfigOptions.php
··· 1 + <?php 2 + 3 + final class PhabricatorMySQLConfigOptions 4 + extends PhabricatorApplicationConfigOptions { 5 + 6 + public function getName() { 7 + return pht("MySQL"); 8 + } 9 + 10 + public function getDescription() { 11 + return pht("Database configuration."); 12 + } 13 + 14 + public function getOptions() { 15 + return array( 16 + $this->newOption('mysql.host', 'string', 'localhost') 17 + ->setLocked(true) 18 + ->setDescription( 19 + pht("MySQL database hostname.")) 20 + ->addExample('localhost', pht('MySQL on this machine')) 21 + ->addExample('db.example.com:3300', pht('Nonstandard port')), 22 + $this->newOption('mysql.user', 'string', 'root') 23 + ->setLocked(true) 24 + ->setDescription( 25 + pht("MySQL username to use when connecting to the database.")), 26 + $this->newOption('mysql.pass', 'string', null) 27 + ->setHidden(true) 28 + ->setDescription( 29 + pht("MySQL password to use when connecting to the database.")), 30 + $this->newOption( 31 + 'mysql.configuration-provider', 32 + 'class', 33 + 'DefaultDatabaseConfigurationProvider') 34 + ->setLocked(true) 35 + ->setBaseClass('DatabaseConfigurationProvider') 36 + ->setSummary( 37 + pht('Configure database configuration class.')) 38 + ->setDescription( 39 + pht( 40 + "Phabricator chooses which database to connect to through a ". 41 + "swappable configuration provider. You almost certainly do not ". 42 + "need to change this.")), 43 + $this->newOption( 44 + 'mysql.implementation', 45 + 'class', 46 + 'AphrontMySQLDatabaseConnection') 47 + ->setLocked(true) 48 + ->setBaseClass('AphrontMySQLDatabaseConnectionBase') 49 + ->setSummary( 50 + pht('Configure database connection class.')) 51 + ->setDescription( 52 + pht( 53 + "Phabricator connects to MySQL through a swappable abstraction ". 54 + "layer. You can choose an alternate implementation by setting ". 55 + "this option. To provide your own implementation, extend ". 56 + "`AphrontMySQLDatabaseConnectionBase`. It is very unlikely that ". 57 + "you need to change this.")), 58 + $this->newOption('storage.default-namespace', 'string', 'phabricator') 59 + ->setLocked(true) 60 + ->setSummary( 61 + pht("The namespace that Phabricator databases should use.")) 62 + ->setDescription( 63 + pht( 64 + "Phabricator puts databases in a namespace, which defaults to ". 65 + "'phabricator' -- for instance, the Differential database is ". 66 + "named 'phabricator_differential' by default. You can change ". 67 + "this namespace if you want. Normally, you should not do this ". 68 + "unless you are developing Phabricator and using namespaces to ". 69 + "separate multiple sandbox datasets.")), 70 + ); 71 + } 72 + 73 + }
+1 -2
src/infrastructure/storage/lisk/PhabricatorLiskDAO.php
··· 103 103 'mysql.configuration-provider', 104 104 array($this, $mode, $namespace)); 105 105 106 - $retries = PhabricatorEnv::getEnvConfig('mysql.connection-retries'); 107 106 return PhabricatorEnv::newObjectFromConfig( 108 107 'mysql.implementation', 109 108 array( ··· 112 111 'pass' => $conf->getPassword(), 113 112 'host' => $conf->getHost(), 114 113 'database' => $conf->getDatabase(), 115 - 'retries' => $retries, 114 + 'retries' => 3, 116 115 ), 117 116 )); 118 117 }