<controller><![CDATA[wcf\acp\page\PageListPage]]></controller>
<parent>wcf.acp.menu.link.cms</parent>
<permissions>admin.content.cms.canManagePage</permissions>
- </acpmenuitem>
+ </acpmenuitem>
+
+ <acpmenuitem name="wcf.acp.menu.link.cms.menu.list">
+ <controller><![CDATA[wcf\acp\page\MenuListPage]]></controller>
+ <parent>wcf.acp.menu.link.cms</parent>
+ <permissions>admin.content.cms.canManageMenu</permissions>
+ </acpmenuitem>
</import>
</data>
<usersonly>1</usersonly>
</option>
+ <option name="admin.content.cms.canManageMenu">
+ <categoryname>admin.content</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>0</defaultvalue>
+ <admindefaultvalue>1</admindefaultvalue>
+ <usersonly>1</usersonly>
+ </option>
+
<!-- user.message -->
<option name="user.message.canUseSmilies">
<categoryname>user.message</categoryname>
--- /dev/null
+{include file='header' pageTitle='wcf.acp.menu.'|concat:$action}
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.menu.{$action}{/lang}</h1>
+</header>
+
+{include file='formError'}
+
+{if $success|isset}
+ <p class="success">{lang}wcf.global.success.{$action}{/lang}</p>
+{/if}
+
+<div class="contentNavigation">
+ <nav>
+ <ul>
+ <li><a href="{link controller='MenuList'}{/link}" class="button"><span class="icon icon16 fa-list"></span> <span>{lang}wcf.acp.menu.list{/lang}</span></a></li>
+
+ {event name='contentNavigationButtons'}
+ </ul>
+ </nav>
+</div>
+
+<form method="post" action="{if $action == 'add'}{link controller='MenuAdd'}{/link}{else}{link controller='MenuEdit' id=$menuID}{/link}{/if}">
+ <div class="container containerPadding marginTop">
+ <fieldset>
+ <legend>{lang}wcf.global.form.data{/lang}</legend>
+
+ <dl{if $errorField == 'title'} class="formError"{/if}>
+ <dt><label for="title">{lang}wcf.global.title{/lang}</label></dt>
+ <dd>
+ <input type="text" id="title" name="title" value="{$i18nPlainValues['title']}" autofocus="autofocus" class="long" />
+ {if $errorField == 'title'}
+ <small class="innerError">
+ {if $errorType == 'title' || $errorType == 'multilingual'}
+ {lang}wcf.global.form.error.{@$errorType}{/lang}
+ {else}
+ {lang}wcf.acp.menu.title.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ <small>{lang}wcf.acp.menu.title.description{/lang}</small>
+ {include file='multipleLanguageInputJavascript' elementIdentifier='title' forceSelection=false}
+ </dd>
+ </dl>
+
+ {event name='dataFields'}
+ </fieldset>
+
+ {event name='fieldsets'}
+ </div>
+
+ <div class="formSubmit">
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
+ {@SECURITY_TOKEN_INPUT_TAG}
+ </div>
+</form>
+
+{include file='footer'}
--- /dev/null
+{include file='header' pageTitle='wcf.acp.menu.item.'|concat:$action}
+
+<script data-relocate="true">
+ //<![CDATA[
+ $(function() {
+ var $isInternalLink = $('input[name=isInternalLink]').filter('[value=1]');
+ var $pageIDContainer = $('#pageIDContainer');
+ var $externalURLContainer = $('#externalURLContainer');
+
+ function handleIsInternalLink() {
+ if ($isInternalLink.is(':checked')) {
+ $pageIDContainer.show();
+ $externalURLContainer.hide();
+ }
+ else {
+ $pageIDContainer.hide();
+ $externalURLContainer.show();
+ }
+ }
+
+ $('input[name=isInternalLink]').change(handleIsInternalLink);
+ handleIsInternalLink();
+ });
+ //]]>
+</script>
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.menu.item.{$action}{/lang}</h1>
+</header>
+
+{include file='formError'}
+
+{if $success|isset}
+ <p class="success">{lang}wcf.global.success.{$action}{/lang}</p>
+{/if}
+
+<div class="contentNavigation">
+ <nav>
+ <ul>
+ <li><a href="{link controller='MenuItemList' id=$menuID}{/link}" class="button"><span class="icon icon16 fa-list"></span> <span>{lang}wcf.acp.menu.item.list{/lang}</span></a></li>
+
+ {event name='contentNavigationButtons'}
+ </ul>
+ </nav>
+</div>
+
+<form method="post" action="{if $action == 'add'}{link controller='MenuItemAdd'}{/link}{else}{link controller='MenuItemEdit' id=$itemID}{/link}{/if}">
+ <div class="container containerPadding marginTop">
+ <fieldset>
+ <legend>{lang}wcf.acp.pageMenu.data{/lang}</legend>
+
+
+ <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->getMenuItem()->itemID}"{if $menuItemNode->getMenuItem()->itemID == $parentItemID} selected="selected"{/if}>{if $menuItemNode->getDepth() > 1}{@" "|str_repeat:($menuItemNode->getDepth() - 1)}{/if}{lang}{$menuItemNode->getMenuItem()->title}{/lang}</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="{$title}" class="long" required="required" />
+ {if $errorField == 'title'}
+ <small class="innerError">
+ {if $errorType == 'multilingual'}
+ {lang}wcf.global.form.error.multilingual{/lang}
+ {else}
+ {lang}wcf.acp.menu.item.title.error.{$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+
+ {include file='multipleLanguageInputJavascript' elementIdentifier='title' forceSelection=false}
+ </dd>
+ </dl>
+
+ {event name='dataFields'}
+ </fieldset>
+
+ <fieldset>
+ <legend>{lang}wcf.acp.menu.item.link{/lang}</legend>
+
+ <dl>
+ <dt></dt>
+ <dd class="floated">
+ <label><input type="radio" name="isInternalLink" value="1"{if $isInternalLink} checked="checked"{/if} /> {lang}wcf.acp.menu.item.link.internal{/lang}</label>
+ <label><input type="radio" name="isInternalLink" value="0"{if !$isInternalLink} checked="checked"{/if} /> {lang}wcf.acp.menu.item.link.external{/lang}</label>
+ </dd>
+ </dl>
+
+ <dl id="pageIDContainer"{if $errorField == 'pageID'} class="formError"{/if}>
+ <dt><label for="pageID">{lang}wcf.acp.page.parentPageID{/lang}</label></dt>
+ <dd>
+ <select name="pageID" id="pageID">
+ <option value="0">{lang}wcf.global.noSelection{/lang}</option>
+
+ {foreach from=$pageNodeList item=pageNode}
+ <option value="{@$pageNode->getPage()->pageID}"{if $pageNode->getPage()->pageID == $pageID} selected="selected"{/if}>{if $pageNode->getDepth() > 1}{@" "|str_repeat:($pageNode->getDepth() - 1)}{/if}{$pageNode->getPage()->displayName}</option>
+ {/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="externalURLContainer"{if $errorField == 'externalURL'} class="formError"{/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" />
+ {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='multipleLanguageInputJavascript' elementIdentifier='externalURL' forceSelection=false}
+ </dd>
+ </dl>
+
+ {event name='linkFields'}
+ </fieldset>
+
+ <fieldset>
+ <legend>{lang}wcf.acp.menu.item.advanced{/lang}</legend>
+
+ <dl>
+ <dt><label for="showOrder">{lang}wcf.acp.menu.item.showOrder{/lang}</label></dt>
+ <dd>
+ <input type="number" name="showOrder" id="showOrder" value="{@$showOrder}" class="tiny" min="0" />
+ </dd>
+ </dl>
+
+ {if $action == 'add' || !$menuItem->isLandingPage}
+ <dl>
+ <dt></dt>
+ <dd>
+ <label><input type="checkbox" name="isDisabled" id="isDisabled" value="1"{if $isDisabled} checked="checked"{/if} /> <span>{lang}wcf.acp.menu.item.isDisabled{/lang}</span></label>
+ </dd>
+ </dl>
+ {/if}
+
+ {event name='advancedFields'}
+ </fieldset>
+
+ {event name='fields'}
+ </div>
+
+ <div class="formSubmit">
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" />
+ {if $action == 'add'}<input type="hidden" name="menuID" value="{@$menuID}" />{/if}
+ {@SECURITY_TOKEN_INPUT_TAG}
+ </div>
+</form>
+
+{include file='footer'}
--- /dev/null
+{include file='header' pageTitle='wcf.acp.menu.item.list'}
+
+<script data-relocate="true">
+ //<![CDATA[
+ $(function() {
+ new WCF.Action.Delete('wcf\\data\\menu\\item\\MenuItemAction', '.sortableNode', '> .sortableNodeLabel .jsDeleteButton');
+ new WCF.Action.Toggle('wcf\\data\\menu\\item\\MenuItemAction', '.sortableNode', '> .sortableNodeLabel .jsToggleButton');
+ new WCF.Sortable.List('menuItemList', 'wcf\\data\\menu\\item\\MenuItemAction', undefined, { protectRoot: true }, false, { menuID: '{@$menuID}' });
+ });
+ //]]>
+</script>
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.menu.item.list{/lang}</h1>
+</header>
+
+<div class="contentNavigation">
+ <nav>
+ <ul>
+ <li><a href="{link controller='MenuItemAdd'}menuID={@$menuID}{/link}" class="button"><span class="icon icon16 fa-plus"></span> <span>{lang}wcf.acp.menu.item.add{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsTop'}
+ </ul>
+ </nav>
+</div>
+
+{hascontent}
+ <div id="menuItemList" class="container containerPadding sortableListContainer marginTop">
+ <ol class="sortableList" data-object-id="0">
+ {content}
+ {foreach from=$menuItemNodeList item=menuItemNode}
+ <li class="sortableNode" data-object-id="{@$menuItemNode->getMenuItem()->itemID}">
+ <span class="sortableNodeLabel">
+ <a href="{link controller='MenuItemEdit' id=$menuItemNode->getMenuItem()->itemID}{/link}">{lang}{$menuItemNode->getMenuItem()->title}{/lang}</a>
+ <span class="statusDisplay sortableButtonContainer">
+ {if $menuItemNode->getMenuItem()->canDisable()}
+ <span class="icon icon16 icon-check{if $menuItemNode->getMenuItem()->isDisabled}-empty{/if} jsToggleButton jsTooltip pointer" title="{lang}wcf.global.button.{if $menuItemNode->getMenuItem()->isDisabled}enable{else}disable{/if}{/lang}" data-object-id="{@$menuItemNode->getMenuItem()->itemID}"></span>
+ {else}
+ <span class="icon icon16 icon-check{if $menuItemNode->getMenuItem()->isDisabled}-empty{/if} disabled" title="{lang}wcf.global.button.{if $menuItemNode->getMenuItem()->isDisabled}enable{else}disable{/if}{/lang}"></span>
+ {/if}
+ <a href="{link controller='MenuItemEdit' id=$menuItemNode->getMenuItem()->itemID}{/link}" class="jsTooltip" title="{lang}wcf.global.button.edit{/lang}"><span class="icon icon16 fa-pencil"></span></a>
+ {if $menuItemNode->getMenuItem()->canDelete()}
+ <span class="icon icon16 fa-times jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{@$menuItemNode->getMenuItem()->itemID}" data-confirm-message="{lang menuItem=$menuItemNode->getMenuItem()}wcf.acp.menu.item.delete.confirmMessage{/lang}"></span>
+ {else}
+ <span class="icon icon16 fa-times disabled" title="{lang}wcf.global.button.delete{/lang}"></span>
+ {/if}
+
+ {event name='itemButtons'}
+ </span>
+ </span>
+
+ <ol class="sortableList" data-object-id="{@$menuItemNode->getMenuItem()->itemID}">{if !$menuItemNode->hasChildren()}</ol></li>{/if}
+
+ {if !$menuItemNode->hasChildren() && $menuItemNode->isLastSibling()}
+ {@"</ol></li>"|str_repeat:$menuItemNode->getOpenParentNodes()}
+ {/if}
+ {/foreach}
+ {/content}
+ </ol>
+ </div>
+
+ <div class="formSubmit">
+ <button class="button buttonPrimary" data-type="submit">{lang}wcf.global.button.saveSorting{/lang}</button>
+ </div>
+{hascontentelse}
+ <p class="info">{lang}wcf.global.noItems{/lang}</p>
+{/hascontent}
+
+{include file='footer'}
--- /dev/null
+{include file='header' pageTitle='wcf.acp.menu.list'}
+
+<script data-relocate="true">
+ //<![CDATA[
+ $(function() {
+ new WCF.Action.Delete('wcf\\data\\menu\\MenuAction', '.jsMenuRow');
+ });
+ //]]>
+</script>
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.menu.list{/lang}</h1>
+</header>
+
+<div class="contentNavigation">
+ {pages print=true assign=pagesLinks controller="MenuList" link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder"}
+
+ <nav>
+ <ul>
+ <li><a href="{link controller='MenuAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}wcf.acp.menu.add{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsTop'}
+ </ul>
+ </nav>
+</div>
+
+{if $objects|count}
+ <div class="tabularBox tabularBoxTitle marginTop">
+ <header>
+ <h2>{lang}wcf.acp.menu.list{/lang} <span class="badge badgeInverse">{#$items}</span></h2>
+ </header>
+
+ <table class="table">
+ <thead>
+ <tr>
+ <th class="columnPageID{if $sortField == 'menuID'} active {@$sortOrder}{/if}" colspan="2"><a href="{link controller='MenuList'}pageNo={@$pageNo}&sortField=menuID&sortOrder={if $sortField == 'menuID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
+ <th class="columnTitle{if $sortField == 'title'} active {@$sortOrder}{/if}"><a href="{link controller='MenuList'}pageNo={@$pageNo}&sortField=title&sortOrder={if $sortField == 'title' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.global.name{/lang}</a></th>
+ <th class="columnDigits columnItems{if $sortField == 'customURL'} active {@$sortOrder}{/if}"><a href="{link controller='MenuList'}pageNo={@$pageNo}&sortField=items&sortOrder={if $sortField == 'items' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.acp.menu.items{/lang}</a></th>
+
+ {event name='columnHeads'}
+ </tr>
+ </thead>
+
+ <tbody>
+ {foreach from=$objects item=menu}
+ <tr class="jsMenuRow">
+ <td class="columnIcon">
+ <a href="{link controller='MenuEdit' id=$menu->menuID}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip"><span class="icon icon16 icon-pencil"></span></a>
+ <a href="{link controller='MenuItemList' id=$menu->menuID}{/link}" title="{lang}wcf.acp.menu.item.list{/lang}" class="jsTooltip"><span class="icon icon16 icon-list"></span></a>
+ {if $menu->canDelete()}
+ <span class="icon icon16 icon-remove jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{@$menu->menuID}" data-confirm-message="{lang}wcf.acp.menu.delete.confirmMessage{/lang}"></span>
+ {else}
+ <span class="icon icon16 icon-remove disabled" title="{lang}wcf.global.button.delete{/lang}"></span>
+ {/if}
+
+ {event name='rowButtons'}
+ </td>
+ <td class="columnID columnPageID">{@$menu->menuID}</td>
+ <td class="columnTitle"><a href="{link controller='MenuItemList' id=$menu->menuID}{/link}">{lang}{$menu->title}{/lang}</a></td>
+ <td class="columnDigits columnItems">{#$menu->items}</td>
+
+ {event name='columns'}
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+ </div>
+
+ <div class="contentNavigation">
+ {@$pagesLinks}
+
+
+ <nav>
+ <ul>
+ <li><a href="{link controller='MenuAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}wcf.acp.menu.add{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsBottom'}
+ </ul>
+ </nav>
+ </div>
+{/if}
+
+{include file='footer'}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\menu\MenuAction;
+use wcf\data\menu\MenuEditor;
+use wcf\form\AbstractForm;
+use wcf\system\exception\UserInputException;
+use wcf\system\language\I18nHandler;
+use wcf\system\WCF;
+
+/**
+ * Shows the menu add form.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class MenuAddForm extends AbstractForm {
+ /**
+ * @see \wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.cms.menu.list';
+
+ /**
+ * @see \wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.content.cms.canManageMenu');
+
+ /**
+ * menu title
+ * @var string
+ */
+ public $title = '';
+
+ /**
+ * @see \wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ I18nHandler::getInstance()->register('title');
+ }
+
+ /**
+ * @see \wcf\form\IForm::readFormParameters()
+ */
+ public function readFormParameters() {
+ parent::readFormParameters();
+
+ I18nHandler::getInstance()->readValues();
+
+ if (I18nHandler::getInstance()->isPlainValue('title')) $this->title = I18nHandler::getInstance()->getValue('title');
+ }
+
+ /**
+ * @see \wcf\form\IForm::validate()
+ */
+ public function validate() {
+ parent::validate();
+
+ // validate menu title
+ if (!I18nHandler::getInstance()->validateValue('title')) {
+ if (I18nHandler::getInstance()->isPlainValue('title')) {
+ throw new UserInputException('title');
+ }
+ else {
+ throw new UserInputException('title', 'multilingual');
+ }
+ }
+ }
+
+ /**
+ * @see \wcf\form\IForm::save()
+ */
+ public function save() {
+ parent::save();
+
+ // save label
+ $this->objectAction = new MenuAction(array(), 'create', array('data' => array_merge($this->additionalFields, array(
+ 'title' => $this->title,
+ 'packageID' => 1,
+ 'identifier' => ''
+ ))));
+ $returnValues = $this->objectAction->executeAction();
+ // set generic identifier
+ $menuEditor = new MenuEditor($returnValues['returnValues']);
+ $menuEditor->update(array(
+ 'identifier' => 'com.woltlab.wcf.generic'.$menuEditor->menuID
+ ));
+ // save i18n
+ if (!I18nHandler::getInstance()->isPlainValue('title')) {
+ I18nHandler::getInstance()->save('title', 'wcf.menu.menu'.$menuEditor->menuID, 'wcf.menu', 1);
+
+ // update title
+ $menuEditor->update(array(
+ 'title' => 'wcf.menu.menu'.$menuEditor->menuID
+ ));
+ }
+ $this->saved();
+
+ // reset values
+ $this->title = '';
+
+ // show success
+ WCF::getTPL()->assign(array(
+ 'success' => true
+ ));
+
+ I18nHandler::getInstance()->reset();
+ }
+
+ /**
+ * @see \wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ I18nHandler::getInstance()->assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'action' => 'add',
+ 'title' => 'title'
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\menu\MenuAction;
+use wcf\data\menu\Menu;
+use wcf\form\AbstractForm;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\language\I18nHandler;
+use wcf\system\WCF;
+
+/**
+ * Shows the menu edit form.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class MenuEditForm extends MenuAddForm {
+ /**
+ * menu id
+ * @var integer
+ */
+ public $menuID = 0;
+
+ /**
+ * menu object
+ * @var \wcf\data\menu\Menu
+ */
+ public $menu = null;
+
+ /**
+ * @see \wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ if (isset($_REQUEST['id'])) $this->menuID = intval($_REQUEST['id']);
+ $this->menu = new Menu($this->menuID);
+ if (!$this->menu->menuID) {
+ throw new IllegalLinkException();
+ }
+ }
+
+ /**
+ * @see \wcf\form\IForm::save()
+ */
+ public function save() {
+ AbstractForm::save();
+
+ $this->title = 'wcf.menu.menu'.$this->menu->menuID;
+ 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);
+ }
+
+ // update menu
+ $this->objectAction = new MenuAction(array($this->menuID), 'update', array('data' => array_merge($this->additionalFields, array(
+ 'title' => $this->title
+ ))));
+ $this->objectAction->executeAction();
+ $this->saved();
+
+ // show success
+ WCF::getTPL()->assign(array(
+ 'success' => true
+ ));
+ }
+
+ /**
+ * @see \wcf\page\IPage::readData()
+ */
+ public function readData() {
+ parent::readData();
+
+ if (empty($_POST)) {
+ I18nHandler::getInstance()->setOptions('title', 1, $this->menu->title, 'wcf.menu.menu\d+');
+
+ $this->title = $this->menu->title;
+ }
+ }
+
+ /**
+ * @see \wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ I18nHandler::getInstance()->assignVariables(!empty($_POST));
+
+ WCF::getTPL()->assign(array(
+ 'action' => 'edit',
+ 'menuID' => $this->menuID,
+ 'menu' => $this->menu
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\form\AbstractForm;
+use wcf\system\exception\UserInputException;
+use wcf\system\language\I18nHandler;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+use wcf\data\menu\Menu;
+use wcf\system\exception\IllegalLinkException;
+use wcf\data\menu\item\MenuItemNodeTree;
+use wcf\data\page\Page;
+use wcf\data\menu\item\MenuItem;
+use wcf\data\menu\item\MenuItemAction;
+use wcf\data\menu\item\MenuItemEditor;
+use wcf\data\page\PageNodeTree;
+
+/**
+ * Shows the menu item add form.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class MenuItemAddForm extends AbstractForm {
+ /**
+ * @see \wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.cms.menu.list';
+
+ /**
+ * @see \wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.content.cms.canManageMenu');
+
+ /**
+ * menu id
+ * @var integer
+ */
+ public $menuID = 0;
+
+ /**
+ * menu object
+ * @var \wcf\data\menu\Menu
+ */
+ public $menu = null;
+
+ /**
+ * activation state
+ * @var boolean
+ */
+ public $isDisabled = false;
+
+ /**
+ * internal link
+ * @var boolean
+ */
+ public $isInternalLink = true;
+
+ /**
+ * page id
+ * @var integer
+ */
+ public $pageID = null;
+
+ /**
+ * menu item title
+ * @var string
+ */
+ public $title = '';
+
+ /**
+ * external url
+ * @var string
+ */
+ public $externalURL = '';
+
+ /**
+ * id of the parent menu item
+ * @var integer
+ */
+ public $parentItemID = null;
+
+ /**
+ * show order
+ * @var integer
+ */
+ public $showOrder = 0;
+
+ /**
+ * menu item node tree
+ * @var wcf\data\menu\item\MenuItemNodeTree
+ */
+ public $menuItems = null;
+
+ /**
+ * @see \wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ if (isset($_REQUEST['menuID'])) $this->menuID = intval($_REQUEST['menuID']);
+ $this->menu = new Menu($this->menuID);
+ if (!$this->menu->menuID) {
+ throw new IllegalLinkException();
+ }
+
+ I18nHandler::getInstance()->register('title');
+ I18nHandler::getInstance()->register('externalURL');
+ }
+
+ /**
+ * @see \wcf\form\IForm::readFormParameters()
+ */
+ public function readFormParameters() {
+ 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['parentItemID'])) $this->parentItemID = intval($_POST['parentItemID']);
+ if (isset($_POST['showOrder'])) $this->showOrder = intval($_POST['showOrder']);
+ }
+
+ /**
+ * @see \wcf\form\IForm::validate()
+ */
+ public function validate() {
+ parent::validate();
+
+ // 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');
+ }
+ }
+ else {
+ $this->pageID = null;
+
+ // validate external url
+ if (!I18nHandler::getInstance()->validateValue('externalURL')) {
+ throw new UserInputException('externalURL');
+ }
+ }
+
+ // validate page menu item name
+ if (!I18nHandler::getInstance()->validateValue('title')) {
+ throw new UserInputException('title');
+ }
+
+ // validate parent menu item
+ if ($this->parentItemID) {
+ $parentMenuItem = new MenuItem($this->parentItemID);
+ if (!$parentMenuItem->itemID || $parentMenuItem->menuID != $this->menuID) {
+ throw new UserInputException('parentItemID', 'invalid');
+ }
+ }
+ }
+
+ /**
+ * @see \wcf\form\IForm::save()
+ */
+ public function save() {
+ parent::save();
+
+ $this->objectAction = new MenuItemAction(array(), 'create', array('data' => array_merge($this->additionalFields, array(
+ 'isDisabled' => ($this->isDisabled) ? 1 : 0,
+ 'title' => $this->title,
+ 'pageID' => $this->pageID,
+ 'externalURL' => $this->externalURL,
+ 'menuID' => $this->menuID,
+ 'parentItemID' => $this->parentItemID,
+ 'showOrder' => $this->showOrder,
+ 'identifier' => StringUtil::getRandomID()
+ ))));
+ $this->objectAction->executeAction();
+
+ $returnValues = $this->objectAction->getReturnValues();
+ $menuItem = $returnValues['returnValues'];
+
+ // set generic identifier
+ $data = array(
+ 'identifier' => 'com.woltlab.wcf.generic'.$menuItem->itemID
+ );
+ if (!I18nHandler::getInstance()->isPlainValue('title')) {
+ I18nHandler::getInstance()->save('title', 'wcf.menu.item.title'.$menuItem->itemID, 'wcf.menu');
+ $data['title'] = 'wcf.menu.item.title'.$menuItem->itemID;
+ }
+ 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;
+ }
+
+ // update values
+ $menuItemEditor = new MenuItemEditor($menuItem);
+ $menuItemEditor->update($data);
+
+ // call saved event
+ $this->saved();
+
+ // show success
+ WCF::getTPL()->assign('success', true);
+
+ // reset variables
+ $this->isDisabled = $this->isInternalLink = false;
+ $this->pageID = $this->parentItemID = null;
+ $this->externalURL = $this->title = '';
+ $this->showOrder = 0;
+
+ I18nHandler::getInstance()->reset();
+ }
+
+ /**
+ * @see \wcf\page\IPage::readData()
+ */
+ public function readData() {
+ parent::readData();
+
+ $this->menuItems = new MenuItemNodeTree($this->menuID);
+ }
+
+ /**
+ * @see \wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ I18nHandler::getInstance()->assignVariables();
+ $pageNodeList = new PageNodeTree();
+
+ WCF::getTPL()->assign(array(
+ 'action' => 'add',
+ 'menuID' => $this->menuID,
+ 'menu' => $this->menu,
+ 'isDisabled' => $this->isDisabled,
+ 'isInternalLink' => $this->isInternalLink,
+ 'pageID' => $this->pageID,
+ 'title' => $this->title,
+ 'externalURL' => $this->externalURL,
+ 'parentItemID' => $this->parentItemID,
+ 'showOrder' => $this->showOrder,
+ 'menuItemNodeList' => $this->menuItems->getNodeList(),
+ 'pageNodeList' => $pageNodeList->getNodeList()
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\menu\item\MenuItem;
+use wcf\data\menu\item\MenuItemAction;
+use wcf\data\menu\Menu;
+use wcf\form\AbstractForm;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\language\I18nHandler;
+use wcf\system\WCF;
+
+/**
+ * Shows the menu item edit form.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class MenuItemEditForm extends MenuItemAddForm {
+ /**
+ * menu item id
+ * @var integer
+ */
+ public $itemID = 0;
+
+ /**
+ * menu object
+ * @var \wcf\data\menu\Menu
+ */
+ public $menuItem = null;
+
+ /**
+ * @see \wcf\page\IPage::readParameters()
+ */
+ 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');
+ }
+
+ /**
+ * @see \wcf\form\IForm::save()
+ */
+ public function save() {
+ AbstractForm::save();
+
+ $this->title = 'wcf.menu.item.title'.$this->menuItem->itemID;
+ 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(array($this->itemID), 'update', array('data' => array_merge($this->additionalFields, array(
+ 'isDisabled' => ($this->isDisabled) ? 1 : 0,
+ 'title' => $this->title,
+ 'pageID' => $this->pageID,
+ 'externalURL' => $this->externalURL,
+ 'parentItemID' => $this->parentItemID,
+ 'showOrder' => $this->showOrder
+ ))));
+ $this->objectAction->executeAction();
+ $this->saved();
+
+ // show success
+ WCF::getTPL()->assign(array(
+ 'success' => true
+ ));
+ }
+
+ /**
+ * @see \wcf\page\IPage::readData()
+ */
+ public function readData() {
+ parent::readData();
+
+ if (empty($_POST)) {
+ I18nHandler::getInstance()->setOptions('title', 1, $this->menuItem->title, 'wcf.menu.item.title\d+');
+ I18nHandler::getInstance()->setOptions('externalURL', 1, $this->menuItem->externalURL, 'wcf.menu.item.externalURL\d+');
+
+ $this->parentItemID = $this->menuItem->parentItemID;
+ $this->title = $this->menuItem->title;
+ $this->pageID = $this->menuItem->pageID;
+ $this->externalURL = $this->menuItem->externalURL;
+ $this->showOrder = $this->menuItem->showOrder;
+ $this->isDisabled = $this->menuItem->isDisabled;
+ if (!$this->pageID) {
+ $this->isInternalLink = false;
+ }
+ }
+ }
+
+ /**
+ * @see \wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'action' => 'edit',
+ 'itemID' => $this->itemID,
+ 'menuItem' => $this->menuItem
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\page;
+use wcf\data\menu\item\MenuItemNodeTree;
+use wcf\data\menu\Menu;
+use wcf\page\AbstractPage;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\WCF;
+
+/**
+ * Shows a list of menu items.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.page
+ * @category Community Framework
+ */
+class MenuItemListPage extends AbstractPage {
+ /**
+ * @see \wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.cms.menu.list';
+
+ /**
+ * @see \wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.display.canManagePageMenu');
+
+ /**
+ * menu item list
+ * @var \wcf\data\menu\item\MenuItemList
+ */
+ public $objectList = null;
+
+ /**
+ * menu item node tree
+ * @var wcf\data\menu\item\MenuItemNodeTree
+ */
+ public $menuItems = null;
+
+ /**
+ * menu id
+ * @var integer
+ */
+ public $menuID = 0;
+
+ /**
+ * menu object
+ * @var \wcf\data\menu\Menu
+ */
+ public $menu = null;
+
+ /**
+ * @see \wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ if (isset($_REQUEST['id'])) $this->menuID = intval($_REQUEST['id']);
+ $this->menu = new Menu($this->menuID);
+ if (!$this->menu->menuID) {
+ throw new IllegalLinkException();
+ }
+ }
+
+ /**
+ * @see \wcf\page\IPage::readData()
+ */
+ public function readData() {
+ parent::readData();
+
+ $this->menuItems = new MenuItemNodeTree($this->menuID);
+ }
+
+ /**
+ * @see \wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'menuID' => $this->menuID,
+ 'menu' => $this->menu,
+ 'menuItemNodeList' => $this->menuItems->getNodeList()
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\page;
+use wcf\page\SortablePage;
+
+/**
+ * Shows a list of menus.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.page
+ * @category Community Framework
+ */
+class MenuListPage extends SortablePage {
+ /**
+ * @see \wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.cms.menu.list';
+
+ /**
+ * @see \wcf\page\MultipleLinkPage::$objectListClassName
+ */
+ public $objectListClassName = 'wcf\data\menu\MenuList';
+
+ /**
+ * @see \wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.content.cms.canManageMenu');
+
+ /**
+ * @see \wcf\page\SortablePage::$defaultSortField
+ */
+ public $defaultSortField = 'title';
+
+ /**
+ * @see \wcf\page\SortablePage::$validSortFields
+ */
+ public $validSortFields = array('menuID', 'title', 'items');
+}
--- /dev/null
+<?php
+namespace wcf\data\menu;
+use wcf\data\DatabaseObject;
+use wcf\system\WCF;
+
+/**
+ * Represents a menu.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.menu
+ * @category Community Framework
+ */
+class Menu extends DatabaseObject {
+ /**
+ * @see \wcf\data\DatabaseObject::$databaseTableName
+ */
+ protected static $databaseTableName = 'menu';
+
+ /**
+ * @see \wcf\data\DatabaseObject::$databaseTableIndexName
+ */
+ protected static $databaseTableIndexName = 'menuID';
+
+ /**
+ * Returns true if the active user can delete this menu.
+ *
+ * @return boolean
+ */
+ public function canDelete() {
+ if (WCF::getSession()->getPermission('admin.content.cms.canManageMenu') && !$this->originIsSystem) {
+ return true;
+ }
+
+ return false;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\menu;
+use wcf\data\AbstractDatabaseObjectAction;
+use wcf\system\exception\PermissionDeniedException;
+
+/**
+ * Executes menu related actions.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.menu
+ * @category Community Framework
+ */
+class MenuAction extends AbstractDatabaseObjectAction {
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$className
+ */
+ protected $className = 'wcf\data\menu\MenuEditor';
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsCreate
+ */
+ protected $permissionsCreate = array('admin.content.cms.canManageMenu');
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsDelete
+ */
+ protected $permissionsDelete = array('admin.content.cms.canManageMenu');
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsUpdate
+ */
+ protected $permissionsUpdate = array('admin.content.cms.canManageMenu');
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$requireACP
+ */
+ protected $requireACP = array('create', 'delete', 'update');
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::validateDelete()
+ */
+ public function validateDelete() {
+ parent::validateDelete();
+
+ foreach ($this->objects as $object) {
+ if (!$object->canDelete()) {
+ throw new PermissionDeniedException();
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\menu;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Provides functions to edit menus.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.menu
+ * @category Community Framework
+ */
+class MenuEditor extends DatabaseObjectEditor {
+ /**
+ * @see \wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\menu\Menu';
+}
--- /dev/null
+<?php
+namespace wcf\data\menu;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of menus.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.menu
+ * @category Community Framework
+ */
+class MenuList extends DatabaseObjectList {
+ /**
+ * @see \wcf\data\DatabaseObjectList::$className
+ */
+ public $className = 'wcf\data\menu\Menu';
+}
--- /dev/null
+<?php
+namespace wcf\data\menu\item;
+use wcf\data\DatabaseObject;
+use wcf\system\WCF;
+
+/**
+ * Represents a menu item.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.menu.item
+ * @category Community Framework
+ */
+class MenuItem extends DatabaseObject {
+ /**
+ * @see \wcf\data\DatabaseObject::$databaseTableName
+ */
+ protected static $databaseTableName = 'menu_item';
+
+ /**
+ * @see \wcf\data\DatabaseObject::$databaseTableIndexName
+ */
+ protected static $databaseTableIndexName = 'itemID';
+
+ /**
+ * Returns true if the active user can delete this menu item.
+ *
+ * @return boolean
+ */
+ public function canDelete() {
+ if (WCF::getSession()->getPermission('admin.content.cms.canManageMenu') && !$this->originIsSystem) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the active user can disable this menu item.
+ *
+ * @return boolean
+ */
+ public function canDisable() {
+ if (WCF::getSession()->getPermission('admin.content.cms.canManageMenu')) {
+ return true;
+ }
+
+ return false;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\menu\item;
+use wcf\data\AbstractDatabaseObjectAction;
+use wcf\data\IToggleAction;
+use wcf\system\exception\PermissionDeniedException;
+
+/**
+ * Executes menu item related actions.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.menu.item
+ * @category Community Framework
+ */
+class MenuItemAction extends AbstractDatabaseObjectAction implements IToggleAction {
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$className
+ */
+ protected $className = 'wcf\data\menu\item\MenuItemEditor';
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsCreate
+ */
+ protected $permissionsCreate = array('admin.content.cms.canManageMenu');
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsDelete
+ */
+ protected $permissionsDelete = array('admin.content.cms.canManageMenu');
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsUpdate
+ */
+ protected $permissionsUpdate = array('admin.content.cms.canManageMenu');
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$requireACP
+ */
+ protected $requireACP = array('create', 'delete', 'toggle', 'update');
+
+ /**
+ * @see \wcf\data\IToggleAction::validateToggle()
+ */
+ public function validateToggle() {
+ parent::validateUpdate();
+
+ foreach ($this->objects as $object) {
+ if (!$object->canDisable()) {
+ throw new PermissionDeniedException();
+ }
+ }
+ }
+
+ /**
+ * @see \wcf\data\IToggleAction::toggle()
+ */
+ public function toggle() {
+ foreach ($this->objects as $object) {
+ $object->update(array('isDisabled' => ($object->isDisabled) ? 0 : 1));
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\menu\item;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Provides functions to edit menu items.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.menu.item
+ * @category Community Framework
+ */
+class MenuItemEditor extends DatabaseObjectEditor {
+ /**
+ * @see \wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\menu\item\MenuItem';
+}
--- /dev/null
+<?php
+namespace wcf\data\menu\item;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of menu items.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.menu.item
+ * @category Community Framework
+ */
+class MenuItemList extends DatabaseObjectList {
+ /**
+ * @see \wcf\data\DatabaseObjectList::$className
+ */
+ public $className = 'wcf\data\menu\item\MenuItem';
+}
--- /dev/null
+<?php
+namespace wcf\data\menu\item;
+
+/**
+ * Represents a menu item node element.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.menu.item
+ * @category Community Framework
+ */
+class MenuItemNode implements \Countable, \RecursiveIterator {
+ /**
+ * parent node
+ * @var \wcf\data\menu\item\MenuItemNode
+ */
+ protected $parentNode = null;
+
+ /**
+ * children of this node
+ * @var array<\wcf\data\menu\item\MenuItemNode>
+ */
+ protected $children = array();
+
+ /**
+ * menu item object
+ * @var \wcf\data\menu\item\MenuItem
+ */
+ protected $menuItem = null;
+
+ /**
+ * node depth
+ * @var integer
+ */
+ protected $depth = 0;
+
+ /**
+ * iterator position
+ * @var integer
+ */
+ private $position = 0;
+
+ /**
+ * Creates a new MenuItemNode object.
+ *
+ * @param \wcf\data\menu\item\MenuItemNode $parentNode
+ * @param \wcf\data\menu\item\MenuItem $menuItem
+ * @param integer $depth
+ */
+ public function __construct($parentNode = null, MenuItem $menuItem = null, $depth = 0) {
+ $this->parentNode = $parentNode;
+ $this->menuItem = $menuItem;
+ $this->depth = $depth;
+ }
+
+ /**
+ * Sets the children of this node.
+ *
+ * @param array<\wcf\data\menu\item\MenuItemNode> $children
+ */
+ public function setChildren(array $children) {
+ $this->children = $children;
+ }
+
+ /**
+ * Returns the parent node
+ *
+ * @return \wcf\data\menu\item\MenuItemNode
+ */
+ public function getParentNode() {
+ return $this->parentNode;
+ }
+
+ /**
+ * Returns the menu item object of this node.
+ *
+ * @return \wcf\data\menu\item\MenuItem
+ */
+ public function getMenuItem() {
+ return $this->menuItem;
+ }
+
+ /**
+ * Returns the number of children.
+ *
+ * @return integer
+ */
+ public function count() {
+ return count($this->children);
+ }
+
+ /**
+ * Returns true if this element is the last sibling.
+ *
+ * @return boolean
+ */
+ public function isLastSibling() {
+ 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.
+ *
+ * @return integer
+ */
+ public function getOpenParentNodes() {
+ $element = $this;
+ $i = 0;
+
+ while ($element->parentNode->parentNode != null && $element->isLastSibling()) {
+ $i++;
+ $element = $element->parentNode;
+ }
+
+ return $i;
+ }
+
+ /**
+ * @see \Iterator::rewind()
+ */
+ public function rewind() {
+ $this->position = 0;
+ }
+
+ /**
+ * @see \Iterator::valid()
+ */
+ public function valid() {
+ return isset($this->children[$this->position]);
+ }
+
+ /**
+ * @see \Iterator::next()
+ */
+ public function next() {
+ $this->position++;
+ }
+
+ /**
+ * @see \Iterator::current()
+ */
+ public function current() {
+ return $this->children[$this->position];
+ }
+
+ /**
+ * @see \Iterator::key()
+ */
+ public function key() {
+ return $this->position;
+ }
+
+ /**
+ * @see \RecursiveIterator::getChildren()
+ */
+ public function getChildren() {
+ return $this->children[$this->position];
+ }
+
+ /**
+ * @see \RecursiveIterator::hasChildren()
+ */
+ public function hasChildren() {
+ return count($this->children) > 0;
+ }
+
+ /**
+ * Returns node depth.
+ *
+ * @return integer
+ */
+ public function getDepth() {
+ return $this->depth;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\menu\item;
+
+/**
+ * Represents a menu item node tree.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.menu.item
+ * @category Community Framework
+ */
+class MenuItemNodeTree {
+ /**
+ * menu id
+ * @var integer
+ */
+ public $menuID = null;
+
+ /**
+ * list of menu items
+ * @var array<\wcf\data\menu\item\MenuItem>
+ */
+ public $menuItems = array();
+
+ /**
+ * menu item structure
+ * @var array<array>
+ */
+ public $menuItemStructure = array();
+
+ /**
+ * root node
+ * @var \wcf\data\menu\item\MenuItemNode
+ */
+ public $node = null;
+
+ /**
+ * Creates a new MenuItemNodeTree object.
+ *
+ * @param integer $menuID
+ */
+ public function __construct($menuID) {
+ $this->menuID = $menuID;
+
+ // load menu items
+ $menuItemList = new MenuItemList();
+ $menuItemList->getConditionBuilder()->add('menu_item.menuID = ?', array($this->menuID));
+ $menuItemList->sqlOrderBy = "menu_item.showOrder";
+ $menuItemList->readObjects();
+
+ foreach ($menuItemList as $menuItem) {
+ $this->menuItems[$menuItem->itemID] = $menuItem;
+
+ if (!isset($this->menuItemStructure[$menuItem->parentItemID])) {
+ $this->menuItemStructure[$menuItem->parentItemID] = array();
+ }
+ $this->menuItemStructure[$menuItem->parentItemID][] = $menuItem->itemID;
+ }
+
+ // generate node tree
+ $this->node = new MenuItemNode();
+ $this->node->setChildren($this->generateNodeTree(null, $this->node));
+ }
+
+ /**
+ * Generates the node tree recursively
+ *
+ * @param integer $parentID
+ * @param \wcf\data\menu\item\MenuItemNode $parentNode
+ * @param array<integer> $filter
+ * @return array<\wcf\data\menu\item\MenuItemNode>
+ */
+ protected function generateNodeTree($parentID = null, MenuItemNode $parentNode = null) {
+ $nodes = array();
+
+ $itemIDs = (isset($this->menuItemStructure[$parentID]) ? $this->menuItemStructure[$parentID] : array());
+ foreach ($itemIDs as $itemID) {
+ $menuItem = $this->menuItems[$itemID];
+ $node = new MenuItemNode($parentNode, $menuItem, ($parentNode !== null ? ($parentNode->getDepth() + 1) : 0));
+ $nodes[] = $node;
+
+ // get children
+ $node->setChildren($this->generateNodeTree($itemID, $node));
+ }
+
+ return $nodes;
+ }
+
+ /**
+ * Returns the menu item node tree.
+ *
+ * @return array<\wcf\data\menu\item\MenuItemNode>
+ */
+ public function getNodeTree() {
+ return $this->node->getChildren();
+ }
+
+ /**
+ * Returns the iteratable node list
+ *
+ * @return \RecursiveIteratorIterator
+ */
+ public function getNodeList() {
+ return new \RecursiveIteratorIterator($this->node, \RecursiveIteratorIterator::SELF_FIRST);
+ }
+}
/**
* @see \wcf\data\IToggleAction::validateToggle()
- */
+ */
public function validateToggle() {
parent::validateUpdate();
UNIQUE KEY (objectTypeID, objectID)
);
+DROP TABLE IF EXISTS wcf1_menu;
+CREATE TABLE wcf1_menu (
+ menuID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ identifier VARCHAR(255) NOT NULL,
+ title VARCHAR(255) NOT NULL,
+ originIsSystem TINYINT(1) NOT NULL DEFAULT 0,
+ packageID INT(10) NOT NULL
+);
+
+DROP TABLE IF EXISTS wcf1_menu_item;
+CREATE TABLE wcf1_menu_item (
+ itemID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ menuID INT(10) NOT NULL,
+ parentItemID INT(10),
+ identifier VARCHAR(255) NOT NULL,
+ title VARCHAR(255) NOT NULL,
+ pageID INT(10),
+ externalURL VARCHAR(255) NOT NULL DEFAULT '',
+ showOrder INT(10) NOT NULL DEFAULT 0,
+ isDisabled TINYINT(1) NOT NULL DEFAULT 0,
+ originIsSystem TINYINT(1) NOT NULL DEFAULT 0
+);
+
DROP TABLE IF EXISTS wcf1_message_embedded_object;
CREATE TABLE wcf1_message_embedded_object (
messageObjectTypeID INT(10) NOT NULL,
ALTER TABLE wcf1_language_item ADD FOREIGN KEY (languageCategoryID) REFERENCES wcf1_language_category (languageCategoryID) ON DELETE CASCADE;
ALTER TABLE wcf1_language_item ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
+ALTER TABLE wcf1_menu ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
+
+ALTER TABLE wcf1_menu_item ADD FOREIGN KEY (menuID) REFERENCES wcf1_menu (menuID) ON DELETE CASCADE;
+ALTER TABLE wcf1_menu_item ADD FOREIGN KEY (parentItemID) REFERENCES wcf1_menu_item (itemID) ON DELETE SET NULL;
+ALTER TABLE wcf1_menu_item ADD FOREIGN KEY (pageID) REFERENCES wcf1_page (pageID) ON DELETE CASCADE;
+
ALTER TABLE wcf1_modification_log ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
ALTER TABLE wcf1_modification_log ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE SET NULL;