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