<permissions>admin.content.cms.canManageMenu</permissions>
<icon>fa-plus</icon>
</acpmenuitem>
+
+ <acpmenuitem name="wcf.acp.menu.link.cms.box.list">
+ <controller><![CDATA[wcf\acp\page\BoxListPage]]></controller>
+ <parent>wcf.acp.menu.link.cms</parent>
+ <permissions>admin.content.cms.canManageBox</permissions>
+ </acpmenuitem>
+
+ <acpmenuitem name="wcf.acp.menu.link.cms.box.add">
+ <controller><![CDATA[wcf\acp\form\BoxAddForm]]></controller>
+ <parent>wcf.acp.menu.link.cms.box.list</parent>
+ <permissions>admin.content.cms.canManageBox</permissions>
+ <icon>fa-plus</icon>
+ </acpmenuitem>
</import>
</data>
<usersonly>1</usersonly>
</option>
+ <option name="admin.content.cms.canManageBox">
+ <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.box.'|concat:$action}
+
+<header class="boxHeadline">
+ <h1>{if $action == 'add'}{if $isMultilingual}{lang}wcf.acp.box.addMultilingual{/lang}{else}{lang}wcf.acp.box.add{/lang}{/if}{else}{lang}wcf.acp.box.edit{/lang}{/if}</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='BoxList'}{/link}" class="button"><span class="icon icon16 fa-list"></span> <span>{lang}wcf.acp.menu.link.cms.box.list{/lang}</span></a></li>
+
+ {event name='contentNavigationButtons'}
+ </ul>
+ </nav>
+</div>
+
+<form method="post" action="{if $action == 'add'}{link controller='BoxAdd'}{/link}{else}{link controller='BoxEdit' id=$boxID}{/link}{/if}">
+ <section class="marginTop">
+ <h1>{lang}wcf.global.form.data{/lang}</h1>
+
+ <dl{if $errorField == 'name'} class="formError"{/if}>
+ <dt><label for="name">{lang}wcf.global.name{/lang}</label></dt>
+ <dd>
+ <input type="text" id="name" name="name" value="{$name}" required="required" autofocus="autofocus" class="long" />
+ {if $errorField == 'name'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.box.name.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'boxType'} class="formError"{/if}>
+ <dt><label for="boxType">{lang}wcf.acp.box.boxType{/lang}</label></dt>
+ <dd>
+ <select name="boxType" id="boxType">
+ {foreach from=$availableBoxTypes item=availableBoxType}
+ <option value="{@$availableBoxType}"{if $availableBoxType == $boxType} selected="selected"{/if}>{lang}wcf.acp.box.boxType.{@$availableBoxType}{/lang}</option>
+ {/foreach}
+ </select>
+
+ {if $errorField == 'boxType'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.box.boxType.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'position'} class="formError"{/if}>
+ <dt><label for="position">{lang}wcf.acp.box.position{/lang}</label></dt>
+ <dd>
+ <select name="position" id="position">
+ {foreach from=$availablePositions item=availablePosition}
+ <option value="{@$availablePosition}"{if $availablePosition == $position} selected="selected"{/if}>{lang}wcf.acp.box.position.{@$availablePosition}{/lang}</option>
+ {/foreach}
+ </select>
+
+ {if $errorField == 'position'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.box.position.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl>
+ <dt><label for="showOrder">{lang}wcf.acp.box.showOrder{/lang}</label></dt>
+ <dd>
+ <input type="number" id="showOrder" name="showOrder" value="{@$showOrder}" class="tiny" min="0" />
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'cssClassName'} class="formError"{/if}>
+ <dt><label for="cssClassName">{lang}wcf.acp.box.cssClassName{/lang}</label></dt>
+ <dd>
+ <input type="text" id="cssClassName" name="cssClassName" value="{$cssClassName}" class="long" />
+ {if $errorField == 'cssClassName'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.box.cssClassName.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'className'} class="formError"{/if}>
+ <dt><label for="className">{lang}wcf.acp.box.className{/lang}</label></dt>
+ <dd>
+ <input type="text" id="className" name="className" value="{$className}" class="long" />
+ {if $errorField == 'className'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.box.className.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl>
+ <dt></dt>
+ <dd>
+ <label><input type="checkbox" id="showHeader" name="showHeader" value="1" {if $showHeader}checked="checked" {/if}/> {lang}wcf.acp.box.showHeader{/lang}</label>
+ </dd>
+ </dl>
+
+ <dl>
+ <dt></dt>
+ <dd>
+ <label><input type="checkbox" id="visibleEverywhere" name="visibleEverywhere" value="1" {if $visibleEverywhere}checked="checked" {/if}/> {lang}wcf.acp.box.visibleEverywhere{/lang}</label>
+ </dd>
+ </dl>
+
+ {event name='dataFields'}
+ </section>
+
+ {if !$isMultilingual}
+ <fieldset>
+ <legend>content</legend>
+
+ <dl{if $errorField == 'title'} class="formError"{/if}>
+ <dt><label for="title">{lang}wcf.acp.box.title{/lang}</label></dt>
+ <dd>
+ <input type="text" id="title" name="title[0]" value="{if !$title[0]|empty}{$title[0]}{/if}" class="long" />
+ {if $errorField == 'title'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.box.title.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'content'} class="formError"{/if}>
+ <dt><label for="content0">{lang}wcf.acp.box.content{/lang}</label></dt>
+ <dd>
+ <textarea name="content[0]" id="content0">{if !$content[0]|empty}{$content[0]}{/if}</textarea>
+ {if $errorField == 'content'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.box.content.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+ </fieldset>
+ {else}
+ <div class="tabMenuContainer">
+ <nav class="tabMenu">
+ <ul>
+ {foreach from=$availableLanguages item=availableLanguage}
+ {assign var='containerID' value='language'|concat:$availableLanguage->languageID}
+ <li><a href="{@$__wcf->getAnchor($containerID)}">{$availableLanguage->languageName}</a></li>
+ {/foreach}
+ </ul>
+ </nav>
+
+ {foreach from=$availableLanguages item=availableLanguage}
+ <div id="language{@$availableLanguage->languageID}" class="tabMenuContent">
+ <div>
+ <dl{if $errorField == 'title'} class="formError"{/if}>
+ <dt><label for="title{@$availableLanguage->languageID}">{lang}wcf.acp.box.title{/lang}</label></dt>
+ <dd>
+ <input type="text" id="title{@$availableLanguage->languageID}" name="title[{@$availableLanguage->languageID}]" value="{if !$title[$availableLanguage->languageID]|empty}{$title[$availableLanguage->languageID]}{/if}" class="long" />
+ {if $errorField == 'title'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.box.title.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'content'} class="formError"{/if}>
+ <dt><label for="content{@$availableLanguage->languageID}">{lang}wcf.acp.box.content{/lang}</label></dt>
+ <dd>
+ <textarea name="content[{@$availableLanguage->languageID}]" id="content{@$availableLanguage->languageID}">{if !$content[$availableLanguage->languageID]|empty}{$content[$availableLanguage->languageID]}{/if}</textarea>
+ {if $errorField == 'content'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.box.content.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+ </div>
+ </div>
+ {/foreach}
+ </div>
+ {/if}
+
+ {event name='sections'}
+
+ <div class="formSubmit">
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
+ <input type="hidden" name="isMultilingual" value="{@$isMultilingual}" />
+ {@SECURITY_TOKEN_INPUT_TAG}
+ </div>
+</form>
+
+{include file='footer'}
--- /dev/null
+{include file='header' pageTitle='wcf.acp.box.list'}
+
+<script data-relocate="true">
+ //<![CDATA[
+ $(function() {
+ new WCF.Action.Delete('wcf\\data\\box\\BoxAction', '.jsBoxRow');
+ });
+ //]]>
+</script>
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.box.list{/lang}</h1>
+</header>
+
+<div class="contentNavigation">
+ {pages print=true assign=pagesLinks controller="BoxList" link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder"}
+
+ <nav>
+ <ul>
+ <li><a href="{link controller='BoxAdd'}{/link}" class="button"><span class="icon icon16 fa-plus"></span> <span>{lang}wcf.acp.box.add{/lang}</span></a></li>
+ <li><a href="{link controller='BoxAdd'}isMultilingual=1{/link}" class="button"><span class="icon icon16 fa-plus"></span> <span>{lang}wcf.acp.box.addMultilingual{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsTop'}
+ </ul>
+ </nav>
+</div>
+
+{if $objects|count}
+ <div class="tabularBox tabularBoxTitle marginTop">
+ <header>
+ <h2>{lang}wcf.acp.box.list{/lang} <span class="badge badgeInverse">{#$items}</span></h2>
+ </header>
+
+ <table class="table">
+ <thead>
+ <tr>
+ <th class="columnPageID{if $sortField == 'boxID'} active {@$sortOrder}{/if}" colspan="2"><a href="{link controller='BoxList'}pageNo={@$pageNo}&sortField=boxID&sortOrder={if $sortField == 'boxID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
+ <th class="columnTitle columnName{if $sortField == 'name'} active {@$sortOrder}{/if}"><a href="{link controller='BoxList'}pageNo={@$pageNo}&sortField=name&sortOrder={if $sortField == 'name' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.global.name{/lang}</a></th>
+ <th class="columnText columnBoxType{if $sortField == 'boxType'} active {@$sortOrder}{/if}"><a href="{link controller='BoxList'}pageNo={@$pageNo}&sortField=boxType&sortOrder={if $sortField == 'boxType' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.acp.box.boxType{/lang}</a></th>
+ <th class="columnText columnPosition{if $sortField == 'position'} active {@$sortOrder}{/if}"><a href="{link controller='BoxList'}pageNo={@$pageNo}&sortField=position&sortOrder={if $sortField == 'position' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.acp.box.position{/lang}</a></th>
+
+ {event name='columnHeads'}
+ </tr>
+ </thead>
+
+ <tbody>
+ {foreach from=$objects item=box}
+ <tr class="jsBoxRow">
+ <td class="columnIcon">
+ <a href="{link controller='BoxEdit' id=$box->boxID}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip"><span class="icon icon16 fa-pencil"></span></a>
+ {if $box->canDelete()}
+ <span class="icon icon16 fa-times jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{@$box->boxID}" data-confirm-message="{lang}wcf.acp.box.delete.confirmMessage{/lang}"></span>
+ {else}
+ <span class="icon icon16 fa-times disabled" title="{lang}wcf.global.button.delete{/lang}"></span>
+ {/if}
+
+ {event name='rowButtons'}
+ </td>
+ <td class="columnID columnBoxID">{@$box->boxID}</td>
+ <td class="columnTitle columnName"><a href="{link controller='BoxEdit' id=$box->boxID}{/link}">{$box->name}</a></td>
+ <td class="columnText columnBoxType">{$box->boxType}</td>
+ <td class="columnText columnPosition">{$box->position}</td>
+
+ {event name='columns'}
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+ </div>
+
+ <div class="contentNavigation">
+ {@$pagesLinks}
+
+
+ <nav>
+ <ul>
+ <li><a href="{link controller='BoxAdd'}{/link}" class="button"><span class="icon icon16 fa-plus"></span> <span>{lang}wcf.acp.box.add{/lang}</span></a></li>
+ <li><a href="{link controller='BoxAdd'}isMultilingual=1{/link}" class="button"><span class="icon icon16 fa-plus"></span> <span>{lang}wcf.acp.box.addMultilingual{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsBottom'}
+ </ul>
+ </nav>
+ </div>
+{/if}
+
+{include file='footer'}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\box\Box;
+use wcf\data\box\BoxAction;
+use wcf\data\box\BoxEditor;
+use wcf\form\AbstractForm;
+use wcf\system\exception\UserInputException;
+use wcf\system\language\LanguageFactory;
+use wcf\system\WCF;
+use wcf\util\ArrayUtil;
+use wcf\util\StringUtil;
+
+/**
+ * Shows the box 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 BoxAddForm extends AbstractForm {
+ /**
+ * @inheritDoc
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.cms.box.add';
+
+ /**
+ * @inheritDoc
+ */
+ public $neededPermissions = ['admin.content.cms.canManageBox'];
+
+ /**
+ * true if created box is multi-lingual
+ * @var boolean
+ */
+ public $isMultilingual = 0;
+
+ /**
+ * box type
+ * @var string
+ */
+ public $boxType = '';
+
+ /**
+ * box position
+ * @var string
+ */
+ public $position = '';
+
+ /**
+ * show order
+ * @var integer
+ */
+ public $showOrder = 0;
+
+ /**
+ * true if created box is visible everywhere
+ * @var boolean
+ */
+ public $visibleEverywhere = 1;
+
+ /**
+ * css class name of created box
+ * @var string
+ */
+ public $cssClassName = '';
+
+ /**
+ * true if box header is visible
+ * @var boolean
+ */
+ public $showHeader = 1;
+
+ /**
+ * php class name
+ * @var string
+ */
+ public $className = '';
+
+ /**
+ * box name
+ * @var string
+ */
+ public $name = '';
+
+ /**
+ * page titles
+ * @var array<string>
+ */
+ public $title = [];
+
+ /**
+ * page contents
+ * @var array<string>
+ */
+ public $content = [];
+
+ /**
+ * @inheritDoc
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ if (!empty($_REQUEST['isMultilingual'])) $this->isMultilingual = 1;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function readFormParameters() {
+ parent::readFormParameters();
+
+ $this->visibleEverywhere = $this->showOrder = 0;
+ if (isset($_POST['name'])) $this->name = StringUtil::trim($_POST['name']);
+ if (isset($_POST['boxType'])) $this->boxType = $_POST['boxType'];
+ if (isset($_POST['position'])) $this->position = $_POST['position'];
+ if (isset($_POST['showOrder'])) $this->showOrder = intval($_POST['showOrder']);
+ if (isset($_POST['visibleEverywhere'])) $this->visibleEverywhere = intval($_POST['visibleEverywhere']);
+ if (isset($_POST['cssClassName'])) $this->cssClassName = StringUtil::trim($_POST['cssClassName']);
+ if (isset($_POST['showHeader'])) $this->showHeader = intval($_POST['showHeader']);
+ if (isset($_POST['className'])) $this->className = StringUtil::trim($_POST['className']);
+
+ if (isset($_POST['title']) && is_array($_POST['title'])) $this->title = ArrayUtil::trim($_POST['title']);
+ if (isset($_POST['content']) && is_array($_POST['content'])) $this->content = ArrayUtil::trim($_POST['content']);
+
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validate() {
+ parent::validate();
+
+ // validate name
+ $this->validateName();
+
+ // validate box type
+ if (!in_array($this->boxType, Box::$availableBoxTypes)) {
+ throw new UserInputException('boxType');
+ }
+
+ // validate box position
+ if (!in_array($this->position, Box::$availablePositions)) {
+ throw new UserInputException('position');
+ }
+
+ // validate class name
+ if ($this->boxType == 'system') {
+ if (empty($this->className)) {
+ throw new UserInputException('className');
+ }
+
+ // @todo check class
+
+ }
+ }
+
+ /**
+ * Validates box name.
+ */
+ protected function validateName() {
+ if (empty($this->name)) {
+ throw new UserInputException('name');
+ }
+ if (Box::getBoxByName($this->name)) {
+ throw new UserInputException('name', 'notUnique');
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function save() {
+ parent::save();
+
+ $content = [];
+ if ($this->isMultilingual) {
+ foreach (LanguageFactory::getInstance()->getLanguages() as $language) {
+ $content[$language->languageID] = [
+ 'title' => (!empty($_POST['title'][$language->languageID]) ? $_POST['title'][$language->languageID] : ''),
+ 'content' => (!empty($_POST['content'][$language->languageID]) ? $_POST['content'][$language->languageID] : '')
+ ];
+ }
+ }
+ else {
+ $content[0] = [
+ 'title' => (!empty($_POST['title'][0]) ? $_POST['title'][0] : ''),
+ 'content' => (!empty($_POST['content'][0]) ? $_POST['content'][0] : '')
+ ];
+ }
+
+ $this->objectAction = new BoxAction([], 'create', ['data' => array_merge($this->additionalFields, [
+ 'name' => $this->name,
+ 'packageID' => 1,
+ 'isMultilingual' => $this->isMultilingual,
+ 'boxType' => $this->boxType,
+ 'position' => $this->position,
+ 'showOrder' => $this->showOrder,
+ 'visibleEverywhere' => $this->visibleEverywhere,
+ 'cssClassName' => $this->cssClassName,
+ 'showHeader' => $this->showHeader,
+ 'className' => $this->className,
+ 'identifier' => ''
+ ]), 'content' => $content]);
+ $returnValues = $this->objectAction->executeAction();
+ // set generic box identifier
+ $boxEditor = new BoxEditor($returnValues['returnValues']);
+ $boxEditor->update([
+ 'identifier' => 'com.woltlab.wcf.generic'.$boxEditor->boxID
+ ]);
+
+ // call saved event
+ $this->saved();
+
+ // show success
+ WCF::getTPL()->assign('success', true);
+
+ // reset variables
+ $this->boxType = $this->position = $this->cssClassName = $this->className = $this->name = '';
+ $this->showOrder = 0;
+ $this->visibleEverywhere = $this->showHeader = 1;
+ $this->title = $this->content = [];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign([
+ 'action' => 'add',
+ 'isMultilingual' => $this->isMultilingual,
+ 'name' => $this->name,
+ 'boxType' => $this->boxType,
+ 'position' => $this->position,
+ 'cssClassName' => $this->cssClassName,
+ 'className' => $this->className,
+ 'showOrder' => $this->showOrder,
+ 'visibleEverywhere' => $this->visibleEverywhere,
+ 'showHeader' => $this->showHeader,
+ 'title' => $this->title,
+ 'content' => $this->content,
+ 'availableLanguages' => LanguageFactory::getInstance()->getLanguages(),
+ 'availableBoxTypes' => Box::$availableBoxTypes,
+ 'availablePositions' => Box::$availablePositions
+ ]);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\box\Box;
+use wcf\data\box\BoxAction;
+use wcf\form\AbstractForm;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\language\LanguageFactory;
+use wcf\system\WCF;
+
+/**
+ * Shows the box 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 BoxEditForm extends BoxAddForm {
+ /**
+ * @inheritDoc
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.cms.box.list';
+
+ /**
+ * box id
+ * @var integer
+ */
+ public $boxID = 0;
+
+ /**
+ * box object
+ * @var Box
+ */
+ public $box = null;
+
+ /**
+ * @inheritDoc
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ if (isset($_REQUEST['id'])) $this->boxID = intval($_REQUEST['id']);
+ $this->box = new Box($this->boxID);
+ if (!$this->box->boxID) {
+ throw new IllegalLinkException();
+ }
+ if ($this->box->isMultilingual) $this->isMultilingual = 1;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function validateName() {
+ if (mb_strtolower($this->name) != mb_strtolower($this->box->name)) {
+ parent::validateName();
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function save() {
+ AbstractForm::save();
+
+ $content = array();
+ if ($this->isMultilingual) {
+ foreach (LanguageFactory::getInstance()->getLanguages() as $language) {
+ $content[$language->languageID] = [
+ 'title' => (!empty($_POST['title'][$language->languageID]) ? $_POST['title'][$language->languageID] : ''),
+ 'content' => (!empty($_POST['content'][$language->languageID]) ? $_POST['content'][$language->languageID] : ''),
+ ];
+ }
+ }
+ else {
+ $content[0] = [
+ 'title' => (!empty($_POST['title'][0]) ? $_POST['title'][0] : ''),
+ 'content' => (!empty($_POST['content'][0]) ? $_POST['content'][0] : ''),
+ ];
+ }
+
+ $this->objectAction = new BoxAction([$this->box], 'update', ['data' => array_merge($this->additionalFields, [
+ 'name' => $this->name,
+ 'isMultilingual' => $this->isMultilingual,
+ 'boxType' => $this->boxType,
+ 'position' => $this->position,
+ 'showOrder' => $this->showOrder,
+ 'visibleEverywhere' => $this->visibleEverywhere,
+ 'cssClassName' => $this->cssClassName,
+ 'showHeader' => $this->showHeader,
+ 'className' => $this->className
+ ]), 'content' => $content]);
+ $this->objectAction->executeAction();
+
+ // call saved event
+ $this->saved();
+
+ // show success
+ WCF::getTPL()->assign('success', true);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function readData() {
+ parent::readData();
+
+ if (empty($_POST)) {
+ $this->name = $this->box->name;
+ $this->boxType = $this->box->boxType;
+ $this->position = $this->box->position;
+ $this->showOrder = $this->box->showOrder;
+ $this->cssClassName = $this->box->cssClassName;
+ $this->className = $this->box->className;
+ if ($this->box->showHeader) $this->showHeader = 1;
+ if ($this->box->visibleEverywhere) $this->visibleEverywhere = 1;
+
+ foreach ($this->box->getBoxContent() as $languageID => $content) {
+ $this->title[$languageID] = $content['title'];
+ $this->content[$languageID] = $content['content'];
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'action' => 'edit',
+ 'boxID' => $this->boxID,
+ 'box' => $this->box
+ ));
+ }
+}
$this->validateCustomUrl();
}
+ /**
+ * Validates page name.
+ */
protected function validateName() {
if (empty($this->name)) {
throw new UserInputException('name');
}
}
+ /**
+ * Validates parent page id.
+ */
protected function validateParentPageID() {
if ($this->parentPageID) {
$page = new Page($this->parentPageID);
}
}
+ /**
+ * Validates package id.
+ */
protected function validatePackageID() {
if (!isset($this->availableApplications[$this->packageID])) {
throw new UserInputException('packageID', 'invalid');
}
}
+ /**
+ * Validates custom urls.
+ */
protected function validateCustomUrl() {
foreach ($this->customURL as $type => $customURL) {
if (!empty($customURL) && !RouteHandler::isValidCustomUrl($customURL)) {
'identifier' => ''
]), 'content' => $content]);
$returnValues = $this->objectAction->executeAction();
- // set generic page name
+ // set generic page identifier
$pageEditor = new PageEditor($returnValues['returnValues']);
$pageEditor->update([
'identifier' => 'com.woltlab.wcf.generic'.$pageEditor->pageID
--- /dev/null
+<?php
+namespace wcf\acp\page;
+use wcf\page\SortablePage;
+
+/**
+ * Shows a list of boxes.
+ *
+ * @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 BoxListPage extends SortablePage {
+ /**
+ * @see \wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.cms.box.list';
+
+ /**
+ * @see \wcf\page\MultipleLinkPage::$objectListClassName
+ */
+ public $objectListClassName = 'wcf\data\box\BoxList';
+
+ /**
+ * @see \wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.content.cms.canManageBox');
+
+ /**
+ * @see \wcf\page\SortablePage::$defaultSortField
+ */
+ public $defaultSortField = 'name';
+
+ /**
+ * @see \wcf\page\SortablePage::$validSortFields
+ */
+ public $validSortFields = array('boxID', 'name', 'boxType', 'position', 'showOrder');
+}
--- /dev/null
+<?php
+namespace wcf\data\box;
+use wcf\data\DatabaseObject;
+use wcf\system\WCF;
+
+/**
+ * Represents a box.
+ *
+ * @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.box
+ * @category Community Framework
+ */
+class Box extends DatabaseObject {
+ /**
+ * @inheritDoc
+ */
+ protected static $databaseTableName = 'box';
+
+ /**
+ * @inheritDoc
+ */
+ protected static $databaseTableIndexName = 'boxID';
+
+ /**
+ * available box types
+ * @var string[]
+ */
+ public static $availableBoxTypes = ['text', 'html', 'system', 'menu'];
+
+ /**
+ * available box positions
+ * @var string[]
+ */
+ public static $availablePositions = ['header', 'headerBoxes', 'top', 'sidebarLeft', 'contentTop', 'sidebarRight', 'contentBottom', 'bottom', 'footerBoxes', 'footer'];
+
+ /**
+ * Returns true if the active user can delete this box.
+ *
+ * @return boolean
+ */
+ public function canDelete() {
+ if (WCF::getSession()->getPermission('admin.content.cms.canManageBox') && !$this->originIsSystem) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the box content.
+ *
+ * @return array
+ */
+ public function getBoxContent() {
+ $content = array();
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_box_content
+ WHERE boxID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($this->boxID));
+ while ($row = $statement->fetchArray()) {
+ $content[($row['languageID'] ?: 0)] = [
+ 'title' => $row['title'],
+ 'content' => $row['content']
+ ];
+ }
+
+ return $content;
+ }
+
+ /**
+ * Returns the box with the given name.
+ *
+ * @param string $name
+ * @return Box
+ */
+ public static function getBoxByName($name) {
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_box
+ WHERE name = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($name));
+ $row = $statement->fetchArray();
+ if ($row !== false) return new Box(null, $row);
+
+ return null;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\box;
+use wcf\data\AbstractDatabaseObjectAction;
+use wcf\system\exception\PermissionDeniedException;
+use wcf\system\WCF;
+
+/**
+ * Executes box 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.box
+ * @category Community Framework
+ */
+class BoxAction extends AbstractDatabaseObjectAction {
+ /**
+ * @inheritDoc
+ */
+ protected $className = BoxEditor::class;
+
+ /**
+ * @inheritDoc
+ */
+ protected $permissionsCreate = ['admin.content.cms.canManageBox'];
+
+ /**
+ * @inheritDoc
+ */
+ protected $permissionsDelete = ['admin.content.cms.canManageBox'];
+
+ /**
+ * @inheritDoc
+ */
+ protected $permissionsUpdate = ['admin.content.cms.canManageBox'];
+
+ /**
+ * @inheritDoc
+ */
+ protected $requireACP = ['create', 'delete', 'update'];
+
+ /**
+ * @inheritDoc
+ */
+ public function create() {
+ $box = parent::create();
+
+ // save box content
+ if (!empty($this->parameters['content'])) {
+ $sql = "INSERT INTO wcf".WCF_N."_box_content
+ (boxID, languageID, title, content)
+ VALUES (?, ?, ?, ?)";
+ $statement = WCF::getDB()->prepareStatement($sql);
+
+ foreach ($this->parameters['content'] as $languageID => $content) {
+ $statement->execute([
+ $box->boxID,
+ ($languageID ?: null),
+ $content['title'],
+ $content['content']
+ ]);
+ }
+ }
+
+ return $box;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function update() {
+ parent::update();
+
+ // update box content
+ if (!empty($this->parameters['content'])) {
+ $sql = "DELETE FROM wcf".WCF_N."_box_content
+ WHERE boxID = ?";
+ $deleteStatement = WCF::getDB()->prepareStatement($sql);
+
+ $sql = "INSERT INTO wcf".WCF_N."_box_content
+ (boxID, languageID, title, content)
+ VALUES (?, ?, ?, ?)";
+ $insertStatement = WCF::getDB()->prepareStatement($sql);
+
+ foreach ($this->objects as $box) {
+ $deleteStatement->execute(array($box->boxID));
+
+ foreach ($this->parameters['content'] as $languageID => $content) {
+ $insertStatement->execute([
+ $box->boxID,
+ ($languageID ?: null),
+ $content['title'],
+ $content['content']
+ ]);
+ }
+ }
+ }
+
+ return $box;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function validateDelete() {
+ parent::validateDelete();
+
+ foreach ($this->objects as $object) {
+ if (!$object->canDelete()) {
+ throw new PermissionDeniedException();
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\box;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Provides functions to edit boxes.
+ *
+ * @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.box
+ * @category Community Framework
+ */
+class BoxEditor extends DatabaseObjectEditor {
+ /**
+ * @inheritDoc
+ */
+ protected static $baseClass = Box::class;
+}
--- /dev/null
+<?php
+namespace wcf\data\box;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of boxes.
+ *
+ * @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.box
+ * @category Community Framework
+ */
+class BoxList extends DatabaseObjectList {
+ /**
+ * @inheritDoc
+ */
+ public $className = Box::class;
+}
html TEXT NOT NULL
);
+DROP TABLE IF EXISTS wcf1_box;
+CREATE TABLE wcf1_box (
+ boxID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ identifier VARCHAR(255) NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ boxType VARCHAR(255) NOT NULL,
+ position VARCHAR(255) NOT NULL,
+ showOrder INT(10) NOT NULL DEFAULT 0,
+ visibleEverywhere TINYINT(1) NOT NULL DEFAULT 1,
+ isMultilingual TINYINT(1) NOT NULL DEFAULT 0,
+ cssClassName VARCHAR(255) NOT NULL DEFAULT '',
+ showHeader TINYINT(1) NOT NULL DEFAULT 1,
+ originIsSystem TINYINT(1) NOT NULL DEFAULT 0,
+ packageID INT(10) NOT NULL,
+ className VARCHAR(255) NOT NULL DEFAULT ''
+);
+
+DROP TABLE IF EXISTS wcf1_box_content;
+CREATE TABLE wcf1_box_content (
+ boxID INT(10) NOT NULL,
+ languageID INT(10),
+ title VARCHAR(255) NOT NULL,
+ content MEDIUMTEXT,
+
+ KEY (boxID, languageID)
+);
+
DROP TABLE IF EXISTS wcf1_captcha_question;
CREATE TABLE wcf1_captcha_question (
questionID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
ALTER TABLE wcf1_bbcode_attribute ADD FOREIGN KEY (bbcodeID) REFERENCES wcf1_bbcode (bbcodeID) ON DELETE CASCADE;
+ALTER TABLE wcf1_box ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
+
+ALTER TABLE wcf1_box_content ADD FOREIGN KEY (boxID) REFERENCES wcf1_box (boxID) ON DELETE CASCADE;
+ALTER TABLE wcf1_box_content ADD FOREIGN KEY (languageID) REFERENCES wcf1_language (languageID) ON DELETE CASCADE;
+
ALTER TABLE wcf1_category ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
ALTER TABLE wcf1_cli_history ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;