Use form builder for user options
authorCyperghost <olaf_schmitz_1@t-online.de>
Mon, 18 Nov 2024 13:44:39 +0000 (14:44 +0100)
committerCyperghost <olaf_schmitz_1@t-online.de>
Mon, 18 Nov 2024 13:44:39 +0000 (14:44 +0100)
wcfsetup/install/files/acp/templates/userOptionAdd.tpl
wcfsetup/install/files/lib/acp/form/UserOptionAddForm.class.php
wcfsetup/install/files/lib/acp/form/UserOptionEditForm.class.php

index 01fc449f56b517718c08998dae99d9ccae00536d..bbc33b4e872fca176d0c73f9a21400e19bd26586 100644 (file)
 
 </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'}
index 1a2f1c0e125643dd6994b34ff64c51b213ce7fcd..c3a222a321fa5e755fbac6f73e6d19bb3752f1d1 100644 (file)
@@ -2,30 +2,49 @@
 
 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
@@ -37,114 +56,11 @@ class UserOptionAddForm extends AbstractForm
      */
     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[]
@@ -181,203 +97,272 @@ class UserOptionAddForm extends AbstractForm
     /**
      * @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,
@@ -392,55 +377,7 @@ class UserOptionAddForm extends AbstractForm
         $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();
     }
 }
index 19481cdac861badf033a63a14392b4120a937516..347ac4592a1cfdafc190ba32f44ed92f47b53a99 100644 (file)
@@ -2,12 +2,13 @@
 
 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.
@@ -23,152 +24,65 @@ class UserOptionEditForm extends UserOptionAddForm
      */
     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();
     }
 }