Use the FormBuilder to create/edit tags 6.2-formbuilder-tags
authorCyperghost <olaf_schmitz_1@t-online.de>
Thu, 14 Nov 2024 11:13:48 +0000 (12:13 +0100)
committerCyperghost <olaf_schmitz_1@t-online.de>
Thu, 14 Nov 2024 11:13:48 +0000 (12:13 +0100)
wcfsetup/install/files/acp/templates/__tagFormSynonym.tpl [new file with mode: 0644]
wcfsetup/install/files/acp/templates/tagAdd.tpl
wcfsetup/install/files/lib/acp/form/TagAddForm.class.php
wcfsetup/install/files/lib/acp/form/TagEditForm.class.php
wcfsetup/install/files/lib/data/tag/TagAction.class.php

diff --git a/wcfsetup/install/files/acp/templates/__tagFormSynonym.tpl b/wcfsetup/install/files/acp/templates/__tagFormSynonym.tpl
new file mode 100644 (file)
index 0000000..e72e832
--- /dev/null
@@ -0,0 +1,6 @@
+<dl>
+       <dt><label for="synonyms">{lang}wcf.acp.tag.synonymFor{/lang}</label></dt>
+       <dd>
+               <a href="{link controller='TagEdit' id=$synonym->tagID}{/link}" class="badge tag">{$synonym->name}</a>
+       </dd>
+</dl>
index 86da527c481950d56f0ed93cdd03c22d0fc9e6fe..c8686d132c73fe65b18bc48b4a968e9bce55a6aa 100644 (file)
        </nav>
 </header>
 
-{include file='shared_formNotice'}
-
-<form method="post" action="{if $action == 'add'}{link controller='TagAdd'}{/link}{else}{link controller='TagEdit' object=$tagObj}{/link}{/if}">
-       <div class="section">
-               <dl{if $errorField == 'name'} class="formError"{/if}>
-                       <dt><label for="name">{lang}wcf.global.name{/lang}</label></dt>
-                       <dd>
-                               <input type="text" id="name" name="name" value="{$name}" required autofocus class="long">
-                               {if $errorField == 'name'}
-                                       <small class="innerError">
-                                               {if $errorType == 'empty'}
-                                                       {lang}wcf.global.form.error.empty{/lang}
-                                               {elseif $errorType == 'duplicate'}
-                                                       {lang}wcf.acp.tag.error.name.duplicate{/lang}
-                                               {/if}
-                                       </small>
-                               {/if}
-                       </dd>
-               </dl>
-               
-               {hascontent}
-                       <dl{if $errorField == 'languageID' || $action == 'edit'} class="{if $action == 'edit'}disabled{else}formError{/if}"{/if}>
-                               <dt><label for="languageID">{lang}wcf.acp.tag.languageID{/lang}</label></dt>
-                               <dd>
-                                       <select id="languageID" name="languageID"{if $action == 'edit'} disabled{/if}>
-                                               {content}
-                                                       {foreach from=$availableLanguages item=language}
-                                                               <option value="{$language->languageID}"{if $languageID == $language->languageID} selected{/if}>{$language->languageName} ({$language->languageCode})</option>
-                                                       {/foreach}
-                                               {/content}
-                                       </select>
-                                       {if $errorField == 'languageID'}
-                                               <small class="innerError">
-                                                       {lang}wcf.acp.tag.error.languageID.{$errorType}{/lang}
-                                               </small>
-                                       {/if}
-                               </dd>
-                       </dl>
-               {/hascontent}
-               
-               {if !$tagObj|isset || $tagObj->synonymFor === null}
-                       <dl>
-                               <dt><label for="synonyms">{lang}wcf.acp.tag.synonyms{/lang}</label></dt>
-                               <dd>
-                                       <input id="synonyms" type="text" value="" class="long">
-                               </dd>
-                       </dl>
-                       
-                       <script data-relocate="true">
-                               require(['WoltLabSuite/Core/Ui/ItemList'], function(UiItemList) {
-                                       UiItemList.init(
-                                               'synonyms',
-                                               [{if !$synonyms|empty}{implode from=$synonyms item=synonym}'{$synonym|encodeJS}'{/implode}{/if}],
-                                               {
-                                                       ajax: {
-                                                               className: 'wcf\\data\\tag\\TagAction'
-                                                       },
-                                                       maxLength: {@TAGGING_MAX_TAG_LENGTH},
-                                                       submitFieldName: 'tags[]'
-                                               }
-                                       );
-                               });
-                       </script>
-               {elseif $tagObj|isset}
-                       <dl>
-                               <dt><label for="synonyms">{lang}wcf.acp.tag.synonymFor{/lang}</label></dt>
-                               <dd>
-                                       <a href="{link controller='TagEdit' id=$tagObj->synonymFor}{/link}" class="badge tag">{$synonym->name}</a>
-                               </dd>
-                       </dl>
-               {/if}
-               
-               {event name='dataFields'}
-       </div>
-       
-       {event name='sections'}
-       
-       <div class="formSubmit">
-               <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
-               {csrfToken}
-       </div>
-</form>
+{unsafe:$form->getHtml()}
 
 {include file='footer'}
index f86d3f7ccce9cf41d69d8cc21ebd3c279b783849..57c80f777a2186c33c4ceed6fef1e13b5612e02a 100644 (file)
@@ -2,25 +2,34 @@
 
 namespace wcf\acp\form;
 
+use wcf\data\IStorableObject;
 use wcf\data\tag\Tag;
 use wcf\data\tag\TagAction;
-use wcf\data\tag\TagEditor;
-use wcf\form\AbstractForm;
-use wcf\system\exception\UserInputException;
+use wcf\data\tag\TagList;
+use wcf\form\AbstractFormBuilderForm;
+use wcf\system\form\builder\container\FormContainer;
+use wcf\system\form\builder\data\processor\CustomFormDataProcessor;
+use wcf\system\form\builder\field\SingleSelectionFormField;
+use wcf\system\form\builder\field\tag\TagFormField;
+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\form\builder\TemplateFormNode;
 use wcf\system\language\LanguageFactory;
-use wcf\system\request\LinkHandler;
 use wcf\system\WCF;
-use wcf\util\ArrayUtil;
 use wcf\util\StringUtil;
 
 /**
  * Shows the tag add form.
  *
- * @author  Tim Duesterhus
- * @copyright   2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @author      Olaf Braun, Tim Duesterhus
+ * @copyright   2001-2024 WoltLab GmbH
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ *
+ * @property Tag $formObject
  */
-class TagAddForm extends AbstractForm
+class TagAddForm extends AbstractFormBuilderForm
 {
     /**
      * @inheritDoc
@@ -37,188 +46,102 @@ class TagAddForm extends AbstractForm
      */
     public $neededModules = ['MODULE_TAGGING'];
 
-    /**
-     * list of available languages
-     * @var array
-     */
-    public $availableLanguages = [];
-
-    /**
-     * name value
-     * @var string
-     */
-    public $name = '';
-
-    /**
-     * language value
-     * @var string
-     */
-    public $languageID = 0;
-
-    /**
-     * synonyms
-     * @var string[]
-     */
-    public $synonyms = [];
-
     /**
      * @inheritDoc
      */
-    public function readParameters()
-    {
-        parent::readParameters();
-
-        $this->availableLanguages = LanguageFactory::getInstance()->getContentLanguages();
-    }
+    public $objectActionClass = TagAction::class;
 
     /**
      * @inheritDoc
      */
-    public function readFormParameters()
-    {
-        parent::readFormParameters();
-
-        if (isset($_POST['name'])) {
-            $this->name = \str_replace(',', '', StringUtil::trim($_POST['name']));
-        }
-        if (isset($_POST['languageID'])) {
-            $this->languageID = \intval($_POST['languageID']);
-        }
+    public $objectEditLinkController = TagEditForm::class;
 
-        // actually these are synonyms
-        if (isset($_POST['tags']) && \is_array($_POST['tags'])) {
-            $this->synonyms = ArrayUtil::trim($_POST['tags']);
-        }
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function validate()
+    #[\Override]
+    protected function createForm()
     {
-        parent::validate();
-
-        if (empty($this->name)) {
-            throw new UserInputException('name');
-        }
-
-        // validate language
-        if (!isset($this->tagObj)) {
-            if (empty($this->availableLanguages)) {
-                // force default language id
-                $this->languageID = LanguageFactory::getInstance()->getDefaultLanguageID();
-            } else {
-                if (!isset($this->availableLanguages[$this->languageID])) {
-                    throw new UserInputException('languageID', 'notFound');
-                }
-            }
-        }
-
-        // check for duplicates
-        $tag = Tag::getTag($this->name, $this->languageID);
-        if ($tag !== null && (!isset($this->tagObj) || $tag->tagID != $this->tagObj->tagID)) {
-            throw new UserInputException('name', 'duplicate');
-        }
-
-        // validate synonyms
-        foreach ($this->synonyms as $key => $synonym) {
-            if (\mb_strtolower($synonym) == \mb_strtolower($this->name)) {
-                unset($this->synonyms[$key]);
-            }
-        }
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function readData()
-    {
-        parent::readData();
-
-        if (empty($_POST)) {
-            // pre-select default language id
-            if (!empty($this->availableLanguages)) {
-                $this->languageID = LanguageFactory::getInstance()->getDefaultLanguageID();
-                if (!isset($this->availableLanguages[$this->languageID])) {
-                    // language id is not within content languages, try user's language instead
-                    $this->languageID = WCF::getUser()->languageID;
-                    if (!isset($this->availableLanguages[$this->languageID])) {
-                        // this installation is weird, just select nothing
-                        $this->languageID = 0;
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function save()
-    {
-        parent::save();
-
-        // save tag
-        $this->objectAction = new TagAction([], 'create', [
-            'data' => \array_merge($this->additionalFields, [
-                'name' => $this->name,
-                'languageID' => $this->languageID,
-            ]),
-        ]);
-        $this->objectAction->executeAction();
-        $returnValues = $this->objectAction->getReturnValues();
-        $editor = new TagEditor($returnValues['returnValues']);
-
-        foreach ($this->synonyms as $synonym) {
-            if (empty($synonym)) {
-                continue;
-            }
-
-            // find existing tag
-            $synonymObj = Tag::getTag($synonym, $this->languageID);
-            if ($synonymObj === null) {
-                $synonymAction = new TagAction([], 'create', [
-                    'data' => [
-                        'name' => $synonym,
-                        'languageID' => $this->languageID,
-                        'synonymFor' => $editor->tagID,
-                    ],
-                ]);
-                $synonymAction->executeAction();
-            } else {
-                $editor->addSynonym($synonymObj);
-            }
-        }
-
-        $this->saved();
-
-        // reset values
-        $this->name = '';
-        $this->synonyms = [];
-
-        // show success message
-        WCF::getTPL()->assign([
-            'success' => true,
-            'objectEditLink' => LinkHandler::getInstance()->getControllerLink(
-                TagEditForm::class,
-                ['id' => $returnValues['returnValues']->tagID]
-            ),
+        parent::createForm();
+
+        $this->form->appendChildren([
+            FormContainer::create('general')
+                ->appendChildren([
+                    TextFormField::create('name')
+                        ->label('wcf.global.name')
+                        ->required()
+                        ->maximumLength(TAGGING_MAX_TAG_LENGTH)
+                        ->addValidator(
+                            new FormFieldValidator('duplicateTagValidator', function (TextFormField $field) {
+                                $languageIDFormField = $field->getDocument()->getNodeById('languageID');
+                                \assert($languageIDFormField instanceof SingleSelectionFormField);
+                                $languageID = $languageIDFormField->getValue();
+
+                                $tag = Tag::getTag($field->getValue(), $languageID);
+                                if ($tag !== null && $tag->tagID !== $this->formObject?->tagID) {
+                                    $field->addValidationError(
+                                        new FormFieldValidationError(
+                                            'duplicate',
+                                            'wcf.acp.tag.error.name.duplicate'
+                                        )
+                                    );
+                                }
+                            })
+                        ),
+                    SingleSelectionFormField::create('languageID')
+                        ->label('wcf.acp.tag.languageID')
+                        ->options(LanguageFactory::getInstance()->getContentLanguages())
+                        ->value(WCF::getUser()->languageID)
+                        ->immutable($this->formAction !== 'create')
+                        ->required(),
+                    TagFormField::create('synonyms')
+                        ->available($this->formObject?->synonymFor === null)
+                        ->label('wcf.acp.tag.synonyms'),
+                    TemplateFormNode::create('tagSynonymFor')
+                        ->available($this->formObject?->synonymFor !== null)
+                        ->variables([
+                            'synonym' => new Tag($this->formObject?->synonymFor),
+                        ])
+                        ->templateName('__tagFormSynonym')
+                ])
         ]);
     }
 
-    /**
-     * @inheritDoc
-     */
-    public function assignVariables()
+    #[\Override]
+    protected function finalizeForm()
     {
-        parent::assignVariables();
-
-        WCF::getTPL()->assign([
-            'action' => 'add',
-            'availableLanguages' => $this->availableLanguages,
-            'name' => $this->name,
-            'languageID' => $this->languageID,
-            'synonyms' => $this->synonyms,
-        ]);
+        parent::finalizeForm();
+
+        $this->form->getDataHandler()
+            ->addProcessor(
+                new CustomFormDataProcessor(
+                    'tagNameProcessor',
+                    static function (IFormDocument $document, array $parameters) {
+                        $parameters['data']['name'] = \str_replace(
+                            ',',
+                            '',
+                            StringUtil::trim($parameters['data']['name'])
+                        );
+
+                        return $parameters;
+                    }
+                )
+            )
+            ->addProcessor(
+                new CustomFormDataProcessor(
+                    'synonymsProcessor',
+                    null,
+                    static function (IFormDocument $document, array $data, IStorableObject $tag) {
+                        \assert($tag instanceof Tag);
+
+                        $synonymList = new TagList();
+                        $synonymList->getConditionBuilder()->add('synonymFor = ?', [$tag->tagID]);
+                        $synonymList->readObjects();
+                        $data['synonyms'] = [];
+                        foreach ($synonymList as $synonym) {
+                            $data['synonyms'][] = $synonym->name;
+                        }
+
+                        return $data;
+                    }
+                )
+            );
     }
 }
index 754a8f5af18f343a97dd29bf7bf360405c0cb20b..d70b46cbfbeb1b39dc1d24d155333189dee17f6e 100644 (file)
@@ -2,13 +2,10 @@
 
 namespace wcf\acp\form;
 
+use CuyZ\Valinor\Mapper\MappingError;
 use wcf\data\tag\Tag;
-use wcf\data\tag\TagAction;
-use wcf\data\tag\TagEditor;
-use wcf\data\tag\TagList;
-use wcf\form\AbstractForm;
+use wcf\http\Helper;
 use wcf\system\exception\IllegalLinkException;
-use wcf\system\WCF;
 
 /**
  * Shows the tag edit form.
@@ -29,123 +26,32 @@ class TagEditForm extends TagAddForm
      */
     public $neededPermissions = ['admin.content.tag.canManageTag'];
 
-    /**
-     * tag id
-     * @var int
-     */
-    public $tagID = 0;
-
-    /**
-     * tag object
-     * @var Tag
-     */
-    public $tagObj;
-
     /**
      * @inheritDoc
      */
+    public $formAction = 'edit';
+
+    #[\Override]
     public function readParameters()
     {
         parent::readParameters();
 
-        if (isset($_REQUEST['id'])) {
-            $this->tagID = \intval($_REQUEST['id']);
-        }
-        $this->tagObj = new Tag($this->tagID);
-        if (!$this->tagObj->tagID) {
-            throw new IllegalLinkException();
-        }
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function save()
-    {
-        AbstractForm::save();
-
-        // update tag
-        $this->objectAction = new TagAction([$this->tagID], 'update', [
-            'data' => \array_merge($this->additionalFields, [
-                'name' => $this->name,
-            ]),
-        ]);
-        $this->objectAction->executeAction();
-
-        if ($this->tagObj->synonymFor === null) {
-            // remove synonyms first
-            $sql = "UPDATE  wcf1_tag
-                    SET     synonymFor = ?
-                    WHERE   synonymFor = ?";
-            $statement = WCF::getDB()->prepare($sql);
-            $statement->execute([
-                null,
-                $this->tagID,
-            ]);
-
-            $editor = new TagEditor($this->tagObj);
-            foreach ($this->synonyms as $synonym) {
-                if (empty($synonym)) {
-                    continue;
-                }
-
-                // find existing tag
-                $synonymObj = Tag::getTag($synonym, $this->tagObj->languageID);
-                if ($synonymObj === null) {
-                    $synonymAction = new TagAction([], 'create', [
-                        'data' => [
-                            'name' => $synonym,
-                            'languageID' => $this->tagObj->languageID,
-                            'synonymFor' => $this->tagID,
-                        ],
-                    ]);
-                    $synonymAction->executeAction();
-                } else {
-                    $editor->addSynonym($synonymObj);
-                }
+        try {
+            $queryParameters = Helper::mapQueryParameters(
+                $_GET,
+                <<<'EOT'
+                    array {
+                        id: positive-int
+                    }
+                    EOT
+            );
+            $this->formObject = new Tag($queryParameters['id']);
+
+            if (!$this->formObject->getObjectID()) {
+                throw new IllegalLinkException();
             }
+        } catch (MappingError) {
+            throw new IllegalLinkException();
         }
-
-        $this->saved();
-
-        // show success message
-        WCF::getTPL()->assign('success', true);
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function readData()
-    {
-        $this->languageID = $this->tagObj->languageID;
-
-        parent::readData();
-
-        if (empty($_POST)) {
-            $this->name = $this->tagObj->name;
-            $this->languageID = $this->tagObj->languageID;
-        }
-
-        $synonymList = new TagList();
-        $synonymList->getConditionBuilder()->add('synonymFor = ?', [$this->tagObj->tagID]);
-        $synonymList->readObjects();
-        $this->synonyms = [];
-        foreach ($synonymList as $synonym) {
-            $this->synonyms[] = $synonym->name;
-        }
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function assignVariables()
-    {
-        parent::assignVariables();
-
-        WCF::getTPL()->assign([
-            'tagObj' => $this->tagObj,
-            'action' => 'edit',
-            'synonym' => ($this->tagObj !== null && $this->tagObj->synonymFor) ? new Tag($this->tagObj->synonymFor) : null,
-        ]);
     }
 }
index cd0dfbb58b301a665af20e27e3b1af48babf7c1c..957f87c5bc7851b8bd827efb6f4a513438332aaf 100644 (file)
@@ -16,9 +16,9 @@ use wcf\system\WCF;
  * @copyright   2001-2019 WoltLab GmbH
  * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  *
- * @method  Tag     create()
  * @method  TagEditor[] getObjects()
  * @method  TagEditor   getSingleObject()
+ * @property TagEditor[] $objects
  */
 class TagAction extends AbstractDatabaseObjectAction implements ISearchAction
 {
@@ -66,6 +66,67 @@ class TagAction extends AbstractDatabaseObjectAction implements ISearchAction
         }
     }
 
+    #[\Override]
+    public function create()
+    {
+        $tag = parent::create();
+        \assert($tag instanceof Tag);
+
+        $editor = new TagEditor($tag);
+        $this->saveSynonyms($editor);
+
+        return $tag;
+    }
+
+    #[\Override]
+    public function update()
+    {
+        parent::update();
+
+        foreach ($this->objects as $tagEditor) {
+            if ($tagEditor->synonymFor !== null) {
+                continue;
+            }
+
+            // remove synonyms first
+            $sql = "UPDATE  wcf1_tag
+                    SET     synonymFor = ?
+                    WHERE   synonymFor = ?";
+            $statement = WCF::getDB()->prepare($sql);
+            $statement->execute([
+                null,
+                $tagEditor->tagID,
+            ]);
+
+            $this->saveSynonyms($tagEditor);
+        }
+    }
+
+    private function saveSynonyms(TagEditor $tagEditor): void
+    {
+        $synonyms = $this->parameters['synonyms'] ?? [];
+        foreach ($synonyms as $synonym) {
+            if (empty($synonym)) {
+                continue;
+            }
+
+            // find existing tag
+            $synonymObj = Tag::getTag($synonym, $tagEditor->languageID);
+            if ($synonymObj === null) {
+                $synonymAction = new TagAction([], 'create', [
+                    'data' => [
+                        'name' => $synonym,
+                        'languageID' => $tagEditor->languageID,
+                        'synonymFor' => $tagEditor->tagID,
+                    ],
+                ]);
+                $synonymAction->executeAction();
+            } else {
+                $tagEditor->addSynonym($synonymObj);
+            }
+        }
+    }
+
     /**
      * @inheritDoc
      */