Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/config/controller/settings/PhabricatorConfigEditController.php
12261 views
1
<?php
2
3
final class PhabricatorConfigEditController
4
extends PhabricatorConfigSettingsController {
5
6
public function handleRequest(AphrontRequest $request) {
7
$viewer = $request->getViewer();
8
$key = $request->getURIData('key');
9
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.');
22
}
23
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();
36
}
37
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/');
44
}
45
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());
59
}
60
61
$e_value = null;
62
$errors = array();
63
if ($request->isFormPost() && !$option->getLocked()) {
64
65
$result = $this->readRequest(
66
$option,
67
$request);
68
69
list($e_value, $value_errors, $display_value, $xaction) = $result;
70
$errors = array_merge($errors, $value_errors);
71
72
if (!$errors) {
73
74
$editor = id(new PhabricatorConfigEditor())
75
->setActor($viewer)
76
->setContinueOnNoEffect(true)
77
->setContentSourceFromRequest($request);
78
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();
85
}
86
}
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());
95
}
96
}
97
98
$form = id(new AphrontFormView())
99
->setEncType('multipart/form-data');
100
101
$error_view = null;
102
if ($errors) {
103
$error_view = id(new PHUIInfoView())
104
->setSeverity(PHUIInfoView::SEVERITY_ERROR)
105
->setErrors($errors);
106
}
107
108
$doc_href = PhabricatorEnv::getDoclink(
109
'Configuration Guide: Locked and Hidden Configuration');
110
111
$doc_link = phutil_tag(
112
'a',
113
array(
114
'href' => $doc_href,
115
'target' => '_blank',
116
),
117
pht('Learn more about locked and hidden options.'));
118
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);
127
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);
139
140
$message = $option->getLockedMessage();
141
$status_items[] = id(new PHUIInfoView())
142
->appendChild(array($message, ' ', $doc_link));
143
}
144
145
if ($option->getHidden() || $option->getLocked()) {
146
$controls = array();
147
} else {
148
$controls = $this->renderControls(
149
$option,
150
$display_value,
151
$e_value);
152
}
153
154
$form
155
->setUser($viewer)
156
->addHiddenInput('issue', $request->getStr('issue'));
157
158
$description = $option->newDescriptionRemarkupView($viewer);
159
if ($description) {
160
$form->appendChild(
161
id(new AphrontFormMarkupControl())
162
->setLabel(pht('Description'))
163
->setValue($description));
164
}
165
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));
174
}
175
}
176
177
foreach ($controls as $control) {
178
$form->appendControl($control);
179
}
180
181
if (!$option->getLocked()) {
182
$form->appendChild(
183
id(new AphrontFormSubmitControl())
184
->addCancelButton($done_uri)
185
->setValue(pht('Save Config Entry')));
186
}
187
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);
194
}
195
196
$examples = $this->renderExamples($option);
197
if ($examples) {
198
$examples = $this->buildConfigBoxView(
199
pht('Examples'),
200
$examples);
201
}
202
203
$title = $key;
204
205
$box_header = array();
206
$box_header[] = $key;
207
208
$crumbs = $this->newCrumbs()
209
->addTextCrumb($key, '/config/edit/'.$key);
210
211
$form_box = $this->buildConfigBoxView($box_header, $form, $tag);
212
213
$timeline = $this->buildTransactionTimeline(
214
$config_entry,
215
new PhabricatorConfigTransactionQuery());
216
$timeline->setShouldTerminate(true);
217
218
$header = $this->buildHeaderView($title);
219
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,
229
));
230
231
return $this->newPage()
232
->setTitle($title)
233
->setCrumbs($crumbs)
234
->appendChild($view);
235
}
236
237
private function readRequest(
238
PhabricatorConfigOption $option,
239
AphrontRequest $request) {
240
241
$type = $option->newOptionType();
242
if ($type) {
243
$is_set = $type->isValuePresentInRequest($option, $request);
244
if ($is_set) {
245
$value = $type->readValueFromRequest($option, $request);
246
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;
262
}
263
264
return array(
265
$errors ? pht('Invalid') : null,
266
$errors,
267
$value,
268
$xaction,
269
);
270
} else {
271
$delete_xaction = id(new PhabricatorConfigTransaction())
272
->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT)
273
->setNewValue(
274
array(
275
'deleted' => true,
276
'value' => null,
277
));
278
279
return array(
280
null,
281
array(),
282
null,
283
$delete_xaction,
284
);
285
}
286
}
287
288
// TODO: If we missed on the new `PhabricatorConfigType` map, fall back
289
// to the old semi-modular, semi-hacky way of doing things.
290
291
$xaction = new PhabricatorConfigTransaction();
292
$xaction->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT);
293
294
$e_value = null;
295
$errors = array();
296
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()));
305
}
306
307
if (!$errors) {
308
$xaction->setNewValue(
309
array(
310
'deleted' => false,
311
'value' => $set_value,
312
));
313
} else {
314
$xaction = null;
315
}
316
317
return array($e_value, $errors, $value, $xaction);
318
}
319
320
private function getDisplayValue(
321
PhabricatorConfigOption $option,
322
PhabricatorConfigEntry $entry,
323
$value) {
324
325
$type = $option->newOptionType();
326
if ($type) {
327
return $type->newDisplayValue($option, $value);
328
}
329
330
if ($option->isCustomType()) {
331
return $option->getCustomObject()->getDisplayValue(
332
$option,
333
$entry,
334
$value);
335
}
336
337
throw new Exception(
338
pht(
339
'Unknown configuration option type "%s".',
340
$option->getType()));
341
}
342
343
private function renderControls(
344
PhabricatorConfigOption $option,
345
$display_value,
346
$e_value) {
347
348
$type = $option->newOptionType();
349
if ($type) {
350
return $type->newControls(
351
$option,
352
$display_value,
353
$e_value);
354
}
355
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()));
366
}
367
368
return $controls;
369
}
370
371
private function renderExamples(PhabricatorConfigOption $option) {
372
$examples = $option->getExamples();
373
if (!$examples) {
374
return null;
375
}
376
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')),
381
));
382
foreach ($examples as $example) {
383
list($value, $description) = $example;
384
385
if ($value === null) {
386
$value = phutil_tag('em', array(), pht('(empty)'));
387
} else {
388
if (is_array($value)) {
389
$value = implode("\n", $value);
390
}
391
}
392
393
$table[] = phutil_tag('tr', array('class' => 'column-labels'), array(
394
phutil_tag('th', array(), $description),
395
phutil_tag('td', array(), $value),
396
));
397
}
398
399
require_celerity_resource('config-options-css');
400
401
return phutil_tag(
402
'table',
403
array(
404
'class' => 'config-option-table',
405
'cellspacing' => '0',
406
'cellpadding' => '0',
407
),
408
$table);
409
}
410
411
private function renderDefaults(
412
PhabricatorConfigOption $option,
413
PhabricatorConfigEntry $entry) {
414
415
$stack = PhabricatorEnv::getConfigSourceStack();
416
$stack = $stack->getStack();
417
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')),
422
));
423
424
$is_effective_value = true;
425
foreach ($stack as $key => $source) {
426
$row_classes = array(
427
'column-labels',
428
);
429
430
$value = $source->getKeys(
431
array(
432
$option->getKey(),
433
));
434
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()]);
442
443
if ($is_effective_value) {
444
$is_effective_value = false;
445
$row_classes[] = 'config-options-effective-value';
446
}
447
}
448
449
$table[] = phutil_tag(
450
'tr',
451
array(
452
'class' => implode(' ', $row_classes),
453
),
454
array(
455
phutil_tag('th', array(), $source->getName()),
456
phutil_tag('td', array(), $value),
457
));
458
}
459
460
require_celerity_resource('config-options-css');
461
462
return phutil_tag(
463
'table',
464
array(
465
'class' => 'config-option-table',
466
'cellspacing' => '0',
467
'cellpadding' => '0',
468
),
469
$table);
470
}
471
472
}
473
474