[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/config/controller/ -> PhabricatorConfigEditController.php (source)

   1  <?php
   2  
   3  final class PhabricatorConfigEditController
   4    extends PhabricatorConfigController {
   5  
   6    private $key;
   7  
   8    public function willProcessRequest(array $data) {
   9      $this->key = $data['key'];
  10    }
  11  
  12    public function processRequest() {
  13      $request = $this->getRequest();
  14      $user = $request->getUser();
  15  
  16  
  17      $options = PhabricatorApplicationConfigOptions::loadAllOptions();
  18      if (empty($options[$this->key])) {
  19        $ancient = PhabricatorSetupCheckExtraConfig::getAncientConfig();
  20        if (isset($ancient[$this->key])) {
  21          $desc = pht(
  22            "This configuration has been removed. You can safely delete ".
  23            "it.\n\n%s",
  24            $ancient[$this->key]);
  25        } else {
  26          $desc = pht(
  27            'This configuration option is unknown. It may be misspelled, '.
  28            'or have existed in a previous version of Phabricator.');
  29        }
  30  
  31        // This may be a dead config entry, which existed in the past but no
  32        // longer exists. Allow it to be edited so it can be reviewed and
  33        // deleted.
  34        $option = id(new PhabricatorConfigOption())
  35          ->setKey($this->key)
  36          ->setType('wild')
  37          ->setDefault(null)
  38          ->setDescription($desc);
  39        $group = null;
  40        $group_uri = $this->getApplicationURI();
  41      } else {
  42        $option = $options[$this->key];
  43        $group = $option->getGroup();
  44        $group_uri = $this->getApplicationURI('group/'.$group->getKey().'/');
  45      }
  46  
  47      $issue = $request->getStr('issue');
  48      if ($issue) {
  49        // If the user came here from an open setup issue, send them back.
  50        $done_uri = $this->getApplicationURI('issue/'.$issue.'/');
  51      } else {
  52        $done_uri = $group_uri;
  53      }
  54  
  55      // Check if the config key is already stored in the database.
  56      // Grab the value if it is.
  57      $config_entry = id(new PhabricatorConfigEntry())
  58        ->loadOneWhere(
  59          'configKey = %s AND namespace = %s',
  60          $this->key,
  61          'default');
  62      if (!$config_entry) {
  63        $config_entry = id(new PhabricatorConfigEntry())
  64          ->setConfigKey($this->key)
  65          ->setNamespace('default')
  66          ->setIsDeleted(true);
  67        $config_entry->setPHID($config_entry->generatePHID());
  68      }
  69  
  70      $e_value = null;
  71      $errors = array();
  72      if ($request->isFormPost() && !$option->getLocked()) {
  73  
  74        $result = $this->readRequest(
  75          $option,
  76          $request);
  77  
  78        list($e_value, $value_errors, $display_value, $xaction) = $result;
  79        $errors = array_merge($errors, $value_errors);
  80  
  81        if (!$errors) {
  82  
  83          $editor = id(new PhabricatorConfigEditor())
  84            ->setActor($user)
  85            ->setContinueOnNoEffect(true)
  86            ->setContentSourceFromRequest($request);
  87  
  88          try {
  89            $editor->applyTransactions($config_entry, array($xaction));
  90            return id(new AphrontRedirectResponse())->setURI($done_uri);
  91          } catch (PhabricatorConfigValidationException $ex) {
  92            $e_value = pht('Invalid');
  93            $errors[] = $ex->getMessage();
  94          }
  95        }
  96      } else {
  97        if ($config_entry->getIsDeleted()) {
  98          $display_value = null;
  99        } else {
 100          $display_value = $this->getDisplayValue(
 101            $option,
 102            $config_entry,
 103            $config_entry->getValue());
 104        }
 105      }
 106  
 107      $form = new AphrontFormView();
 108  
 109      $error_view = null;
 110      if ($errors) {
 111        $error_view = id(new AphrontErrorView())
 112          ->setErrors($errors);
 113      } else if ($option->getHidden()) {
 114        $msg = pht(
 115          'This configuration is hidden and can not be edited or viewed from '.
 116          'the web interface.');
 117  
 118        $error_view = id(new AphrontErrorView())
 119          ->setTitle(pht('Configuration Hidden'))
 120          ->setSeverity(AphrontErrorView::SEVERITY_WARNING)
 121          ->appendChild(phutil_tag('p', array(), $msg));
 122      } else if ($option->getLocked()) {
 123  
 124        $msg = $option->getLockedMessage();
 125        $error_view = id(new AphrontErrorView())
 126          ->setTitle(pht('Configuration Locked'))
 127          ->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
 128          ->appendChild(phutil_tag('p', array(), $msg));
 129      }
 130  
 131      if ($option->getHidden()) {
 132        $control = null;
 133      } else {
 134        $control = $this->renderControl(
 135          $option,
 136          $display_value,
 137          $e_value);
 138      }
 139  
 140      $engine = new PhabricatorMarkupEngine();
 141      $engine->setViewer($user);
 142      $engine->addObject($option, 'description');
 143      $engine->process();
 144      $description = phutil_tag(
 145        'div',
 146        array(
 147          'class' => 'phabricator-remarkup',
 148        ),
 149        $engine->getOutput($option, 'description'));
 150  
 151      $form
 152        ->setUser($user)
 153        ->addHiddenInput('issue', $request->getStr('issue'))
 154        ->appendChild(
 155          id(new AphrontFormMarkupControl())
 156            ->setLabel(pht('Description'))
 157            ->setValue($description));
 158  
 159      if ($group) {
 160        $extra = $group->renderContextualDescription(
 161          $option,
 162          $request);
 163        if ($extra !== null) {
 164          $form->appendChild(
 165            id(new AphrontFormMarkupControl())
 166              ->setValue($extra));
 167        }
 168      }
 169  
 170      $form
 171        ->appendChild($control);
 172  
 173      $submit_control = id(new AphrontFormSubmitControl())
 174        ->addCancelButton($done_uri);
 175  
 176      if (!$option->getLocked()) {
 177        $submit_control->setValue(pht('Save Config Entry'));
 178      }
 179  
 180      $form->appendChild($submit_control);
 181  
 182      $examples = $this->renderExamples($option);
 183      if ($examples) {
 184        $form->appendChild(
 185          id(new AphrontFormMarkupControl())
 186            ->setLabel(pht('Examples'))
 187            ->setValue($examples));
 188      }
 189  
 190      if (!$option->getHidden()) {
 191        $form->appendChild(
 192          id(new AphrontFormMarkupControl())
 193            ->setLabel(pht('Default'))
 194            ->setValue($this->renderDefaults($option, $config_entry)));
 195      }
 196  
 197      $title = pht('Edit %s', $this->key);
 198      $short = pht('Edit');
 199  
 200      $form_box = id(new PHUIObjectBoxView())
 201        ->setHeaderText($title)
 202        ->setForm($form);
 203  
 204      if ($error_view) {
 205         $form_box->setErrorView($error_view);
 206      }
 207  
 208      $crumbs = $this->buildApplicationCrumbs();
 209      $crumbs->addTextCrumb(pht('Config'), $this->getApplicationURI());
 210  
 211      if ($group) {
 212        $crumbs->addTextCrumb($group->getName(), $group_uri);
 213      }
 214  
 215      $crumbs->addTextCrumb($this->key, '/config/edit/'.$this->key);
 216  
 217      $xactions = id(new PhabricatorConfigTransactionQuery())
 218        ->withObjectPHIDs(array($config_entry->getPHID()))
 219        ->setViewer($user)
 220        ->execute();
 221  
 222      $xaction_view = id(new PhabricatorApplicationTransactionView())
 223        ->setUser($user)
 224        ->setObjectPHID($config_entry->getPHID())
 225        ->setTransactions($xactions);
 226  
 227      return $this->buildApplicationPage(
 228        array(
 229          $crumbs,
 230          $form_box,
 231          $xaction_view,
 232        ),
 233        array(
 234          'title' => $title,
 235        ));
 236    }
 237  
 238    private function readRequest(
 239      PhabricatorConfigOption $option,
 240      AphrontRequest $request) {
 241  
 242      $xaction = new PhabricatorConfigTransaction();
 243      $xaction->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT);
 244  
 245      $e_value = null;
 246      $errors = array();
 247  
 248      $value = $request->getStr('value');
 249      if (!strlen($value)) {
 250        $value = null;
 251  
 252        $xaction->setNewValue(
 253          array(
 254            'deleted' => true,
 255            'value'   => null,
 256          ));
 257  
 258        return array($e_value, $errors, $value, $xaction);
 259      }
 260  
 261      if ($option->isCustomType()) {
 262        $info = $option->getCustomObject()->readRequest($option, $request);
 263        list($e_value, $errors, $set_value, $value) = $info;
 264      } else {
 265        $type = $option->getType();
 266        $set_value = null;
 267  
 268        switch ($type) {
 269          case 'int':
 270            if (preg_match('/^-?[0-9]+$/', trim($value))) {
 271              $set_value = (int)$value;
 272            } else {
 273              $e_value = pht('Invalid');
 274              $errors[] = pht('Value must be an integer.');
 275            }
 276            break;
 277          case 'string':
 278          case 'enum':
 279            $set_value = (string)$value;
 280            break;
 281          case 'list<string>':
 282          case 'list<regex>':
 283            $set_value = phutil_split_lines(
 284              $request->getStr('value'),
 285              $retain_endings = false);
 286  
 287            foreach ($set_value as $key => $v) {
 288              if (!strlen($v)) {
 289                unset($set_value[$key]);
 290              }
 291            }
 292            $set_value = array_values($set_value);
 293  
 294            break;
 295          case 'set':
 296            $set_value = array_fill_keys($request->getStrList('value'), true);
 297            break;
 298          case 'bool':
 299            switch ($value) {
 300              case 'true':
 301                $set_value = true;
 302                break;
 303              case 'false':
 304                $set_value = false;
 305                break;
 306              default:
 307                $e_value = pht('Invalid');
 308                $errors[] = pht('Value must be boolean, "true" or "false".');
 309                break;
 310            }
 311            break;
 312          case 'class':
 313            if (!class_exists($value)) {
 314              $e_value = pht('Invalid');
 315              $errors[] = pht('Class does not exist.');
 316            } else {
 317              $base = $option->getBaseClass();
 318              if (!is_subclass_of($value, $base)) {
 319                $e_value = pht('Invalid');
 320                $errors[] = pht('Class is not of valid type.');
 321              } else {
 322                $set_value = $value;
 323              }
 324            }
 325            break;
 326          default:
 327            $json = json_decode($value, true);
 328            if ($json === null && strtolower($value) != 'null') {
 329              $e_value = pht('Invalid');
 330              $errors[] = pht(
 331                'The given value must be valid JSON. This means, among '.
 332                'other things, that you must wrap strings in double-quotes.');
 333            } else {
 334              $set_value = $json;
 335            }
 336            break;
 337        }
 338      }
 339  
 340      if (!$errors) {
 341        $xaction->setNewValue(
 342          array(
 343            'deleted' => false,
 344            'value'   => $set_value,
 345          ));
 346      } else {
 347        $xaction = null;
 348      }
 349  
 350      return array($e_value, $errors, $value, $xaction);
 351    }
 352  
 353    private function getDisplayValue(
 354      PhabricatorConfigOption $option,
 355      PhabricatorConfigEntry $entry,
 356      $value) {
 357  
 358      if ($option->isCustomType()) {
 359        return $option->getCustomObject()->getDisplayValue(
 360          $option,
 361          $entry,
 362          $value);
 363      } else {
 364        $type = $option->getType();
 365        switch ($type) {
 366          case 'int':
 367          case 'string':
 368          case 'enum':
 369          case 'class':
 370            return $value;
 371          case 'bool':
 372            return $value ? 'true' : 'false';
 373          case 'list<string>':
 374          case 'list<regex>':
 375            return implode("\n", nonempty($value, array()));
 376          case 'set':
 377            return implode("\n", nonempty(array_keys($value), array()));
 378          default:
 379            return PhabricatorConfigJSON::prettyPrintJSON($value);
 380        }
 381      }
 382    }
 383  
 384    private function renderControl(
 385      PhabricatorConfigOption $option,
 386      $display_value,
 387      $e_value) {
 388  
 389      if ($option->isCustomType()) {
 390        $control = $option->getCustomObject()->renderControl(
 391          $option,
 392          $display_value,
 393          $e_value);
 394      } else {
 395        $type = $option->getType();
 396        switch ($type) {
 397          case 'int':
 398          case 'string':
 399            $control = id(new AphrontFormTextControl());
 400            break;
 401          case 'bool':
 402            $control = id(new AphrontFormSelectControl())
 403              ->setOptions(
 404                array(
 405                  ''      => pht('(Use Default)'),
 406                  'true'  => idx($option->getBoolOptions(), 0),
 407                  'false' => idx($option->getBoolOptions(), 1),
 408                ));
 409            break;
 410          case 'enum':
 411            $options = array_mergev(
 412              array(
 413                array('' => pht('(Use Default)')),
 414                $option->getEnumOptions(),
 415              ));
 416            $control = id(new AphrontFormSelectControl())
 417              ->setOptions($options);
 418            break;
 419          case 'class':
 420            $symbols = id(new PhutilSymbolLoader())
 421              ->setType('class')
 422              ->setAncestorClass($option->getBaseClass())
 423              ->setConcreteOnly(true)
 424              ->selectSymbolsWithoutLoading();
 425            $names = ipull($symbols, 'name', 'name');
 426            asort($names);
 427            $names = array(
 428              '' => pht('(Use Default)'),
 429            ) + $names;
 430  
 431            $control = id(new AphrontFormSelectControl())
 432              ->setOptions($names);
 433            break;
 434          case 'list<string>':
 435          case 'list<regex>':
 436            $control = id(new AphrontFormTextAreaControl())
 437              ->setCaption(pht('Separate values with newlines.'));
 438            break;
 439          case 'set':
 440            $control = id(new AphrontFormTextAreaControl())
 441              ->setCaption(pht('Separate values with newlines or commas.'));
 442            break;
 443          default:
 444            $control = id(new AphrontFormTextAreaControl())
 445              ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
 446              ->setCustomClass('PhabricatorMonospaced')
 447              ->setCaption(pht('Enter value in JSON.'));
 448            break;
 449        }
 450  
 451        $control
 452          ->setLabel(pht('Value'))
 453          ->setError($e_value)
 454          ->setValue($display_value)
 455          ->setName('value');
 456      }
 457  
 458      if ($option->getLocked()) {
 459        $control->setDisabled(true);
 460      }
 461  
 462      return $control;
 463    }
 464  
 465    private function renderExamples(PhabricatorConfigOption $option) {
 466      $examples = $option->getExamples();
 467      if (!$examples) {
 468        return null;
 469      }
 470  
 471      $table = array();
 472      $table[] = phutil_tag('tr', array('class' => 'column-labels'), array(
 473        phutil_tag('th', array(), pht('Example')),
 474        phutil_tag('th', array(), pht('Value')),
 475      ));
 476      foreach ($examples as $example) {
 477        list($value, $description) = $example;
 478  
 479        if ($value === null) {
 480          $value = phutil_tag('em', array(), pht('(empty)'));
 481        } else {
 482          if (is_array($value)) {
 483            $value = implode("\n", $value);
 484          }
 485        }
 486  
 487        $table[] = phutil_tag('tr', array(), array(
 488          phutil_tag('th', array(), $description),
 489          phutil_tag('td', array(), $value),
 490        ));
 491      }
 492  
 493      require_celerity_resource('config-options-css');
 494  
 495      return phutil_tag(
 496        'table',
 497        array(
 498          'class' => 'config-option-table',
 499        ),
 500        $table);
 501    }
 502  
 503    private function renderDefaults(
 504      PhabricatorConfigOption $option,
 505      PhabricatorConfigEntry $entry) {
 506  
 507      $stack = PhabricatorEnv::getConfigSourceStack();
 508      $stack = $stack->getStack();
 509  
 510      $table = array();
 511      $table[] = phutil_tag('tr', array('class' => 'column-labels'), array(
 512        phutil_tag('th', array(), pht('Source')),
 513        phutil_tag('th', array(), pht('Value')),
 514      ));
 515      foreach ($stack as $key => $source) {
 516        $value = $source->getKeys(
 517          array(
 518            $option->getKey(),
 519          ));
 520  
 521        if (!array_key_exists($option->getKey(), $value)) {
 522          $value = phutil_tag('em', array(), pht('(empty)'));
 523        } else {
 524          $value = $this->getDisplayValue(
 525            $option,
 526            $entry,
 527            $value[$option->getKey()]);
 528        }
 529  
 530        $table[] = phutil_tag('tr', array('class' => 'column-labels'), array(
 531          phutil_tag('th', array(), $source->getName()),
 532          phutil_tag('td', array(), $value),
 533        ));
 534      }
 535  
 536      require_celerity_resource('config-options-css');
 537  
 538      return phutil_tag(
 539        'table',
 540        array(
 541          'class' => 'config-option-table',
 542        ),
 543        $table);
 544    }
 545  
 546  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1