</header>
-{include file='shared_formNotice'}
-
-{if !$availableCategories|empty}
- <form method="post" action="{if $action == 'add'}{link controller='UserOptionAdd'}{/link}{else}{link controller='UserOptionEdit' id=$optionID}{/link}{/if}">
- <div class="section">
- <dl{if $errorField == 'optionName'} class="formError"{/if}>
- <dt><label for="optionName">{lang}wcf.global.name{/lang}</label></dt>
- <dd>
- <input type="text" id="optionName" name="optionName" value="{$i18nPlainValues['optionName']}" required autofocus class="long">
- {if $errorField == 'optionName'}
- <small class="innerError">
- {if $errorType == 'multilingual'}
- {lang}wcf.global.form.error.multilingual{/lang}
- {else}
- {lang}wcf.acp.user.option.name.error.{@$errorType}{/lang}
- {/if}
- </small>
- {/if}
- </dd>
- </dl>
- {include file='shared_multipleLanguageInputJavascript' elementIdentifier='optionName' forceSelection=true}
-
- <dl{if $errorField == 'optionDescription'} class="formError"{/if}>
- <dt><label for="optionDescription">{lang}wcf.acp.user.option.description{/lang}</label></dt>
- <dd>
- {* dirty work-around for non-i18n environments *}
- {capture assign=__optionDescription}{lang __optional=true}{$i18nPlainValues['optionDescription']}{/lang}{/capture}
- {if !$__optionDescription && !"~^[a-zA-Z0-9\-\_\.]+$~"|preg_match:$i18nPlainValues['optionDescription']}{capture assign=__optionDescription}{$i18nPlainValues['optionDescription']}{/capture}{/if}
-
- {* value is already encoded inside the capture calls above *}
- <textarea name="optionDescription" id="optionDescription" cols="40" rows="10">{@$__optionDescription}</textarea>
- {if $errorField == 'optionDescription'}
- <small class="innerError">
- {if $errorType == 'empty'}
- {lang}wcf.global.form.error.empty{/lang}
- {else}
- {lang}wcf.acp.user.option.description.error.{@$errorType}{/lang}
- {/if}
- </small>
- {/if}
- </dd>
- </dl>
- {include file='shared_multipleLanguageInputJavascript' elementIdentifier='optionDescription' forceSelection=true}
-
- <dl{if $errorField == 'categoryName'} class="formError"{/if}>
- <dt><label for="categoryName">{lang}wcf.global.category{/lang}</label></dt>
- <dd>
- <select name="categoryName" id="categoryName">
- {foreach from=$availableCategories item=availableCategory}
- <option value="{$availableCategory->categoryName}"{if $availableCategory->categoryName == $categoryName} selected{/if}>{lang}wcf.user.option.category.{$availableCategory->categoryName}{/lang}</option>
- {/foreach}
- </select>
-
- {if $errorField == 'categoryName'}
- <small class="innerError">
- {if $errorType == 'empty'}
- {lang}wcf.global.form.error.empty{/lang}
- {else}
- {lang}wcf.acp.user.option.categoryName.error.{@$errorType}{/lang}
- {/if}
- </small>
- {/if}
- </dd>
- </dl>
-
- <dl>
- <dt><label for="showOrder">{lang}wcf.global.showOrder{/lang}</label></dt>
- <dd>
- <input type="number" id="showOrder" name="showOrder" value="{$showOrder}" class="short">
- </dd>
- </dl>
-
- {event name='dataFields'}
- </div>
-
- <section class="section">
- <h2 class="sectionTitle">{lang}wcf.acp.user.option.typeData{/lang}</h2>
-
- <dl{if $errorField == 'optionType'} class="formError"{/if}>
- <dt><label for="optionType">{lang}wcf.acp.user.option.optionType{/lang}</label></dt>
- <dd>
- <select name="optionType" id="optionType"{if $action === 'edit'} disabled{/if}>
- {foreach from=$availableOptionTypes item=availableOptionType}
- <option value="{$availableOptionType}"{if $availableOptionType == $optionType} selected{/if}>{$availableOptionType}</option>
- {/foreach}
- </select>
- {if $errorField == 'optionType'}
- <small class="innerError">
- {if $errorType == 'empty'}
- {lang}wcf.global.form.error.empty{/lang}
- {else}
- {lang}wcf.acp.user.option.optionType.error.{@$errorType}{/lang}
- {/if}
- </small>
- {/if}
- <small>{lang}wcf.acp.user.option.optionType.description{/lang}</small>
- </dd>
- </dl>
-
- <dl>
- <dt><label for="defaultValue">{lang}wcf.acp.user.option.defaultValue{/lang}</label></dt>
- <dd>
- <input type="text" id="defaultValue" name="defaultValue" value="{$defaultValue}" class="long">
- <small>{lang}wcf.acp.user.option.defaultValue.description{/lang}</small>
- </dd>
- </dl>
-
- <dl{if $errorField == 'selectOptions'} class="formError"{/if}>
- <dt><label for="selectOptions">{lang}wcf.acp.user.option.selectOptions{/lang}</label></dt>
- <dd>
- <textarea name="selectOptions" id="selectOptions" cols="40" rows="10">{$selectOptions}</textarea>
- {if $errorField == 'selectOptions'}
- <small class="innerError">
- {if $errorType == 'empty'}
- {lang}wcf.global.form.error.empty{/lang}
- {else}
- {lang}wcf.acp.user.option.selectOptions.error.{@$errorType}{/lang}
- {/if}
- </small>
- {/if}
- <small>{lang}wcf.acp.user.option.selectOptions.description{/lang}</small>
- </dd>
- </dl>
-
- <dl{if $errorField == 'labeledUrl'} class="formError"{/if}>
- <dt><label for="labeledUrl">{lang}wcf.acp.user.option.labeledUrl{/lang}</label></dt>
- <dd>
- <input type="text" id="labeledUrl" name="labeledUrl" value="{$labeledUrl}" class="long">
- {if $errorField == 'labeledUrl'}
- <small class="innerError">
- {if $errorType == 'empty'}
- {lang}wcf.global.form.error.empty{/lang}
- {else}
- {lang}wcf.acp.user.option.labeledUrl.error.{@$errorType}{/lang}
- {/if}
- </small>
- {/if}
- <small>{lang}wcf.acp.user.option.labeledUrl.description{/lang}</small>
- </dd>
- </dl>
-
- <dl{if $errorField == 'outputClass'} class="formError"{/if}>
- <dt><label for="outputClass">{lang}wcf.acp.user.option.outputClass{/lang}</label></dt>
- <dd>
- <input type="text" id="outputClass" name="outputClass" value="{$outputClass}" class="long">
- {if $errorField == 'outputClass'}
- <small class="innerError">
- {if $errorType == 'empty'}
- {lang}wcf.global.form.error.empty{/lang}
- {else}
- {lang}wcf.acp.user.option.outputClass.error.{@$errorType}{/lang}
- {/if}
- </small>
- {/if}
- <small>{lang}wcf.acp.user.option.outputClass.description{/lang}</small>
- </dd>
- </dl>
-
- {event name='typeDataFields'}
- </section>
-
- <section class="section">
- <h2 class="sectionTitle">{lang}wcf.acp.user.option.access{/lang}</h2>
-
- <dl>
- <dt><label for="editable">{lang}wcf.acp.user.option.editable{/lang}</label></dt>
- <dd>
- <select name="editable" id="editable">
- <option value="1"{if $editable == 1} selected{/if}>{lang}wcf.acp.user.option.editable.1{/lang}</option>
- <option value="2"{if $editable == 2} selected{/if}>{lang}wcf.acp.user.option.editable.2{/lang}</option>
- <option value="3"{if $editable == 3} selected{/if}>{lang}wcf.acp.user.option.editable.3{/lang}</option>
- <option value="6"{if $editable == 6} selected{/if}>{lang}wcf.acp.user.option.editable.6{/lang}</option>
- </select>
- </dd>
- </dl>
-
- <dl>
- <dt><label for="visible">{lang}wcf.acp.user.option.visible{/lang}</label></dt>
- <dd>
- <select name="visible" id="visible">
- <option value="0"{if $visible == 0} selected{/if}>{lang}wcf.acp.user.option.visible.0{/lang}</option>
- <option value="1"{if $visible == 1} selected{/if}>{lang}wcf.acp.user.option.visible.1{/lang}</option>
- <option value="2"{if $visible == 2} selected{/if}>{lang}wcf.acp.user.option.visible.2{/lang}</option>
- <option value="3"{if $visible == 3} selected{/if}>{lang}wcf.acp.user.option.visible.3{/lang}</option>
- <option value="7"{if $visible == 7} selected{/if}>{lang}wcf.acp.user.option.visible.7{/lang}</option>
- <option value="15"{if $visible == 15} selected{/if}>{lang}wcf.acp.user.option.visible.15{/lang}</option>
- </select>
- </dd>
- </dl>
-
- <dl{if $errorField == 'validationPattern'} class="formError"{/if}>
- <dt><label for="validationPattern">{lang}wcf.acp.user.option.validationPattern{/lang}</label></dt>
- <dd>
- <input type="text" id="validationPattern" name="validationPattern" value="{$validationPattern}" class="long">
- {if $errorField == 'validationPattern'}
- <small class="innerError">
- {if $errorType == 'empty'}
- {lang}wcf.global.form.error.empty{/lang}
- {else}
- {lang}wcf.acp.user.option.validationPattern.error.{@$errorType}{/lang}
- {/if}
- </small>
- {/if}
- <small>{lang}wcf.acp.user.option.validationPattern.description{/lang}</small>
- </dd>
- </dl>
-
- <dl>
- <dt></dt>
- <dd>
- <label><input type="checkbox" name="required" id="required" value="1"{if $required == 1} checked{/if}> {lang}wcf.acp.user.option.required{/lang}</label>
- <label><input type="checkbox" name="askDuringRegistration" id="askDuringRegistration" value="1"{if $askDuringRegistration == 1} checked{/if}> {lang}wcf.acp.user.option.askDuringRegistration{/lang}</label>
- <label><input type="checkbox" name="searchable" id="searchable" value="1"{if $searchable == 1} checked{/if}> {lang}wcf.acp.user.option.searchable{/lang}</label>
- </dd>
- </dl>
-
- {event name='accessFields'}
- </section>
-
- {event name='sections'}
-
- <div class="formSubmit">
- <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
- {csrfToken}
- </div>
- </form>
-{else}
- <woltlab-core-notice type="error">{lang}wcf.acp.user.option.error.noCategories{/lang}</woltlab-core-notice>
-{/if}
+{unsafe:$form->getHtml()}
{include file='footer'}
namespace wcf\acp\form;
+use Laminas\Diactoros\Response\HtmlResponse;
+use wcf\data\IStorableObject;
use wcf\data\user\option\category\UserOptionCategory;
use wcf\data\user\option\category\UserOptionCategoryList;
use wcf\data\user\option\UserOption;
use wcf\data\user\option\UserOptionAction;
use wcf\data\user\option\UserOptionEditor;
-use wcf\form\AbstractForm;
-use wcf\system\exception\UserInputException;
+use wcf\form\AbstractFormBuilderForm;
+use wcf\http\error\HtmlErrorRenderer;
+use wcf\system\form\builder\container\FormContainer;
+use wcf\system\form\builder\data\processor\CustomFormDataProcessor;
+use wcf\system\form\builder\field\BooleanFormField;
+use wcf\system\form\builder\field\ClassNameFormField;
+use wcf\system\form\builder\field\dependency\ValueFormFieldDependency;
+use wcf\system\form\builder\field\IntegerFormField;
+use wcf\system\form\builder\field\ItemListFormField;
+use wcf\system\form\builder\field\MultilineItemListFormField;
+use wcf\system\form\builder\field\MultilineTextFormField;
+use wcf\system\form\builder\field\SingleSelectionFormField;
+use wcf\system\form\builder\field\TextFormField;
+use wcf\system\form\builder\field\validation\FormFieldValidationError;
+use wcf\system\form\builder\field\validation\FormFieldValidator;
+use wcf\system\form\builder\IFormDocument;
use wcf\system\language\I18nHandler;
use wcf\system\option\user\DateUserOptionOutput;
+use wcf\system\option\user\IUserOptionOutput;
use wcf\system\option\user\LabeledUrlUserOptionOutput;
+use wcf\system\option\user\MessageUserOptionOutput;
use wcf\system\option\user\SelectOptionsUserOptionOutput;
use wcf\system\option\user\URLUserOptionOutput;
-use wcf\system\request\LinkHandler;
use wcf\system\WCF;
use wcf\util\StringUtil;
/**
* Shows the user option add form.
*
- * @author Marcel Werk
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @author Olaf Braun, Marcel Werk
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ *
+ * @property UserOption $formObject
*/
-class UserOptionAddForm extends AbstractForm
+class UserOptionAddForm extends AbstractFormBuilderForm
{
/**
* @inheritDoc
*/
public $neededPermissions = ['admin.user.canManageUserOption'];
- /**
- * option name
- * @var string
- */
- public $optionName = '';
-
- /**
- * option description
- * @var string
- */
- public $optionDescription = '';
-
- /**
- * category name
- * @var string
- */
- public $categoryName = '';
-
- /**
- * option type
- * @var string
- */
- public $optionType = 'text';
-
- /**
- * option default value
- * @var string
- */
- public $defaultValue = '';
-
- /**
- * validation pattern
- * @var string
- */
- public $validationPattern = '';
-
- /**
- * select options
- * @var string
- */
- public $selectOptions = '';
-
- /**
- * @var string
- * @since 5.4
- */
- public $labeledUrl = '';
-
- /**
- * field is required
- * @var bool
- */
- public $required = 0;
-
- /**
- * shows this field in the registration process
- * @var bool
- */
- public $askDuringRegistration = 0;
-
- /**
- * edit permission bitmask
- * @var int
- */
- public $editable = 3;
-
- /**
- * view permission bitmask
- * @var int
- */
- public $visible = 15;
-
- /**
- * field is searchable
- * @var bool
- */
- public $searchable = 0;
-
- /**
- * show order
- * @var int
- */
- public $showOrder = 0;
-
- /**
- * output class
- * @var string
- */
- public $outputClass = '';
-
/**
* available option categories
* @var UserOptionCategory[]
*/
- public $availableCategories = [];
-
- /**
- * valid editability bits for UserOptions
- * @var int[]
- */
- public $validEditableBits = [
- UserOption::EDITABILITY_NONE,
- UserOption::EDITABILITY_OWNER,
- UserOption::EDITABILITY_ADMINISTRATOR,
- UserOption::EDITABILITY_ALL,
- UserOption::EDITABILITY_OWNER_DURING_REGISTRATION_AND_ADMINISTRATOR,
- ];
-
+ public array $availableCategories = [];
/**
* available option types
* @var string[]
/**
* @inheritDoc
*/
+ public $objectActionClass = UserOptionAction::class;
+
+ /**
+ * @inheritDoc
+ */
+ public $objectEditLinkController = UserOptionEditForm::class;
+
+ #[\Override]
public function readParameters()
{
parent::readParameters();
- I18nHandler::getInstance()->register('optionName');
- I18nHandler::getInstance()->register('optionDescription');
-
// get available categories
$categoryList = new UserOptionCategoryList();
$categoryList->getConditionBuilder()->add('parentCategoryName = ?', ['profile']);
$categoryList->readObjects();
$this->availableCategories = $categoryList->getObjects();
- }
-
- /**
- * @inheritDoc
- */
- public function readFormParameters()
- {
- parent::readFormParameters();
-
- I18nHandler::getInstance()->readValues();
-
- if (I18nHandler::getInstance()->isPlainValue('optionName')) {
- $this->optionName = I18nHandler::getInstance()->getValue('optionName');
- }
- if (I18nHandler::getInstance()->isPlainValue('optionDescription')) {
- $this->optionDescription = I18nHandler::getInstance()->getValue('optionDescription');
- }
- if (isset($_POST['categoryName'])) {
- $this->categoryName = $_POST['categoryName'];
- }
- if (isset($_POST['optionType'])) {
- $this->optionType = $_POST['optionType'];
- }
- if (isset($_POST['defaultValue'])) {
- $this->defaultValue = $_POST['defaultValue'];
- }
- if (isset($_POST['validationPattern'])) {
- $this->validationPattern = $_POST['validationPattern'];
- }
- if (isset($_POST['selectOptions'])) {
- $this->selectOptions = $_POST['selectOptions'];
- }
- if (isset($_POST['required'])) {
- $this->required = \intval($_POST['required']);
- }
- if (isset($_POST['askDuringRegistration'])) {
- $this->askDuringRegistration = \intval($_POST['askDuringRegistration']);
- }
- if (isset($_POST['editable'])) {
- $this->editable = \intval($_POST['editable']);
- }
- if (isset($_POST['visible'])) {
- $this->visible = \intval($_POST['visible']);
- }
- if (isset($_POST['searchable'])) {
- $this->searchable = \intval($_POST['searchable']);
- }
- if (isset($_POST['showOrder'])) {
- $this->showOrder = \intval($_POST['showOrder']);
- }
- if (isset($_POST['outputClass'])) {
- $this->outputClass = StringUtil::trim($_POST['outputClass']);
- }
- if (isset($_POST['labeledUrl'])) {
- $this->labeledUrl = StringUtil::trim($_POST['labeledUrl']);
- }
- if ($this->optionType == 'boolean' || $this->optionType == 'integer') {
- $this->defaultValue = \intval($this->defaultValue);
- }
- if ($this->optionType == 'float') {
- $this->defaultValue = \floatval($this->defaultValue);
- }
- if ($this->optionType == 'date') {
- if (!\preg_match('/\d{4}-\d{2}-\d{2}/', $this->defaultValue)) {
- $this->defaultValue = '';
- }
+ if (empty($this->availableCategories)) {
+ $this->setPsr7Response(
+ new HtmlResponse(
+ (new HtmlErrorRenderer())->renderHtmlMessage(
+ WCF::getLanguage()->getDynamicVariable('wcf.global.error.title'),
+ WCF::getLanguage()->getDynamicVariable('wcf.acp.user.option.error.noCategories'),
+ null,
+ !WCF::getUser()->userID,
+ ),
+ 403
+ )
+ );
}
-
- $this->setDefaultOutputClass();
}
- /**
- * Sets the default output class.
- */
- protected function setDefaultOutputClass()
+ #[\Override]
+ public function createForm()
{
- if (empty($this->outputClass)) {
- if (\in_array($this->optionType, self::$optionTypesUsingSelectOptions)) {
- $this->outputClass = SelectOptionsUserOptionOutput::class;
- }
-
- if ($this->optionType == 'date') {
- $this->outputClass = DateUserOptionOutput::class;
- }
-
- if ($this->optionType == 'URL') {
- $this->outputClass = URLUserOptionOutput::class;
- }
-
- if ($this->optionType == 'labeledUrl') {
- $this->outputClass = LabeledUrlUserOptionOutput::class;
- }
- }
+ parent::createForm();
+
+ $this->form->appendChildren([
+ FormContainer::create('general')
+ ->appendChildren([
+ TextFormField::create('optionName')
+ ->label('wcf.global.name')
+ ->required()
+ ->i18n()
+ ->i18nRequired()
+ ->languageItemPattern('wcf.user.option.(option\d+|\w+)'),
+ MultilineTextFormField::create('optionDescription')
+ ->label('wcf.acp.user.option.description')
+ ->i18n()
+ ->i18nRequired()
+ ->languageItemPattern('wcf.user.option.(option\d+|\w+).description'),
+ SingleSelectionFormField::create('categoryName')
+ ->label('wcf.global.category')
+ ->required()
+ ->options(function () {
+ $options = [];
+ foreach ($this->availableCategories as $category) {
+ $options[$category->categoryName] = $category->getTitle();
+ }
+
+ return $options;
+ }),
+ IntegerFormField::create('showOrder')
+ ->label('wcf.form.field.showOrder')
+ ->value(0)
+ ]),
+ FormContainer::create('typeDataContainer')
+ ->label('wcf.acp.user.option.typeData')
+ ->appendChildren([
+ SingleSelectionFormField::create('optionType')
+ ->label('wcf.acp.user.option.optionType')
+ ->description('wcf.acp.user.option.optionType.description')
+ ->required()
+ ->immutable($this->formAction !== 'create')
+ ->options(\array_combine(self::$availableOptionTypes, self::$availableOptionTypes))
+ ->value('text'),
+ TextFormField::create('defaultValue')
+ ->label('wcf.acp.user.option.defaultValue')
+ ->description('wcf.acp.user.option.defaultValue.description')
+ ->addFieldClass('long'),
+ MultilineItemListFormField::create('selectOptions')
+ ->label('wcf.acp.user.option.selectOptions')
+ ->description('wcf.acp.user.option.selectOptions.description')
+ ->required()
+ ->saveValueType(ItemListFormField::SAVE_VALUE_TYPE_NSV)
+ ->addDependency(
+ ValueFormFieldDependency::create('optionType')
+ ->fieldId('optionType')
+ ->values(self::$optionTypesUsingSelectOptions)
+ ),
+ TextFormField::create('labeledUrl')
+ ->label('wcf.acp.user.option.labeledUrl')
+ ->description('wcf.acp.user.option.labeledUrl.description')
+ ->addFieldClass('long')
+ ->required()
+ ->addValidator(
+ new FormFieldValidator('labeldUrlValidator', function (TextFormField $field) {
+ if (!\strpos($field->getValue(), '%s')) {
+ $field->addValidationError(
+ new FormFieldValidationError(
+ 'invalid',
+ 'wcf.acp.user.option.labeledUrl.error.invalid'
+ )
+ );
+ }
+ })
+ )
+ ->addDependency(
+ ValueFormFieldDependency::create('optionType')
+ ->fieldId('optionType')
+ ->values(['labeledUrl'])
+ ),
+ ClassNameFormField::create('outputClass')
+ ->label('wcf.acp.user.option.outputClass')
+ ->description('wcf.acp.user.option.outputClass.description')
+ ->implementedInterface(IUserOptionOutput::class)
+ ]),
+ FormContainer::create('access')
+ ->label('wcf.acp.user.option.access')
+ ->appendChildren([
+ SingleSelectionFormField::create('editable')
+ ->label('wcf.acp.user.option.editable')
+ ->options([
+ 1 => 'wcf.acp.user.option.editable.1',
+ 2 => 'wcf.acp.user.option.editable.2',
+ 3 => 'wcf.acp.user.option.editable.3',
+ 6 => 'wcf.acp.user.option.editable.6',
+ ])
+ ->value(3),
+ SingleSelectionFormField::create('visible')
+ ->label('wcf.acp.user.option.visible')
+ ->options([
+ 0 => 'wcf.acp.user.option.visible.0',
+ 1 => 'wcf.acp.user.option.visible.1',
+ 2 => 'wcf.acp.user.option.visible.2',
+ 3 => 'wcf.acp.user.option.visible.3',
+ 7 => 'wcf.acp.user.option.visible.7',
+ 15 => 'wcf.acp.user.option.visible.15',
+ ])
+ ->value(15),
+ TextFormField::create('validationPattern')
+ ->label('wcf.acp.user.option.validationPattern')
+ ->description('wcf.acp.user.option.validationPattern.description')
+ ->addDependency(
+ ValueFormFieldDependency::create('validationPatternOptionTypeDependency')
+ ->fieldId('optionType')
+ ->negate()
+ ->values(self::$optionTypesUsingSelectOptions)
+ ),
+ BooleanFormField::create('required')
+ ->label('wcf.acp.user.option.required')
+ ->value(false),
+ BooleanFormField::create('askDuringRegistration')
+ ->label('wcf.acp.user.option.askDuringRegistration')
+ ->value(false),
+ BooleanFormField::create('searchable')
+ ->label('wcf.acp.user.option.searchable')
+ ->value(false),
+ ])
+ ]);
}
- /**
- * @inheritDoc
- */
- public function validate()
+ #[\Override]
+ protected function finalizeForm()
{
- parent::validate();
-
- // option name
- if (!I18nHandler::getInstance()->validateValue('optionName', true)) {
- throw new UserInputException('optionName', 'multilingual');
- }
-
- // category name
- if (empty($this->categoryName)) {
- throw new UserInputException('categoryName');
- }
- $sql = "SELECT categoryID
- FROM wcf1_user_option_category
- WHERE categoryName = ?";
- $statement = WCF::getDB()->prepare($sql);
- $statement->execute([$this->categoryName]);
- if ($statement->fetchArray() === false) {
- throw new UserInputException('categoryName');
- }
-
- // option type
- if (!\in_array($this->optionType, self::$availableOptionTypes)) {
- throw new UserInputException('optionType');
- }
-
- // select options
- if (\in_array($this->optionType, self::$optionTypesUsingSelectOptions) && empty($this->selectOptions)) {
- throw new UserInputException('selectOptions');
- }
-
- if ($this->outputClass && !\class_exists($this->outputClass)) {
- throw new UserInputException('outputClass', 'doesNotExist');
- }
-
- if (!\in_array($this->editable, $this->validEditableBits)) {
- $this->editable = UserOption::EDITABILITY_ALL;
- }
-
- if ($this->optionType == 'labeledUrl' && \strpos($this->labeledUrl, '%s') === false) {
- throw new UserInputException('labeledUrl', 'invalid');
- }
+ parent::finalizeForm();
+
+ $this->form->getDataHandler()
+ ->addProcessor(
+ new CustomFormDataProcessor(
+ 'optionNameDataProcessor',
+ null,
+ function (IFormDocument $document, array $data, IStorableObject $object) {
+ \assert($object instanceof UserOption);
+ $data['optionName'] = 'wcf.user.option.' . $object->optionName;
+ $data['optionDescription'] = 'wcf.user.option.' . $object->optionName . '.description';
+
+ return $data;
+ }
+ ),
+ )
+ ->addProcessor(
+ new CustomFormDataProcessor(
+ 'additionDataProcessor',
+ function (IFormDocument $document, array $parameters) {
+ $additionalData = $this->formObject?->additionalData ?: [];
+
+ if ($parameters['data']['optionType'] == 'select') {
+ $additionalData['allowEmptyValue'] = true;
+ } elseif ($parameters['data']['optionType'] == 'message') {
+ $additionalData['messageObjectType'] = 'com.woltlab.wcf.user.option.generic';
+ }
+
+ $parameters['data']['additionalData'] = \serialize($additionalData);
+
+ return $parameters;
+ }
+ )
+ )
+ ->addProcessor(
+ new CustomFormDataProcessor(
+ 'outputClassDataProcessor',
+ function (IFormDocument $document, array $parameters) {
+ if ($this->formAction !== 'create') {
+ return $parameters;
+ }
+
+ $outputClass = $parameters['data']['outputClass'];
+ $optionType = $parameters['data']['optionType'];
+
+ if (empty($outputClass)) {
+ if (\in_array($optionType, self::$optionTypesUsingSelectOptions)) {
+ $parameters['data']['outputClass'] = SelectOptionsUserOptionOutput::class;
+ } else {
+ $parameters['data']['outputClass'] = match ($optionType) {
+ 'date' => DateUserOptionOutput::class,
+ 'URL' => URLUserOptionOutput::class,
+ 'labeledUrl' => LabeledUrlUserOptionOutput::class,
+ 'message' => MessageUserOptionOutput::class,
+ default => ''
+ };
+ }
+ }
+
+ return $parameters;
+ }
+ )
+ )
+ ->addProcessor(
+ new CustomFormDataProcessor(
+ 'defaultValueDataProcessor',
+ function (IFormDocument $document, array $parameters) {
+ $optionType = $parameters['data']['optionType'];
+ $defaultValue = $parameters['data']['defaultValue'];
+
+ $parameters['data']['defaultValue'] = match ($optionType) {
+ 'boolean', 'integer' => \intval($defaultValue),
+ 'float' => \floatval($defaultValue),
+ 'date' => \preg_match('/\d{4}-\d{2}-\d{2}/', $defaultValue) ? $defaultValue : '',
+ default => $defaultValue,
+ };
+
+ return $parameters;
+ }
+ )
+ );
}
- /**
- * @inheritDoc
- */
+ #[\Override]
public function save()
{
- parent::save();
-
- $additionalData = [];
- if ($this->optionType == 'select') {
- $additionalData['allowEmptyValue'] = true;
- }
- if ($this->optionType == 'message') {
- $additionalData['messageObjectType'] = 'com.woltlab.wcf.user.option.generic';
+ if ($this->formAction === 'create') {
+ $this->additionalFields['optionName'] = StringUtil::getRandomID();
+ $this->additionalFields['packageID'] = PACKAGE_ID;
}
- $this->objectAction = new UserOptionAction([], 'create', [
- 'data' => \array_merge($this->additionalFields, [
- 'optionName' => StringUtil::getRandomID(),
- 'categoryName' => $this->categoryName,
- 'optionType' => $this->optionType,
- 'defaultValue' => $this->defaultValue,
- 'showOrder' => $this->showOrder,
- 'outputClass' => $this->outputClass,
- 'validationPattern' => $this->validationPattern,
- 'selectOptions' => $this->selectOptions,
- 'required' => $this->required,
- 'askDuringRegistration' => $this->askDuringRegistration,
- 'searchable' => $this->searchable,
- 'editable' => $this->editable,
- 'visible' => $this->visible,
- 'packageID' => 1,
- 'additionalData' => !empty($additionalData) ? \serialize($additionalData) : '',
- 'labeledUrl' => $this->labeledUrl,
- ]),
- ]);
- $this->objectAction->executeAction();
+ parent::save();
+ }
- $returnValues = $this->objectAction->getReturnValues();
- $userOption = $returnValues['returnValues'];
+ #[\Override]
+ public function saved()
+ {
+ $userOption = $this->objectAction->getReturnValues()['returnValues'];
+ \assert($userOption instanceof UserOption);
- // save language vars
I18nHandler::getInstance()->save(
'optionName',
'wcf.user.option.option' . $userOption->optionID,
$editor->update([
'optionName' => 'option' . $userOption->optionID,
]);
- $this->saved();
-
- // reset values
- $this->optionName = $this->optionDescription = $this->categoryName = $this->optionType = $this->defaultValue = $this->validationPattern = $this->selectOptions = $this->outputClass = '';
- $this->optionType = 'text';
- $this->required = $this->searchable = $this->showOrder = $this->askDuringRegistration = 0;
- $this->editable = 3;
- $this->visible = 15;
-
- I18nHandler::getInstance()->reset();
-
- // show success
- WCF::getTPL()->assign([
- 'success' => true,
- 'objectEditLink' => LinkHandler::getInstance()->getControllerLink(
- UserOptionEditForm::class,
- ['id' => $userOption->optionID]
- ),
- ]);
- }
-
- /**
- * @inheritDoc
- */
- public function assignVariables()
- {
- parent::assignVariables();
- I18nHandler::getInstance()->assignVariables();
-
- WCF::getTPL()->assign([
- 'optionName' => $this->optionName,
- 'optionDescription' => $this->optionDescription,
- 'categoryName' => $this->categoryName,
- 'optionType' => $this->optionType,
- 'defaultValue' => $this->defaultValue,
- 'validationPattern' => $this->validationPattern,
- 'selectOptions' => $this->selectOptions,
- 'required' => $this->required,
- 'askDuringRegistration' => $this->askDuringRegistration,
- 'editable' => $this->editable,
- 'visible' => $this->visible,
- 'searchable' => $this->searchable,
- 'showOrder' => $this->showOrder,
- 'outputClass' => $this->outputClass,
- 'action' => 'add',
- 'availableCategories' => $this->availableCategories,
- 'availableOptionTypes' => self::$availableOptionTypes,
- 'labeledUrl' => $this->labeledUrl,
- ]);
+ parent::saved();
}
}
namespace wcf\acp\form;
+use CuyZ\Valinor\Mapper\MappingError;
use wcf\data\user\option\UserOption;
-use wcf\data\user\option\UserOptionAction;
-use wcf\form\AbstractForm;
+use wcf\form\AbstractFormBuilderForm;
+use wcf\http\Helper;
use wcf\system\exception\IllegalLinkException;
+use wcf\system\form\builder\field\SingleSelectionFormField;
use wcf\system\language\I18nHandler;
-use wcf\system\WCF;
/**
* Shows the user option edit form.
*/
public $activeMenuItem = 'wcf.acp.menu.link.user.option.list';
- /**
- * user option id
- * @var int
- */
- public $optionID = 0;
-
- /**
- * user option object
- * @var UserOption
- */
- public $userOption;
-
/**
* @inheritDoc
*/
+ public $formAction = 'edit';
+
+ #[\Override]
public function readParameters()
{
parent::readParameters();
- if (isset($_REQUEST['id'])) {
- $this->optionID = \intval($_REQUEST['id']);
- }
- $this->userOption = new UserOption($this->optionID);
- if (!$this->userOption->optionID) {
+ try {
+ $queryParameters = Helper::mapQueryParameters(
+ $_GET,
+ <<<'EOT'
+ array {
+ id: positive-int
+ }
+ EOT
+ );
+ $this->formObject = new UserOption($queryParameters['id']);
+
+ if (!$this->formObject->getObjectID()) {
+ throw new IllegalLinkException();
+ }
+ } catch (MappingError) {
throw new IllegalLinkException();
}
-
- if ($this->userOption->optionName === 'aboutMe') {
- self::$availableOptionTypes[] = 'aboutMe';
- }
}
- /**
- * @inheritDoc
- */
- public function readFormParameters()
+ #[\Override]
+ public function createForm()
{
- parent::readFormParameters();
+ parent::createForm();
- $this->optionType = $this->userOption->optionType;
- }
+ if ($this->formObject->optionName === 'aboutMe') {
+ $optionType = $this->form->getNodeById('optionType');
+ \assert($optionType instanceof SingleSelectionFormField);
- /**
- * @inheritDoc
- */
- protected function setDefaultOutputClass()
- {
+ $optionType->options([
+ ...$optionType->getOptions(),
+ 'aboutMe' => 'aboutMe',
+ ]);
+ }
}
- /**
- * @inheritDoc
- */
- public function save()
+ #[\Override]
+ public function saved()
{
- AbstractForm::save();
-
I18nHandler::getInstance()->save(
'optionName',
- 'wcf.user.option.' . $this->userOption->optionName,
+ 'wcf.user.option.' . $this->formObject->optionName,
'wcf.user.option'
);
I18nHandler::getInstance()->save(
'optionDescription',
- 'wcf.user.option.' . $this->userOption->optionName . '.description',
+ 'wcf.user.option.' . $this->formObject->optionName . '.description',
'wcf.user.option'
);
- $additionalData = \is_array($this->userOption->additionalData) ? $this->userOption->additionalData : [];
- if ($this->optionType === 'message' && !isset($additionalData['messageObjectType'])) {
- $additionalData['messageObjectType'] = 'com.woltlab.wcf.user.option.generic';
- }
-
- $this->objectAction = new UserOptionAction([$this->userOption], 'update', [
- 'data' => \array_merge($this->additionalFields, [
- 'categoryName' => $this->categoryName,
- 'optionType' => $this->optionType,
- 'defaultValue' => $this->defaultValue,
- 'showOrder' => $this->showOrder,
- 'outputClass' => $this->outputClass,
- 'validationPattern' => $this->validationPattern,
- 'selectOptions' => $this->selectOptions,
- 'required' => $this->required,
- 'askDuringRegistration' => $this->askDuringRegistration,
- 'searchable' => $this->searchable,
- 'editable' => $this->editable,
- 'visible' => $this->visible,
- 'additionalData' => !empty($additionalData) ? \serialize($additionalData) : '',
- 'labeledUrl' => $this->labeledUrl,
- ]),
- ]);
- $this->objectAction->executeAction();
- $this->saved();
-
- WCF::getTPL()->assign('success', true);
- }
-
- /**
- * @inheritDoc
- */
- public function readData()
- {
- parent::readData();
-
- I18nHandler::getInstance()->setOptions(
- 'optionName',
- 1,
- 'wcf.user.option.' . $this->userOption->optionName,
- 'wcf.user.option.option\d+'
- );
- I18nHandler::getInstance()->setOptions(
- 'optionDescription',
- 1,
- 'wcf.user.option.' . $this->userOption->optionName . '.description',
- 'wcf.user.option.option\d+.description'
- );
-
- if (empty($_POST)) {
- $this->categoryName = $this->userOption->categoryName;
- $this->optionType = $this->userOption->optionType;
- $this->defaultValue = $this->userOption->defaultValue;
- $this->validationPattern = $this->userOption->validationPattern;
- $this->selectOptions = $this->userOption->selectOptions;
- $this->required = $this->userOption->required;
- $this->askDuringRegistration = $this->userOption->askDuringRegistration;
- $this->editable = $this->userOption->editable;
- $this->visible = $this->userOption->visible;
- $this->searchable = $this->userOption->searchable;
- $this->showOrder = $this->userOption->showOrder;
- $this->outputClass = $this->userOption->outputClass;
- $this->labeledUrl = $this->userOption->labeledUrl;
- }
- }
-
- /**
- * @inheritDoc
- */
- public function assignVariables()
- {
- parent::assignVariables();
-
- I18nHandler::getInstance()->assignVariables(!empty($_POST));
-
- WCF::getTPL()->assign([
- 'action' => 'edit',
- 'optionID' => $this->optionID,
- 'userOption' => $this->userOption,
- ]);
+ AbstractFormBuilderForm::saved();
}
}