Remove product literal strings in "pht()", part 15
[phabricator.git] / src / applications / config / controller / settings / PhabricatorConfigEditController.php
blob38c592efca252f32313b71096f15794df8795584
1 <?php
3 final class PhabricatorConfigEditController
4 extends PhabricatorConfigSettingsController {
6 public function handleRequest(AphrontRequest $request) {
7 $viewer = $request->getViewer();
8 $key = $request->getURIData('key');
10 $options = PhabricatorApplicationConfigOptions::loadAllOptions();
11 if (empty($options[$key])) {
12 $ancient = PhabricatorExtraConfigSetupCheck::getAncientConfig();
13 if (isset($ancient[$key])) {
14 $desc = pht(
15 "This configuration has been removed. You can safely delete ".
16 "it.\n\n%s",
17 $ancient[$key]);
18 } else {
19 $desc = pht(
20 'This configuration option is unknown. It may be misspelled, '.
21 'or have existed in a previous version of the software.');
24 // This may be a dead config entry, which existed in the past but no
25 // longer exists. Allow it to be edited so it can be reviewed and
26 // deleted.
27 $option = id(new PhabricatorConfigOption())
28 ->setKey($key)
29 ->setType('wild')
30 ->setDefault(null)
31 ->setDescription($desc);
32 $group = null;
33 } else {
34 $option = $options[$key];
35 $group = $option->getGroup();
38 $issue = $request->getStr('issue');
39 if ($issue) {
40 // If the user came here from an open setup issue, send them back.
41 $done_uri = $this->getApplicationURI('issue/'.$issue.'/');
42 } else {
43 $done_uri = $this->getApplicationURI('settings/');
46 // Check if the config key is already stored in the database.
47 // Grab the value if it is.
48 $config_entry = id(new PhabricatorConfigEntry())
49 ->loadOneWhere(
50 'configKey = %s AND namespace = %s',
51 $key,
52 'default');
53 if (!$config_entry) {
54 $config_entry = id(new PhabricatorConfigEntry())
55 ->setConfigKey($key)
56 ->setNamespace('default')
57 ->setIsDeleted(true);
58 $config_entry->setPHID($config_entry->generatePHID());
61 $e_value = null;
62 $errors = array();
63 if ($request->isFormPost() && !$option->getLocked()) {
65 $result = $this->readRequest(
66 $option,
67 $request);
69 list($e_value, $value_errors, $display_value, $xaction) = $result;
70 $errors = array_merge($errors, $value_errors);
72 if (!$errors) {
74 $editor = id(new PhabricatorConfigEditor())
75 ->setActor($viewer)
76 ->setContinueOnNoEffect(true)
77 ->setContentSourceFromRequest($request);
79 try {
80 $editor->applyTransactions($config_entry, array($xaction));
81 return id(new AphrontRedirectResponse())->setURI($done_uri);
82 } catch (PhabricatorConfigValidationException $ex) {
83 $e_value = pht('Invalid');
84 $errors[] = $ex->getMessage();
87 } else {
88 if ($config_entry->getIsDeleted()) {
89 $display_value = null;
90 } else {
91 $display_value = $this->getDisplayValue(
92 $option,
93 $config_entry,
94 $config_entry->getValue());
98 $form = id(new AphrontFormView())
99 ->setEncType('multipart/form-data');
101 $error_view = null;
102 if ($errors) {
103 $error_view = id(new PHUIInfoView())
104 ->setSeverity(PHUIInfoView::SEVERITY_ERROR)
105 ->setErrors($errors);
108 $doc_href = PhabricatorEnv::getDoclink(
109 'Configuration Guide: Locked and Hidden Configuration');
111 $doc_link = phutil_tag(
112 'a',
113 array(
114 'href' => $doc_href,
115 'target' => '_blank',
117 pht('Learn more about locked and hidden options.'));
119 $status_items = array();
120 $tag = null;
121 if ($option->getHidden()) {
122 $tag = id(new PHUITagView())
123 ->setName(pht('Hidden'))
124 ->setColor(PHUITagView::COLOR_GREY)
125 ->setBorder(PHUITagView::BORDER_NONE)
126 ->setType(PHUITagView::TYPE_SHADE);
128 $message = pht(
129 'This configuration is hidden and can not be edited or viewed from '.
130 'the web interface.');
131 $status_items[] = id(new PHUIInfoView())
132 ->appendChild(array($message, ' ', $doc_link));
133 } else if ($option->getLocked()) {
134 $tag = id(new PHUITagView())
135 ->setName(pht('Locked'))
136 ->setColor(PHUITagView::COLOR_RED)
137 ->setBorder(PHUITagView::BORDER_NONE)
138 ->setType(PHUITagView::TYPE_SHADE);
140 $message = $option->getLockedMessage();
141 $status_items[] = id(new PHUIInfoView())
142 ->appendChild(array($message, ' ', $doc_link));
145 if ($option->getHidden() || $option->getLocked()) {
146 $controls = array();
147 } else {
148 $controls = $this->renderControls(
149 $option,
150 $display_value,
151 $e_value);
154 $form
155 ->setUser($viewer)
156 ->addHiddenInput('issue', $request->getStr('issue'));
158 $description = $option->newDescriptionRemarkupView($viewer);
159 if ($description) {
160 $form->appendChild(
161 id(new AphrontFormMarkupControl())
162 ->setLabel(pht('Description'))
163 ->setValue($description));
166 if ($group) {
167 $extra = $group->renderContextualDescription(
168 $option,
169 $request);
170 if ($extra !== null) {
171 $form->appendChild(
172 id(new AphrontFormMarkupControl())
173 ->setValue($extra));
177 foreach ($controls as $control) {
178 $form->appendControl($control);
181 if (!$option->getLocked()) {
182 $form->appendChild(
183 id(new AphrontFormSubmitControl())
184 ->addCancelButton($done_uri)
185 ->setValue(pht('Save Config Entry')));
188 $current_config = null;
189 if (!$option->getHidden()) {
190 $current_config = $this->renderDefaults($option, $config_entry);
191 $current_config = $this->buildConfigBoxView(
192 pht('Current Configuration'),
193 $current_config);
196 $examples = $this->renderExamples($option);
197 if ($examples) {
198 $examples = $this->buildConfigBoxView(
199 pht('Examples'),
200 $examples);
203 $title = $key;
205 $box_header = array();
206 $box_header[] = $key;
208 $crumbs = $this->newCrumbs()
209 ->addTextCrumb($key, '/config/edit/'.$key);
211 $form_box = $this->buildConfigBoxView($box_header, $form, $tag);
213 $timeline = $this->buildTransactionTimeline(
214 $config_entry,
215 new PhabricatorConfigTransactionQuery());
216 $timeline->setShouldTerminate(true);
218 $header = $this->buildHeaderView($title);
220 $view = id(new PHUITwoColumnView())
221 ->setHeader($header)
222 ->setFooter(
223 array(
224 $error_view,
225 $form_box,
226 $status_items,
227 $examples,
228 $current_config,
231 return $this->newPage()
232 ->setTitle($title)
233 ->setCrumbs($crumbs)
234 ->appendChild($view);
237 private function readRequest(
238 PhabricatorConfigOption $option,
239 AphrontRequest $request) {
241 $type = $option->newOptionType();
242 if ($type) {
243 $is_set = $type->isValuePresentInRequest($option, $request);
244 if ($is_set) {
245 $value = $type->readValueFromRequest($option, $request);
247 $errors = array();
248 try {
249 $canonical_value = $type->newValueFromRequestValue(
250 $option,
251 $value);
252 $type->validateStoredValue($option, $canonical_value);
253 $xaction = $type->newTransaction($option, $canonical_value);
254 } catch (PhabricatorConfigValidationException $ex) {
255 $errors[] = $ex->getMessage();
256 $xaction = null;
257 } catch (Exception $ex) {
258 // NOTE: Some older validators throw bare exceptions. Purely in good
259 // taste, it would be nice to convert these at some point.
260 $errors[] = $ex->getMessage();
261 $xaction = null;
264 return array(
265 $errors ? pht('Invalid') : null,
266 $errors,
267 $value,
268 $xaction,
270 } else {
271 $delete_xaction = id(new PhabricatorConfigTransaction())
272 ->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT)
273 ->setNewValue(
274 array(
275 'deleted' => true,
276 'value' => null,
279 return array(
280 null,
281 array(),
282 null,
283 $delete_xaction,
288 // TODO: If we missed on the new `PhabricatorConfigType` map, fall back
289 // to the old semi-modular, semi-hacky way of doing things.
291 $xaction = new PhabricatorConfigTransaction();
292 $xaction->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT);
294 $e_value = null;
295 $errors = array();
297 if ($option->isCustomType()) {
298 $info = $option->getCustomObject()->readRequest($option, $request);
299 list($e_value, $errors, $set_value, $value) = $info;
300 } else {
301 throw new Exception(
302 pht(
303 'Unknown configuration option type "%s".',
304 $option->getType()));
307 if (!$errors) {
308 $xaction->setNewValue(
309 array(
310 'deleted' => false,
311 'value' => $set_value,
313 } else {
314 $xaction = null;
317 return array($e_value, $errors, $value, $xaction);
320 private function getDisplayValue(
321 PhabricatorConfigOption $option,
322 PhabricatorConfigEntry $entry,
323 $value) {
325 $type = $option->newOptionType();
326 if ($type) {
327 return $type->newDisplayValue($option, $value);
330 if ($option->isCustomType()) {
331 return $option->getCustomObject()->getDisplayValue(
332 $option,
333 $entry,
334 $value);
337 throw new Exception(
338 pht(
339 'Unknown configuration option type "%s".',
340 $option->getType()));
343 private function renderControls(
344 PhabricatorConfigOption $option,
345 $display_value,
346 $e_value) {
348 $type = $option->newOptionType();
349 if ($type) {
350 return $type->newControls(
351 $option,
352 $display_value,
353 $e_value);
356 if ($option->isCustomType()) {
357 $controls = $option->getCustomObject()->renderControls(
358 $option,
359 $display_value,
360 $e_value);
361 } else {
362 throw new Exception(
363 pht(
364 'Unknown configuration option type "%s".',
365 $option->getType()));
368 return $controls;
371 private function renderExamples(PhabricatorConfigOption $option) {
372 $examples = $option->getExamples();
373 if (!$examples) {
374 return null;
377 $table = array();
378 $table[] = phutil_tag('tr', array('class' => 'column-labels'), array(
379 phutil_tag('th', array(), pht('Example')),
380 phutil_tag('th', array(), pht('Value')),
382 foreach ($examples as $example) {
383 list($value, $description) = $example;
385 if ($value === null) {
386 $value = phutil_tag('em', array(), pht('(empty)'));
387 } else {
388 if (is_array($value)) {
389 $value = implode("\n", $value);
393 $table[] = phutil_tag('tr', array('class' => 'column-labels'), array(
394 phutil_tag('th', array(), $description),
395 phutil_tag('td', array(), $value),
399 require_celerity_resource('config-options-css');
401 return phutil_tag(
402 'table',
403 array(
404 'class' => 'config-option-table',
405 'cellspacing' => '0',
406 'cellpadding' => '0',
408 $table);
411 private function renderDefaults(
412 PhabricatorConfigOption $option,
413 PhabricatorConfigEntry $entry) {
415 $stack = PhabricatorEnv::getConfigSourceStack();
416 $stack = $stack->getStack();
418 $table = array();
419 $table[] = phutil_tag('tr', array('class' => 'column-labels'), array(
420 phutil_tag('th', array(), pht('Source')),
421 phutil_tag('th', array(), pht('Value')),
424 $is_effective_value = true;
425 foreach ($stack as $key => $source) {
426 $row_classes = array(
427 'column-labels',
430 $value = $source->getKeys(
431 array(
432 $option->getKey(),
435 if (!array_key_exists($option->getKey(), $value)) {
436 $value = phutil_tag('em', array(), pht('(No Value Configured)'));
437 } else {
438 $value = $this->getDisplayValue(
439 $option,
440 $entry,
441 $value[$option->getKey()]);
443 if ($is_effective_value) {
444 $is_effective_value = false;
445 $row_classes[] = 'config-options-effective-value';
449 $table[] = phutil_tag(
450 'tr',
451 array(
452 'class' => implode(' ', $row_classes),
454 array(
455 phutil_tag('th', array(), $source->getName()),
456 phutil_tag('td', array(), $value),
460 require_celerity_resource('config-options-css');
462 return phutil_tag(
463 'table',
464 array(
465 'class' => 'config-option-table',
466 'cellspacing' => '0',
467 'cellpadding' => '0',
469 $table);