}
});
});
+
+ require(['Dictionary', 'Language', 'WoltLab/WCF/Acp/Ui/Box/Handler'], function(Dictionary, Language, AcpUiBoxHandler) {
+ Language.addObject({
+ 'wcf.page.pageObjectID.search.noResults': '{lang}wcf.page.pageObjectID.search.noResults{/lang}',
+ 'wcf.page.pageObjectID.search.results': '{lang}wcf.page.pageObjectID.search.results{/lang}',
+ 'wcf.page.pageObjectID.search.results.description': '{lang}wcf.page.pageObjectID.search.results.description{/lang}',
+ 'wcf.page.pageObjectID.search.terms': '{lang}wcf.page.pageObjectID.search.terms{/lang}',
+ 'wcf.page.pageObjectID.search.terms.description': '{lang}wcf.page.pageObjectID.search.terms.description{/lang}'
+ });
+
+ var handlers = new Dictionary();
+ {foreach from=$pageHandlers key=handlerPageID item=requireObjectID}
+ handlers.set({@$handlerPageID}, {if $requireObjectID}true{else}false{/if});
+ {/foreach}
+
+ AcpUiBoxHandler.init(handlers);
+ });
</script>
{/if}
{event name='dataFields'}
</section>
+ <section class="section">
+ <h2 class="sectionTitle">{lang}wcf.acp.box.link{/lang}</h2>
+
+ <dl>
+ <dt></dt>
+ <dd class="floated">
+ <label><input type="radio" name="linkType" value="none"{if $linkType == 'none'} checked="checked"{/if} /> {lang}wcf.acp.box.linkType.none{/lang}</label>
+ <label><input type="radio" name="linkType" value="internal"{if $linkType == 'internal'} checked="checked"{/if} /> {lang}wcf.acp.box.linkType.internal{/lang}</label>
+ <label><input type="radio" name="linkType" value="external"{if $linkType == 'external'} checked="checked"{/if} /> {lang}wcf.acp.box.linkType.external{/lang}</label>
+ </dd>
+ </dl>
+
+ <dl id="linkPageIDContainer"{if $errorField == 'linkPageID'} class="formError"{/if}{if $linkType != 'internal'} style="display: none;"{/if}>
+ <dt><label for="linkPageID">{lang}wcf.acp.box.linkPageID{/lang}</label></dt>
+ <dd>
+ <select name="linkPageID" id="linkPageID">
+ <option value="0">{lang}wcf.global.noSelection{/lang}</option>
+
+ {foreach from=$pageNodeList item=pageNode}
+ <option value="{@$pageNode->getPage()->pageID}"{if $pageNode->getPage()->pageID == $linkPageID} selected="selected"{/if}>{if $pageNode->getDepth() > 1}{@" "|str_repeat:($pageNode->getDepth() - 1)}{/if}{$pageNode->getPage()->name}</option>
+ {/foreach}
+ </select>
+ {if $errorField == 'linkPageID'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.box.linkPageID.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl id="linkPageObjectIDContainer"{if $errorField == 'linkPageObjectID'} class="formError"{/if}{if !$linkPageID || !$pageHandler[$linkPageID]|isset} style="display: none;"{/if}>
+ <dt><label for="linkPageObjectID">{lang}wcf.acp.box.linkPageObjectID{/lang}</label></dt>
+ <dd>
+ <div class="inputAddon">
+ <input type="text" id="linkPageObjectID" name="linkPageObjectID" value="{$linkPageObjectID}" class="short">
+ <a href="#" id="searchLinkPageObjectID" class="inputSuffix button jsTooltip" title="{lang}wcf.acp.page.objectID.search{/lang}"><span class="icon icon16 fa-search"></span></a>
+ </div>
+ {if $errorField == 'linkPageObjectID'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.box.linkPageObjectID.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl id="externalURLContainer"{if $errorField == 'externalURL'} class="formError"{/if}{if $linkType != 'external'} style="display: none;"{/if}>
+ <dt><label for="externalURL">{lang}wcf.acp.box.link.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.box.link.externalURL.error.{$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ {event name='linkFields'}
+ </section>
+
{if !$isMultilingual}
<section class="section">
<h2 class="sectionTitle">content</h2>
--- /dev/null
+/**
+ * Provides the interface logic to add and edit boxes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2016 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLab/WCF/Acp/Ui/Box/Handler
+ */
+define(['Dictionary', 'WoltLab/Wcf/Ui/Page/Search/Handler'], function(Dictionary, UiPageSearchHandler) {
+ "use strict";
+
+ var _activePageId = 0;
+ var _cache;
+ var _containerExternalLink;
+ var _containerPageID;
+ var _containerPageObjectId = null;
+ var _handlers;
+ var _pageId;
+ var _pageObjectId;
+
+ /**
+ * @exports WoltLab/WCF/Acp/Ui/Box/Handler
+ */
+ return {
+ /**
+ * Initializes the interface logic.
+ *
+ * @param {Dictionary} handlers list of handlers by page id supporting page object ids
+ */
+ init: function(handlers) {
+ _handlers = handlers;
+
+ _containerPageID = elById('linkPageIDContainer');
+ _containerExternalLink = elById('externalURLContainer');
+ _containerPageObjectId = elById('linkPageObjectIDContainer');
+
+ if (_handlers.size) {
+ _pageId = elById('linkPageID');
+ _pageId.addEventListener('change', this._togglePageId.bind(this));
+
+ _pageObjectId = elById('linkPageObjectID');
+
+ _cache = new Dictionary();
+ _activePageId = ~~_pageId.value;
+ if (_activePageId && _handlers.has(_activePageId)) {
+ _cache.set(_activePageId, ~~_pageObjectId.value);
+ }
+
+ elById('searchLinkPageObjectID').addEventListener(WCF_CLICK_EVENT, this._openSearch.bind(this));
+
+ // toggle page object id container on init
+ if (_handlers.has(~~_pageId.value)) {
+ elShow(_containerPageObjectId);
+ }
+ }
+
+ elBySelAll('input[name="linkType"]', null, (function(input) {
+ input.addEventListener('change', this._toggleLinkType.bind(this, input.value));
+
+ if (input.checked) {
+ this._toggleLinkType(input.value);
+ }
+ }).bind(this));
+ },
+
+ /**
+ * Toggles between the interface for internal and external links.
+ *
+ * @param {string} value selected option value
+ * @protected
+ */
+ _toggleLinkType: function(value) {
+ if (value == 'none') {
+ elHide(_containerPageID);
+ elHide(_containerPageObjectId);
+ elHide(_containerExternalLink);
+ }
+ if (value == 'internal') {
+ elShow(_containerPageID);
+ elHide(_containerExternalLink);
+ this._togglePageId();
+ }
+ if (value == 'external') {
+ elHide(_containerPageID);
+ elHide(_containerPageObjectId);
+ elShow(_containerExternalLink);
+ }
+ },
+
+ /**
+ * Handles the changed page selection.
+ *
+ * @protected
+ */
+ _togglePageId: function() {
+ if (_handlers.has(_activePageId)) {
+ _cache.set(_activePageId, ~~_pageObjectId.value);
+ }
+
+ _activePageId = ~~_pageId.value;
+
+ // page w/o pageObjectID support, discard value
+ if (!_handlers.has(_activePageId)) {
+ _pageObjectId.value = '';
+
+ elHide(_containerPageObjectId);
+
+ return;
+ }
+
+ var newValue = ~~_cache.get(_activePageId);
+ _pageObjectId.value = (newValue) ? newValue : '';
+
+ elShow(_containerPageObjectId);
+ },
+
+ /**
+ * Opens the handler lookup dialog.
+ *
+ * @param {Event} event event object
+ * @protected
+ */
+ _openSearch: function(event) {
+ event.preventDefault();
+
+ UiPageSearchHandler.open(_activePageId, _pageId.options[_pageId.selectedIndex].textContent, function(objectId) {
+ _pageObjectId.value = objectId;
+ _cache.set(_activePageId, objectId);
+ });
+ }
+ };
+});
use wcf\data\box\BoxEditor;
use wcf\data\media\Media;
use wcf\data\media\ViewableMediaList;
+use wcf\data\page\Page;
use wcf\data\page\PageNodeTree;
use wcf\form\AbstractForm;
use wcf\system\database\util\PreparedStatementConditionBuilder;
use wcf\system\exception\UserInputException;
use wcf\system\language\LanguageFactory;
+use wcf\system\page\handler\ILookupPageHandler;
use wcf\system\WCF;
use wcf\util\ArrayUtil;
use wcf\util\StringUtil;
*/
public $pageIDs = [];
+ /**
+ * link type
+ * @var string
+ */
+ public $linkType = 'none';
+
+ /**
+ * link page id
+ * @var int
+ */
+ public $linkPageID = 0;
+
+ /**
+ * link page object id
+ * @var int
+ */
+ public $linkPageObjectID = 0;
+
+ /**
+ * link external URL
+ * @var string
+ */
+ public $externalURL = '';
+
+ /**
+ * list of page handlers by page id
+ * @var \wcf\system\page\handler\IMenuPageHandler[]
+ */
+ public $pageHandlers = [];
+
+ /**
+ * nested list of page nodes
+ * @var \RecursiveIteratorIterator
+ */
+ public $pageNodeList;
+
/**
* @inheritDoc
*/
parent::readParameters();
if (!empty($_REQUEST['isMultilingual'])) $this->isMultilingual = 1;
+
+ $this->pageNodeList = (new PageNodeTree())->getNodeList();
+
+ // fetch page handlers
+ foreach ($this->pageNodeList as $pageNode) {
+ $handler = $pageNode->getPage()->getHandler();
+ if ($handler !== null) {
+ if ($handler instanceof ILookupPageHandler) {
+ $this->pageHandlers[$pageNode->getPage()->pageID] = $pageNode->getPage()->requireObjectID;
+ }
+ }
+ }
}
/**
if (isset($_POST['controller'])) $this->controller = StringUtil::trim($_POST['controller']);
if (isset($_POST['pageIDs']) && is_array($_POST['pageIDs'])) $this->pageIDs = ArrayUtil::toIntegerArray($_POST['pageIDs']);
+ if (isset($_POST['linkType'])) $this->linkType = $_POST['linkType'];
+ if (!empty($_POST['linkPageID'])) $this->linkPageID = intval($_POST['linkPageID']);
+ if (!empty($_POST['linkPageObjectID'])) $this->linkPageObjectID = intval($_POST['linkPageObjectID']);
+ if (isset($_POST['externalURL'])) $this->externalURL = StringUtil::trim($_POST['externalURL']);
+
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']);
// @todo check controller
}
+ // validate link
+ if ($this->linkType == 'internal') {
+ $this->externalURL = '';
+
+ if (!$this->linkPageID) {
+ throw new UserInputException('linkPageID');
+ }
+ $page = new Page($this->linkPageID);
+ if (!$page->pageID) {
+ throw new UserInputException('linkPageID', 'invalid');
+ }
+
+ // validate page object id
+ if (isset($this->pageHandlers[$page->pageID])) {
+ if ($this->pageHandlers[$page->pageID] && !$this->linkPageObjectID) {
+ throw new UserInputException('linkPageObjectID');
+ }
+
+ /** @var ILookupPageHandler $handler */
+ $handler = $page->getHandler();
+ if ($this->linkPageObjectID && !$handler->isValid($this->linkPageObjectID)) {
+ throw new UserInputException('linkPageObjectID', 'invalid');
+ }
+ }
+ }
+ else if ($this->linkType == 'external') {
+ $this->linkPageID = $this->linkPageObjectID = null;
+
+ if (empty($this->externalURL)) {
+ throw new UserInputException('externalURL');
+ }
+ }
+ else {
+ $this->linkPageID = $this->linkPageObjectID = null;
+ $this->externalURL = '';
+ }
+
// validate page ids
if (!empty($this->pageIDs)) {
$conditionBuilder = new PreparedStatementConditionBuilder();
'cssClassName' => $this->cssClassName,
'showHeader' => $this->showHeader,
'controller' => $this->controller,
+ 'linkPageID' => $this->linkPageID,
+ 'linkPageObjectID' => ($this->linkPageObjectID ?: 0),
+ 'externalURL' => $this->externalURL,
'identifier' => ''
]), 'content' => $content, 'pageIDs' => $this->pageIDs ]);
$returnValues = $this->objectAction->executeAction();
'imageID' => $this->imageID,
'images' => $this->images,
'pageIDs' => $this->pageIDs,
+ 'linkType' => $this->linkType,
+ 'linkPageID' => $this->linkPageID,
+ 'linkPageObjectID' => $this->linkPageObjectID,
+ 'externalURL' => $this->externalURL,
'availableLanguages' => LanguageFactory::getInstance()->getLanguages(),
'availableBoxTypes' => Box::$availableBoxTypes,
'availablePositions' => Box::$availablePositions,
- 'pageNodeList' => (new PageNodeTree())->getNodeList()
+ 'pageNodeList' => $this->pageNodeList,
+ 'pageHandlers' => $this->pageHandlers
]);
}
}
if ($this->box->visibleEverywhere) $this->visibleEverywhere = 1;
else $this->visibleEverywhere = 0;
$this->pageIDs = $this->box->getPageIDs();
+ $this->linkPageID = $this->box->linkPageID;
+ $this->linkPageObjectID = $this->box->linkPageObjectID;
+ $this->externalURL = $this->box->externalURL;
+ if ($this->linkPageID) $this->linkType = 'internal';
+ if ($this->externalURL) $this->linkType = 'external';
foreach ($this->box->getBoxContent() as $languageID => $content) {
$this->title[$languageID] = $content['title'];
use wcf\data\menu\Menu;
use wcf\data\menu\MenuCache;
use wcf\data\DatabaseObject;
+use wcf\data\page\Page;
+use wcf\data\page\PageCache;
+use wcf\system\exception\SystemException;
+use wcf\system\page\handler\ILookupPageHandler;
+use wcf\system\page\handler\IMenuPageHandler;
use wcf\system\WCF;
use wcf\util\StringUtil;
* @property-read integer $packageID
* @property-read string $controller
* @property-read integer|null $menuID
+ * @property-read integer $linkPageID
+ * @property-read integer $linkPageObjectID
+ * @property-read string $externalURL
*/
class Box extends DatabaseObject {
/**
*/
protected $__controller;
+ /**
+ * @var IMenuPageHandler
+ */
+ protected $linkPageHandler;
+
+ /**
+ * page object
+ * @var Page
+ */
+ protected $linkPage;
+
/**
* Returns true if the active user can delete this box.
*
return (isset($boxContent[0]) && $boxContent[0]['imageID']);
}
+ /**
+ * Returns the URL of this box.
+ *
+ * @return string
+ */
public function getLink() {
- // @todo
- return '';
+ if ($this->linkPageObjectID) {
+ $handler = $this->getLinkPageHandler();
+ if ($handler && $handler instanceof ILookupPageHandler) {
+ return $handler->getLink($this->linkPageObjectID);
+ }
+ }
+
+ if ($this->linkPageID) {
+ return $this->getLinkPage()->getLink();
+ }
+ else {
+ return $this->externalURL;
+ }
}
+ /**
+ * Returns true if this box has a link.
+ *
+ * @return boolean
+ */
public function hasLink() {
- // @todo
- return false;
+ return ($this->linkPageID || !empty($this->externalURL));
+ }
+
+ /**
+ * Returns the IMenuPageHandler of the linked page.
+ *
+ * @return IMenuPageHandler|null
+ * @throws SystemException
+ */
+ protected function getLinkPageHandler() {
+ $page = $this->getLinkPage();
+ if ($page !== null && $page->handler) {
+ if ($this->linkPageHandler === null) {
+ $className = $page->handler;
+ $this->linkPageHandler = new $className;
+ if (!($this->linkPageHandler instanceof IMenuPageHandler)) {
+ throw new SystemException("Expected a valid handler implementing '" . IMenuPageHandler::class . "'.");
+ }
+ }
+ }
+
+ return $this->linkPageHandler;
+ }
+
+ /**
+ * Returns the page that is linked by this box.
+ *
+ * @return Page|null
+ */
+ public function getLinkPage() {
+ if ($this->linkPage === null && $this->linkPageID) {
+ $this->linkPage = PageCache::getInstance()->getPage($this->linkPageID);
+ }
+
+ return $this->linkPage;
}
/**
originIsSystem TINYINT(1) NOT NULL DEFAULT 0,
packageID INT(10) NOT NULL,
controller VARCHAR(255) NOT NULL DEFAULT '',
- menuID INT(10) NULL
+ menuID INT(10),
+ linkPageID INT(10),
+ linkPageObjectID INT(10) NOT NULL DEFAULT 0,
+ externalURL VARCHAR(255) NOT NULL DEFAULT ''
);
DROP TABLE IF EXISTS wcf1_box_content;
ALTER TABLE wcf1_box ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
ALTER TABLE wcf1_box ADD FOREIGN KEY (menuID) REFERENCES wcf1_menu (menuID) ON DELETE CASCADE;
+ALTER TABLE wcf1_box ADD FOREIGN KEY (linkPageID) REFERENCES wcf1_page (pageID) ON DELETE SET NULL;
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;