Use FormBuild to create menu items
authorCyperghost <olaf_schmitz_1@t-online.de>
Wed, 20 Nov 2024 11:25:04 +0000 (12:25 +0100)
committerCyperghost <olaf_schmitz_1@t-online.de>
Wed, 20 Nov 2024 11:25:04 +0000 (12:25 +0100)
wcfsetup/install/files/acp/templates/menuItemAdd.tpl
wcfsetup/install/files/lib/acp/form/MenuItemAddForm.class.php
wcfsetup/install/files/lib/acp/form/MenuItemEditForm.class.php
wcfsetup/install/files/lib/data/menu/item/MenuItemAction.class.php
wcfsetup/install/files/lib/data/menu/item/MenuItemNode.class.php

index 6f6380782e3eff3fbbc3a1f379ea5d98aaa89cb5..48103d579141639e29407bc460b8116f073a8b76 100644 (file)
@@ -1,33 +1,5 @@
 {include file='header' pageTitle='wcf.acp.menu.item.'|concat:$action}
 
-<script data-relocate="true">
-       require(['Dictionary', 'Language', 'WoltLabSuite/Core/Acp/Ui/Menu/Item/Handler'], function(Dictionary, Language, AcpUiMenuItemHandler) {
-               Language.addObject({
-                       'wcf.page.pageObjectID': '{jslang}wcf.page.pageObjectID{/jslang}',
-                       {foreach from=$pageNodeList item=pageNode}
-                               {capture assign='pageObjectIDLanguageItem'}{lang __optional=true}wcf.page.pageObjectID.{@$pageNode->identifier}{/lang}{/capture}
-                               {if $pageObjectIDLanguageItem}
-                                       'wcf.page.pageObjectID.{@$pageNode->identifier}': '{@$pageObjectIDLanguageItem|encodeJS}',
-                               {/if}
-                               {capture assign='pageObjectIDLanguageItem'}{lang __optional=true}wcf.page.pageObjectID.search.{@$pageNode->identifier}{/lang}{/capture}
-                               {if $pageObjectIDLanguageItem}
-                                       'wcf.page.pageObjectID.search.{@$pageNode->identifier}': '{@$pageObjectIDLanguageItem|encodeJS}',
-                               {/if}
-                       {/foreach}
-                       'wcf.page.pageObjectID.search.noResults': '{jslang}wcf.page.pageObjectID.search.noResults{/jslang}',
-                       'wcf.page.pageObjectID.search.results': '{jslang}wcf.page.pageObjectID.search.results{/jslang}',
-                       'wcf.page.pageObjectID.search.terms': '{jslang}wcf.page.pageObjectID.search.terms{/jslang}'
-               });
-               
-               var handlers = new Dictionary();
-               {foreach from=$pageHandlers key=handlerPageID item=requireObjectID}
-                       handlers.set({@$handlerPageID}, {if $requireObjectID}true{else}false{/if});
-               {/foreach}
-               
-               AcpUiMenuItemHandler.init(handlers);
-       });
-</script>
-
 <header class="contentHeader">
        <div class="contentHeaderTitle">
                <h1 class="contentTitle">{lang}wcf.acp.menu.item.{$action}{/lang}</h1>
@@ -47,7 +19,7 @@
                                        <div class="dropdownMenu">
                                                <ul class="scrollableDropdownMenu">
                                                        {foreach from=$menuItemNodeList item='menuItemNode'}
-                                                               <li{if $menuItemNode->itemID == $itemID} class="active"{/if}><a href="{link controller='MenuItemEdit' object=$menuItemNode}{/link}">{if $menuItemNode->getDepth() > 1}{@"&nbsp;&nbsp;&nbsp;&nbsp;"|str_repeat:($menuItemNode->getDepth() - 1)}{/if}{$menuItemNode->getTitle()}</a></li>
+                                                               <li{if $menuItemNode->itemID == $formObject->itemID} class="active"{/if}><a href="{link controller='MenuItemEdit' object=$menuItemNode}{/link}">{if $menuItemNode->getDepth() > 1}{@"&nbsp;&nbsp;&nbsp;&nbsp;"|str_repeat:($menuItemNode->getDepth() - 1)}{/if}{$menuItemNode->getTitle()}</a></li>
                                                        {/foreach}
                                                </ul>
                                        </div>
        </nav>
 </header>
 
-{include file='shared_formNotice'}
-
-<form method="post" action="{if $action == 'add'}{link controller='MenuItemAdd'}{/link}{else}{link controller='MenuItemEdit' id=$itemID}{/link}{/if}">
-       <div class="section">
-               <dl{if $errorField == 'parentItemID'} class="formError"{/if}>
-                       <dt><label for="parentItemID">{lang}wcf.acp.menu.item.parentItem{/lang}</label></dt>
-                       <dd>
-                               <select name="parentItemID" id="parentItemID">
-                                       <option value="0">{lang}wcf.global.noSelection{/lang}</option>
-                                       
-                                       {foreach from=$menuItemNodeList item=menuItemNode}
-                                               <option
-                                                       value="{$menuItemNode->itemID}"
-                                                       {if $menuItemNode->itemID == $parentItemID} selected{/if}
-                                                       {if $action === 'edit' && $menuItemNode->itemID == $itemID} disabled{/if}
-                                               >
-                                                       {if $menuItemNode->getDepth() > 1}{@"&nbsp;&nbsp;&nbsp;&nbsp;"|str_repeat:($menuItemNode->getDepth() - 1)}{/if}{$menuItemNode->getTitle()}
-                                               </option>
-                                       {/foreach}
-                               </select>
-                               {if $errorField == 'parentItemID'}
-                                       <small class="innerError">
-                                               {if $errorType == 'empty'}
-                                                       {lang}wcf.global.form.error.empty{/lang}
-                                               {else}
-                                                       {lang}wcf.acp.menu.item.parentItemID.error.{$errorType}{/lang}
-                                               {/if}
-                                       </small>
-                               {/if}
-                       </dd>
-               </dl>
-               
-               <dl{if $errorField == 'title'} class="formError"{/if}>
-                       <dt><label for="title">{lang}wcf.global.name{/lang}</label></dt>
-                       <dd>
-                               <input type="text" name="title" id="title" value="{$i18nPlainValues['title']}" maxlength="255" class="long" required>
-                               {if $errorField == 'title'}
-                                       <small class="innerError">
-                                               {if $errorType == 'empty' || $errorType == 'multilingual'}
-                                                       {lang}wcf.global.form.error.{@$errorType}{/lang}
-                                               {else}
-                                                       {lang}wcf.acp.menu.item.title.error.{$errorType}{/lang}
-                                               {/if}
-                                       </small>
-                               {/if}
-
-                               {include file='shared_multipleLanguageInputJavascript' elementIdentifier='title' forceSelection=false}
-                       </dd>
-               </dl>
-               
-               <dl>
-                       <dt><label for="showOrder">{lang}wcf.global.showOrder{/lang}</label></dt>
-                       <dd>
-                               <input type="number" name="showOrder" id="showOrder" value="{$showOrder}" class="tiny" min="0">
-                       </dd>
-               </dl>
-               
-               <dl>
-                       <dt></dt>
-                       <dd>
-                               <label><input type="checkbox" name="isDisabled" id="isDisabled" value="1"{if $isDisabled} checked{/if}> <span>{lang}wcf.acp.menu.item.isDisabled{/lang}</span></label>
-                       </dd>
-               </dl>
-               
-               {event name='dataFields'}
-       </div>
-       
-       <section class="section">
-               <h2 class="sectionTitle">{lang}wcf.acp.menu.item.link{/lang}</h2>
-       
-               <dl>
-                       <dt></dt>
-                       <dd class="floated">
-                               <label><input type="radio" name="isInternalLink" value="1"{if $isInternalLink} checked{/if}> {lang}wcf.acp.menu.item.link.internal{/lang}</label>
-                               <label><input type="radio" name="isInternalLink" value="0"{if !$isInternalLink} checked{/if}> {lang}wcf.acp.menu.item.link.external{/lang}</label>
-                       </dd>
-               </dl>
-               
-               <dl id="pageIDContainer"{if $errorField == 'pageID'} class="formError"{/if}{if !$isInternalLink} style="display: none;"{/if}>
-                       <dt><label for="pageID">{lang}wcf.acp.page.page{/lang}</label></dt>
-                       <dd>
-                               <select name="pageID" id="pageID">
-                                       <option value="0">{lang}wcf.global.noSelection{/lang}</option>
-                                       
-                                       {foreach from=$pageNodeList item=pageNode}
-                                               {if !$pageNode->requireObjectID || $pageHandlers[$pageNode->pageID]|isset}
-                                                       <option value="{$pageNode->pageID}"{if $pageNode->pageID == $pageID} selected{/if} data-identifier="{@$pageNode->identifier}">{if $pageNode->getDepth() > 1}{@"&nbsp;&nbsp;&nbsp;&nbsp;"|str_repeat:($pageNode->getDepth() - 1)}{/if}{$pageNode->name}</option>
-                                               {/if}
-                                       {/foreach}
-                               </select>
-                               {if $errorField == 'pageID'}
-                                       <small class="innerError">
-                                               {if $errorType == 'empty'}
-                                                       {lang}wcf.global.form.error.empty{/lang}
-                                               {else}
-                                                       {lang}wcf.acp.menu.item.pageID.error.{@$errorType}{/lang}
-                                               {/if}
-                                       </small>
-                               {/if}
-                       </dd>
-               </dl>
-               
-               <dl id="pageObjectIDContainer"{if $errorField == 'pageObjectID'} class="formError"{/if}{if !$pageID || !$pageHandler[$pageID]|isset} style="display: none;"{/if}>
-                       <dt><label for="pageObjectID">{lang}wcf.page.pageObjectID{/lang}</label></dt>
-                       <dd>
-                               <div class="inputAddon">
-                                       <input type="text" id="pageObjectID" name="pageObjectID" value="{$pageObjectID}" class="short">
-                                       <a href="#" id="searchPageObjectID" class="inputSuffix button jsTooltip" title="{lang}wcf.page.pageObjectID.search{/lang}">{icon name='magnifying-glass'}</a>
-                               </div>
-                               {if $errorField == 'pageObjectID'}
-                                       <small class="innerError">
-                                               {if $errorType == 'empty'}
-                                                       {lang}wcf.global.form.error.empty{/lang}
-                                               {else}
-                                                       {lang}wcf.acp.menu.item.pageObjectID.error.{@$errorType}{/lang}
-                                               {/if}
-                                       </small>
-                               {/if}
-                       </dd>
-               </dl>
-               
-               <dl id="externalURLContainer"{if $errorField == 'externalURL'} class="formError"{/if}{if $isInternalLink} style="display: none;"{/if}>
-                       <dt><label for="externalURL">{lang}wcf.acp.menu.item.externalURL{/lang}</label></dt>
-                       <dd>
-                               <input type="text" name="externalURL" id="externalURL" value="{$externalURL}" class="long" maxlength="255" placeholder="http://">
-                               {if $errorField == 'externalURL'}
-                                       <small class="innerError">
-                                               {if $errorType == 'empty'}
-                                                       {lang}wcf.global.form.error.empty{/lang}
-                                               {else}
-                                                       {lang}wcf.acp.menu.item.externalURL.error.{$errorType}{/lang}
-                                               {/if}
-                                       </small>
-                               {/if}
-
-                               {include file='shared_multipleLanguageInputJavascript' elementIdentifier='externalURL' forceSelection=false}
-                       </dd>
-               </dl>
-               
-               {event name='linkFields'}
-       </section>
-       
-       {event name='sections'}
-       
-       <div class="formSubmit">
-               <input type="submit" value="{lang}wcf.global.button.submit{/lang}">
-               {if $action == 'add'}<input type="hidden" name="menuID" value="{$menuID}">{/if}
-               {csrfToken}
-       </div>
-</form>
+{unsafe:$form->getHtml()}
 
 {include file='footer'}
index 3cd578b892af5c94b421b5de5584f2f81f7cab5b..4770d79fc97611a15fe0ee028ed20fd63a68ba03 100644 (file)
@@ -4,29 +4,40 @@ namespace wcf\acp\form;
 
 use wcf\data\menu\item\MenuItem;
 use wcf\data\menu\item\MenuItemAction;
-use wcf\data\menu\item\MenuItemEditor;
 use wcf\data\menu\item\MenuItemNodeTree;
 use wcf\data\menu\Menu;
 use wcf\data\page\Page;
 use wcf\data\page\PageNodeTree;
-use wcf\form\AbstractForm;
+use wcf\form\AbstractFormBuilderForm;
 use wcf\system\exception\IllegalLinkException;
-use wcf\system\exception\UserInputException;
-use wcf\system\language\I18nHandler;
+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\dependency\ValueFormFieldDependency;
+use wcf\system\form\builder\field\IntegerFormField;
+use wcf\system\form\builder\field\RadioButtonFormField;
+use wcf\system\form\builder\field\SelectFormField;
+use wcf\system\form\builder\field\SingleSelectionFormField;
+use wcf\system\form\builder\field\TitleFormField;
+use wcf\system\form\builder\field\UrlFormField;
+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\page\handler\ILookupPageHandler;
 use wcf\system\request\LinkHandler;
 use wcf\system\WCF;
-use wcf\util\StringUtil;
 
 /**
  * Shows the menu item add form.
  *
- * @author  Marcel Werk
- * @copyright   2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @since   3.0
+ * @author      Olaf Braun, Marcel Werk
+ * @copyright   2001-2024 WoltLab GmbH
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since       3.0
+ *
+ * @property   MenuItem $formObject
  */
-class MenuItemAddForm extends AbstractForm
+class MenuItemAddForm extends AbstractFormBuilderForm
 {
     /**
      * @inheritDoc
@@ -40,85 +51,27 @@ class MenuItemAddForm extends AbstractForm
 
     /**
      * menu id
-     * @var int
      */
-    public $menuID = 0;
+    public int $menuID = 0;
 
     /**
      * menu object
-     * @var Menu
-     */
-    public $menu;
-
-    /**
-     * activation state
-     * @var bool
-     */
-    public $isDisabled = false;
-
-    /**
-     * internal link
-     * @var bool
-     */
-    public $isInternalLink = true;
-
-    /**
-     * list of page handlers by page id
-     * @var \wcf\system\page\handler\IMenuPageHandler[]
-     */
-    public $pageHandlers = [];
-
-    /**
-     * page id
-     * @var int
-     */
-    public $pageID;
-
-    /**
-     * page object id
-     * @var int
-     */
-    public $pageObjectID;
-
-    /**
-     * menu item title
-     * @var string
-     */
-    public $title = '';
-
-    /**
-     * external url
-     * @var string
-     */
-    public $externalURL = '';
-
-    /**
-     * id of the parent menu item
-     * @var int
-     */
-    public $parentItemID;
-
-    /**
-     * show order
-     * @var int
      */
-    public $showOrder = 0;
+    public Menu $menu;
 
-    /**
-     * menu item node tree
-     * @var MenuItemNodeTree
-     */
-    public $menuItems;
+    public \RecursiveIteratorIterator $menuItemNodeList;
 
     /**
-     * nested list of page nodes
-     * @var \RecursiveIteratorIterator
+     * @inheritDoc
      */
-    public $pageNodeList;
+    public $objectEditLinkController = MenuItemEditForm::class;
 
     /**
      * @inheritDoc
      */
+    public $objectActionClass = MenuItemAction::class;
+
+    #[\Override]
     public function readParameters()
     {
         parent::readParameters();
@@ -130,225 +83,176 @@ class MenuItemAddForm extends AbstractForm
         if (!$this->menu->menuID) {
             throw new IllegalLinkException();
         }
-
-        I18nHandler::getInstance()->register('title');
-        I18nHandler::getInstance()->register('externalURL');
-
-        $this->pageNodeList = (new PageNodeTree())->getNodeList();
-
-        // fetch page handlers
-        foreach ($this->pageNodeList as $pageNode) {
-            $handler = $pageNode->getHandler();
-            if ($handler !== null) {
-                if ($handler instanceof ILookupPageHandler) {
-                    $this->pageHandlers[$pageNode->pageID] = $pageNode->requireObjectID;
-                }
-            }
-        }
     }
 
-    /**
-     * @inheritDoc
-     */
-    public function readFormParameters()
+    #[\Override]
+    protected function createForm()
     {
-        parent::readFormParameters();
-
-        I18nHandler::getInstance()->readValues();
-        if (I18nHandler::getInstance()->isPlainValue('title')) {
-            $this->title = I18nHandler::getInstance()->getValue('title');
-        }
-        if (I18nHandler::getInstance()->isPlainValue('externalURL')) {
-            $this->externalURL = I18nHandler::getInstance()->getValue('externalURL');
-        }
-
-        if (isset($_POST['isDisabled'])) {
-            $this->isDisabled = true;
-        }
-        $this->isInternalLink = false;
-        if (isset($_POST['isInternalLink'])) {
-            $this->isInternalLink = (bool)$_POST['isInternalLink'];
-        }
-        if (!empty($_POST['pageID'])) {
-            $this->pageID = \intval($_POST['pageID']);
-        }
-        if (!empty($_POST['pageObjectID'])) {
-            $this->pageObjectID = \intval($_POST['pageObjectID']);
-        }
-        if (!empty($_POST['parentItemID'])) {
-            $this->parentItemID = \intval($_POST['parentItemID']);
-        }
-        if (isset($_POST['showOrder'])) {
-            $this->showOrder = \intval($_POST['showOrder']);
-        }
+        parent::createForm();
+
+        $this->menuItemNodeList = (new MenuItemNodeTree($this->menuID, null, false))->getNodeList();
+
+        $this->form->appendChildren([
+            FormContainer::create('generalContainer')
+                ->appendChildren([
+                    SelectFormField::create('parentItemID')
+                        ->label('wcf.acp.menu.item.parentItem')
+                        ->options($this->menuItemNodeList, true),
+                    TitleFormField::create()
+                        ->i18n()
+                        ->required()
+                        ->languageItemPattern('wcf.menu.item.[\w\.]+'),
+                    IntegerFormField::create('showOrder')
+                        ->label('wcf.global.showOrder')
+                        ->minimum(0)
+                        ->value(0),
+                    BooleanFormField::create('isDisabled')
+                        ->label('wcf.acp.menu.item.isDisabled')
+                        ->value(false)
+                ]),
+            FormContainer::create('linkContainer')
+                ->label('wcf.acp.menu.item.link')
+                ->appendChildren([
+                    RadioButtonFormField::create('isInternalLink')
+                        ->options([
+                            0 => 'wcf.acp.menu.item.link.external',
+                            1 => 'wcf.acp.menu.item.link.internal',
+                        ])
+                        ->value(1),
+                    SingleSelectionFormField::create('pageID')
+                        ->label('wcf.acp.page.page')
+                        ->options((new PageNodeTree())->getNodeList(), true)
+                        ->required()
+                        ->addDependency(
+                            ValueFormFieldDependency::create('isInternalLinkDependency')
+                                ->fieldId('isInternalLink')
+                                ->values([1])
+                        ),
+                    // TODO change this to an new FormField
+                    IntegerFormField::create('pageObjectID')
+                        ->label('wcf.page.pageObjectID')
+                        ->addFieldClass('short')
+                        ->addValidator(
+                            new FormFieldValidator('requiredObjectIDValidator', function (IntegerFormField $formField) {
+                                $pageFormField = $this->form->getNodeById('pageID');
+                                \assert($pageFormField instanceof SingleSelectionFormField);
+                                $pageID = $pageFormField->getValue();
+                                $page = new Page($pageID);
+                                $pageObjectID = $formField->getValue();
+
+                                if (!$page->pageID) {
+                                    return;
+                                }
+
+                                if ($page->requireObjectID) {
+                                    $pageHandler = $page->getHandler();
+
+                                    if ($pageHandler instanceof ILookupPageHandler) {
+                                        if (empty($pageObjectID)) {
+                                            $formField->addValidationError(new FormFieldValidationError('empty'));
+                                            return;
+                                        }
+                                        if (!$pageHandler->isValid($pageObjectID)) {
+                                            $formField->addValidationError(
+                                                new FormFieldValidationError(
+                                                    'invalid',
+                                                    'wcf.acp.menu.item.pageObjectID.error.invalid'
+                                                )
+                                            );
+                                        }
+                                    } elseif ($pageHandler !== null) {
+                                        // page requires an object id, but no handler is registered
+                                        $pageFormField->addValidationError(
+                                            new FormFieldValidationError(
+                                                'invalid',
+                                                'wcf.acp.menu.item.pageID.error.invalid'
+                                            )
+                                        );
+                                    }
+                                }
+                            })
+                        )
+                        ->addDependency(
+                            ValueFormFieldDependency::create('isInternalLinkDependency')
+                                ->fieldId('isInternalLink')
+                                ->values([1])
+                        ),
+                    UrlFormField::create('externalURL')
+                        ->label('wcf.acp.menu.item.externalURL')
+                        ->maximumLength(255)
+                        ->placeholder('http://')
+                        ->i18n()
+                        ->required()
+                        ->languageItemPattern('wcf.menu.item.externalURL\d+')
+                        ->addDependency(
+                            ValueFormFieldDependency::create('isInternalLinkDependency')
+                                ->fieldId('isInternalLink')
+                                ->values([0])
+                        )
+                ])
+        ]);
     }
 
-    /**
-     * @inheritDoc
-     */
-    public function validate()
+    #[\Override]
+    protected function finalizeForm()
     {
-        parent::validate();
-
-        // validate parent menu item
-        if ($this->parentItemID) {
-            $parentMenuItem = new MenuItem($this->parentItemID);
-            if (!$parentMenuItem->itemID || $parentMenuItem->menuID != $this->menuID) {
-                throw new UserInputException('parentItemID', 'invalid');
-            }
-        }
-
-        // validate page menu item name
-        if (!I18nHandler::getInstance()->validateValue('title')) {
-            if (I18nHandler::getInstance()->isPlainValue('title')) {
-                throw new UserInputException('title');
-            } else {
-                throw new UserInputException('title', 'multilingual');
-            }
-        }
-
-        // validate menu item controller
-        if ($this->isInternalLink) {
-            $this->externalURL = '';
-
-            if (!$this->pageID) {
-                throw new UserInputException('pageID');
-            }
-            $page = new Page($this->pageID);
-            if (!$page->pageID) {
-                throw new UserInputException('pageID', 'invalid');
-            }
-
-            // validate page object id
-            if ($page->requireObjectID) {
-                if (isset($this->pageHandlers[$page->pageID])) {
-                    if ($this->pageHandlers[$page->pageID] && !$this->pageObjectID) {
-                        throw new UserInputException('pageObjectID');
+        parent::finalizeForm();
+
+        $this->form
+            ->getDataHandler()
+            ->addProcessor(
+                new CustomFormDataProcessor(
+                    'externalLinkDataProcessor',
+                    function (IFormDocument $document, array $parameters) {
+                        if ($parameters['data']['isInternalLink']) {
+                            $parameters['data']['externalURL'] = '';
+                        } else {
+                            $parameters['data']['pageID'] = null;
+                            $parameters['data']['pageObjectID'] = null;
+                        }
+                        unset($parameters['data']['isInternalLink']);
+
+                        return $parameters;
                     }
-
-                    /** @var ILookupPageHandler $handler */
-                    $handler = $page->getHandler();
-                    if ($this->pageObjectID && !$handler->isValid($this->pageObjectID)) {
-                        throw new UserInputException('pageObjectID', 'invalid');
-                    }
-                } else {
-                    // page requires an object id, but no handler is registered
-                    throw new UserInputException('pageID', 'invalid');
-                }
-            }
-        } else {
-            $this->pageID = $this->pageObjectID = null;
-
-            // validate external url
-            if (!I18nHandler::getInstance()->validateValue('externalURL')) {
-                throw new UserInputException('externalURL');
-            }
-        }
+                )
+            );
     }
 
-    /**
-     * @inheritDoc
-     */
+    #[\Override]
     public function save()
     {
-        parent::save();
-
-        $this->objectAction = new MenuItemAction([], 'create', [
-            'data' => \array_merge($this->additionalFields, [
-                'isDisabled' => $this->isDisabled ? 1 : 0,
-                'title' => $this->title,
-                'pageID' => $this->pageID,
-                'pageObjectID' => $this->pageObjectID ?: 0,
-                'externalURL' => $this->externalURL,
-                'menuID' => $this->menuID,
-                'parentItemID' => $this->parentItemID,
-                'showOrder' => $this->showOrder,
-                'identifier' => StringUtil::getRandomID(),
-                'packageID' => 1,
-            ]),
-        ]);
-        $this->objectAction->executeAction();
-
-        $returnValues = $this->objectAction->getReturnValues();
-        $menuItem = $returnValues['returnValues'];
-
-        // set generic identifier
-        $data = [
-            'identifier' => 'com.woltlab.wcf.generic' . $menuItem->itemID,
-        ];
-        if (!I18nHandler::getInstance()->isPlainValue('title')) {
-            I18nHandler::getInstance()->save('title', 'wcf.menu.item.' . $data['identifier'], 'wcf.menu');
-            $data['title'] = 'wcf.menu.item.' . $data['identifier'];
-        }
-        if (!I18nHandler::getInstance()->isPlainValue('externalURL')) {
-            I18nHandler::getInstance()->save(
-                'externalURL',
-                'wcf.menu.item.externalURL' . $menuItem->itemID,
-                'wcf.menu'
-            );
-            $data['externalURL'] = 'wcf.menu.item.externalURL' . $menuItem->itemID;
+        if ($this->formAction === 'create') {
+            $this->additionalFields['menuID'] = $this->menuID;
+            $this->additionalFields['identifier'] = '';
+            $this->additionalFields['packageID'] = PACKAGE_ID;
         }
 
-        // update values
-        $menuItemEditor = new MenuItemEditor($menuItem);
-        $menuItemEditor->update($data);
-
-        // call saved event
-        $this->saved();
-
-        // show success message
-        WCF::getTPL()->assign([
-            'success' => true,
-            'objectEditLink' => LinkHandler::getInstance()->getControllerLink(
-                MenuItemEditForm::class,
-                ['id' => $menuItem->itemID]
-            ),
-        ]);
-
-        // reset variables
-        $this->isInternalLink = true;
-        $this->isDisabled = false;
-        $this->pageID = $this->pageObjectID = $this->parentItemID = null;
-        $this->externalURL = $this->title = '';
-        $this->showOrder = 0;
-
-        I18nHandler::getInstance()->reset();
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function readData()
-    {
-        parent::readData();
-
-        $this->menuItems = new MenuItemNodeTree($this->menuID, null, false);
+        parent::save();
     }
 
-    /**
-     * @inheritDoc
-     */
+    #[\Override]
     public function assignVariables()
     {
         parent::assignVariables();
 
-        I18nHandler::getInstance()->assignVariables();
-
         WCF::getTPL()->assign([
-            'action' => 'add',
             'menuID' => $this->menuID,
             'menu' => $this->menu,
-            'isDisabled' => $this->isDisabled,
-            'isInternalLink' => $this->isInternalLink,
-            'pageID' => $this->pageID,
-            'pageObjectID' => $this->pageObjectID,
-            'title' => $this->title,
-            'externalURL' => $this->externalURL,
-            'parentItemID' => $this->parentItemID,
-            'showOrder' => $this->showOrder,
-            'menuItemNodeList' => $this->menuItems->getNodeList(),
-            'pageNodeList' => $this->pageNodeList,
-            'pageHandlers' => $this->pageHandlers,
+            'menuItemNodeList' => $this->menuItemNodeList,
         ]);
     }
+
+    #[\Override]
+    protected function setFormAction()
+    {
+        $this->form->action(
+            LinkHandler::getInstance()->getLink(
+                'MenuItemAdd',
+                [
+                    'menuID' => $this->menuID,
+                    'isACP' => true
+                ]
+            )
+        );
+    }
 }
index ebb12f9b8bbb0442446315515fdce11393a9c646..24da9f0af327756db7acd528f7f79bed9f360297 100644 (file)
@@ -2,16 +2,12 @@
 
 namespace wcf\acp\form;
 
+use CuyZ\Valinor\Mapper\MappingError;
 use wcf\data\menu\item\MenuItem;
-use wcf\data\menu\item\MenuItemAction;
 use wcf\data\menu\Menu;
-use wcf\data\page\PageNodeTree;
-use wcf\form\AbstractForm;
+use wcf\form\AbstractFormBuilderForm;
+use wcf\http\Helper;
 use wcf\system\exception\IllegalLinkException;
-use wcf\system\exception\UserInputException;
-use wcf\system\language\I18nHandler;
-use wcf\system\page\handler\ILookupPageHandler;
-use wcf\system\WCF;
 
 /**
  * Shows the menu item edit form.
@@ -23,152 +19,41 @@ use wcf\system\WCF;
  */
 class MenuItemEditForm extends MenuItemAddForm
 {
-    /**
-     * menu item id
-     * @var int
-     */
-    public $itemID = 0;
-
-    /**
-     * menu item object
-     * @var MenuItem
-     */
-    public $menuItem;
-
-    /**
-     * @inheritDoc
-     */
-    public function readParameters()
-    {
-        AbstractForm::readParameters();
-
-        if (isset($_REQUEST['id'])) {
-            $this->itemID = \intval($_REQUEST['id']);
-        }
-        $this->menuItem = new MenuItem($this->itemID);
-        if (!$this->menuItem->itemID) {
-            throw new IllegalLinkException();
-        }
-
-        $this->menu = new Menu($this->menuItem->menuID);
-        $this->menuID = $this->menu->menuID;
-
-        I18nHandler::getInstance()->register('title');
-        I18nHandler::getInstance()->register('externalURL');
-
-        $this->pageNodeList = (new PageNodeTree())->getNodeList();
-
-        // fetch page handlers
-        foreach ($this->pageNodeList as $pageNode) {
-            $handler = $pageNode->getHandler();
-            if ($handler !== null) {
-                if ($handler instanceof ILookupPageHandler) {
-                    $this->pageHandlers[$pageNode->pageID] = $pageNode->requireObjectID;
-                }
-            }
-        }
-    }
-
     /**
      * @inheritDoc
      */
-    public function validate()
-    {
-        parent::validate();
+    public $formAction = 'edit';
 
-        if ($this->parentItemID == $this->itemID) {
-            throw new UserInputException('parentItemID');
-        }
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function save()
-    {
-        AbstractForm::save();
-
-        $this->title = 'wcf.menu.item.' . $this->menuItem->identifier;
-        if (I18nHandler::getInstance()->isPlainValue('title')) {
-            I18nHandler::getInstance()->remove($this->title);
-            $this->title = I18nHandler::getInstance()->getValue('title');
-        } else {
-            I18nHandler::getInstance()->save('title', $this->title, 'wcf.menu', 1);
-        }
-        $this->externalURL = 'wcf.menu.item.externalURL' . $this->menuItem->itemID;
-        if (I18nHandler::getInstance()->isPlainValue('externalURL')) {
-            I18nHandler::getInstance()->remove($this->externalURL);
-            $this->externalURL = I18nHandler::getInstance()->getValue('externalURL');
-        } else {
-            I18nHandler::getInstance()->save('externalURL', $this->externalURL, 'wcf.menu', 1);
-        }
-
-        // update menu
-        $this->objectAction = new MenuItemAction([$this->itemID], 'update', [
-            'data' => \array_merge($this->additionalFields, [
-                'isDisabled' => $this->isDisabled ? 1 : 0,
-                'title' => $this->title,
-                'pageID' => $this->pageID,
-                'pageObjectID' => $this->pageObjectID ?: 0,
-                'externalURL' => $this->externalURL,
-                'parentItemID' => $this->parentItemID,
-                'showOrder' => $this->showOrder,
-            ]),
-        ]);
-        $this->objectAction->executeAction();
-        $this->saved();
-
-        // show success message
-        WCF::getTPL()->assign('success', true);
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function readData()
+    #[\Override]
+    public function readParameters()
     {
-        parent::readData();
-
-        if (empty($_POST)) {
-            I18nHandler::getInstance()->setOptions(
-                'title',
-                1,
-                $this->menuItem->title,
-                'wcf.menu.item.' . $this->menuItem->identifier
-            );
-            I18nHandler::getInstance()->setOptions(
-                'externalURL',
-                1,
-                $this->menuItem->externalURL,
-                'wcf.menu.item.externalURL\d+'
+        AbstractFormBuilderForm::readParameters();
+
+        try {
+            $queryParameters = Helper::mapQueryParameters(
+                $_GET,
+                <<<'EOT'
+                    array {
+                        id: positive-int
+                    }
+                    EOT
             );
+            $this->formObject = new MenuItem($queryParameters['id']);
 
-            $this->parentItemID = $this->menuItem->parentItemID;
-            $this->title = $this->menuItem->title;
-            $this->pageID = $this->menuItem->pageID;
-            $this->pageObjectID = $this->menuItem->pageObjectID;
-            $this->externalURL = $this->menuItem->externalURL;
-            $this->showOrder = $this->menuItem->showOrder;
-            $this->isDisabled = $this->menuItem->isDisabled;
-            if (!$this->pageID) {
-                $this->isInternalLink = false;
+            if (!$this->formObject->getObjectID()) {
+                throw new IllegalLinkException();
             }
+        } catch (MappingError) {
+            throw new IllegalLinkException();
         }
+
+        $this->menuID = $this->formObject->menuID;
+        $this->menu = new Menu($this->menuID);
     }
 
-    /**
-     * @inheritDoc
-     */
-    public function assignVariables()
+    #[\Override]
+    protected function setFormAction()
     {
-        parent::assignVariables();
-
-        I18nHandler::getInstance()->assignVariables(!empty($_POST));
-
-        WCF::getTPL()->assign([
-            'action' => 'edit',
-            'itemID' => $this->itemID,
-            'menuItem' => $this->menuItem,
-        ]);
+        AbstractFormBuilderForm::setFormAction();
     }
 }
index f8c10a03a901eb80a10a3695f86ed10e2e0127f2..f14767603b483be4839eacf168453ecc1a466980 100644 (file)
@@ -3,10 +3,12 @@
 namespace wcf\data\menu\item;
 
 use wcf\data\AbstractDatabaseObjectAction;
+use wcf\data\DatabaseObject;
 use wcf\data\ISortableAction;
 use wcf\data\IToggleAction;
 use wcf\data\menu\Menu;
 use wcf\data\TDatabaseObjectToggle;
+use wcf\data\TI18nDatabaseObjectAction;
 use wcf\system\exception\PermissionDeniedException;
 use wcf\system\exception\UserInputException;
 use wcf\system\WCF;
@@ -19,13 +21,13 @@ use wcf\system\WCF;
  * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @since   3.0
  *
- * @method  MenuItem        create()
  * @method  MenuItemEditor[]    getObjects()
  * @method  MenuItemEditor      getSingleObject()
  */
 class MenuItemAction extends AbstractDatabaseObjectAction implements ISortableAction, IToggleAction
 {
     use TDatabaseObjectToggle;
+    use TI18nDatabaseObjectAction;
 
     /**
      * @inheritDoc
@@ -52,6 +54,37 @@ class MenuItemAction extends AbstractDatabaseObjectAction implements ISortableAc
      */
     protected $requireACP = ['create', 'delete', 'toggle', 'update'];
 
+    public function create()
+    {
+        // `title` column doesn't have a default value
+        $this->parameters['data']['title'] = $this->parameters['data']['title'] ?? '';
+
+        /** @var MenuItem $menuItem */
+        $menuItem = parent::create();
+
+        if (!$menuItem->identifier) {
+            $editor = new MenuItemEditor($menuItem);
+            $editor->update([
+                'identifier' => 'com.woltlab.wcf.generic' . $menuItem->itemID,
+            ]);
+            $menuItem = new MenuItem($menuItem->itemID);
+        }
+
+        $this->saveI18nValue($menuItem);
+
+        return $menuItem;
+    }
+
+    #[\Override]
+    public function update()
+    {
+        parent::update();
+
+        foreach ($this->getObjects() as $editor) {
+            $this->saveI18nValue($editor->getDecoratedObject());
+        }
+    }
+
     /**
      * @inheritDoc
      */
@@ -134,4 +167,33 @@ class MenuItemAction extends AbstractDatabaseObjectAction implements ISortableAc
         }
         WCF::getDB()->commitTransaction();
     }
+
+    #[\Override]
+    public function getI18nSaveTypes(): array
+    {
+        return ['title' => 'wcf.menu.item.\w+'];
+    }
+
+    #[\Override]
+    public function getLanguageCategory(): string
+    {
+        return 'wcf.menu';
+    }
+
+    #[\Override]
+    public function getPackageID(): int
+    {
+        return PACKAGE_ID;
+    }
+
+    protected function getLanguageItem(DatabaseObject $object, string $regex): string
+    {
+        \assert($object instanceof MenuItem);
+
+        return \str_replace(
+            '\w+',
+            $object->identifier ?: 'com.woltlab.wcf.generic' . $object->itemID,
+            $regex
+        );
+    }
 }
index de5431d9886b0b471b7ab46ca4a6125b2e482600..f3aeeb5b57498b8da3097a9b34f1237ee82e4bc4 100644 (file)
@@ -3,6 +3,8 @@
 namespace wcf\data\menu\item;
 
 use wcf\data\DatabaseObjectDecorator;
+use wcf\data\IObjectTreeNode;
+use wcf\data\TObjectTreeNode;
 
 /**
  * Represents a menu item node element.
@@ -15,13 +17,9 @@ use wcf\data\DatabaseObjectDecorator;
  * @method  MenuItem    getDecoratedObject()
  * @mixin   MenuItem
  */
-class MenuItemNode extends DatabaseObjectDecorator implements \Countable, \RecursiveIterator
+class MenuItemNode extends DatabaseObjectDecorator implements IObjectTreeNode
 {
-    /**
-     * children of this node
-     * @var MenuItemNode[]
-     */
-    protected $children = [];
+    use TObjectTreeNode;
 
     /**
      * node depth
@@ -33,12 +31,6 @@ class MenuItemNode extends DatabaseObjectDecorator implements \Countable, \Recur
      */
     protected bool $isActive = false;
 
-    /**
-     * parent node
-     * @var MenuItemNode
-     */
-    protected $parentNode;
-
     /**
      * iterator position
      * @var int
@@ -78,54 +70,6 @@ class MenuItemNode extends DatabaseObjectDecorator implements \Countable, \Recur
         $this->children = $children;
     }
 
-    /**
-     * Returns the parent node
-     */
-    public function getParentNode(): self
-    {
-        return $this->parentNode;
-    }
-
-    /**
-     * Returns the number of children.
-     */
-    public function count(): int
-    {
-        return \count($this->children);
-    }
-
-    /**
-     * Returns true if this element is the last sibling.
-     */
-    public function isLastSibling(): bool
-    {
-        foreach ($this->parentNode as $key => $child) {
-            if ($child === $this) {
-                if ($key == \count($this->parentNode) - 1) {
-                    return true;
-                }
-
-                return false;
-            }
-        }
-    }
-
-    /**
-     * Returns the number of open parent nodes.
-     */
-    public function getOpenParentNodes(): int
-    {
-        $element = $this;
-        $i = 0;
-
-        while ($element->parentNode->parentNode != null && $element->isLastSibling()) {
-            $i++;
-            $element = $element->parentNode;
-        }
-
-        return $i;
-    }
-
     /**
      * Marks this item and all its direct ancestors as active.
      */
@@ -147,62 +91,6 @@ class MenuItemNode extends DatabaseObjectDecorator implements \Countable, \Recur
         return $this->isActive;
     }
 
-    /**
-     * @inheritDoc
-     */
-    public function rewind(): void
-    {
-        $this->position = 0;
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function valid(): bool
-    {
-        return isset($this->children[$this->position]);
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function next(): void
-    {
-        $this->position++;
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function current(): self
-    {
-        return $this->children[$this->position];
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function key(): int
-    {
-        return $this->position;
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function getChildren(): self
-    {
-        return $this->children[$this->position];
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function hasChildren(): bool
-    {
-        return \count($this->children) > 0;
-    }
-
     /**
      * Returns node depth.
      */