<attribute name="0">
<validationpattern>^\d+$</validationpattern>
<required>1</required>
- <usetext>1</usetext>
</attribute>
<attribute name="1">
<validationpattern>^(small|medium|large|original)$</validationpattern>
<attribute name="0">
<validationpattern>^\d+(,\d+)*$</validationpattern>
<required>1</required>
- <usetext>1</usetext>
+ </attribute>
+ </attributes>
+ </bbcode>
+
+ <bbcode name="wsp">
+ <classname><![CDATA[wcf\system\bbcode\WoltLabSuitePageBBCode]]></classname>
+ <attributes>
+ <attribute name="0">
+ <validationpattern>^\d+$</validationpattern>
+ <required>1</required>
</attribute>
</attributes>
</bbcode>
config.plugins.push('WoltLabMedia');
{/if}
- // load the button plugin last to ensure all buttons have been initialized
- // already and we can safely add all icons
- config.plugins.push('WoltLabButton');
-
{if $__redactorConfig|isset}{@$__redactorConfig}{/if}
{assign var=$__redactorConfig value=''}
{event name='redactorConfig'}
+ // load the button plugin last to ensure all buttons have been initialized
+ // already and we can safely add all icons
+ config.plugins.push('WoltLabButton');
+
$(element).redactor(config);
});
});
<textarea name="content[{@$languageID}]" id="content{@$languageID}">{if !$content[$languageID]|empty}{$content[$languageID]}{/if}</textarea>
{if $pageType == 'text'}
+ {capture append='__redactorJavaScript'}, '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabPage.js?v={@LAST_UPDATE_TIME}'{/capture}
+ {capture append='__redactorConfig'}
+ Language.addObject({
+ 'wcf.page.search': '{lang}wcf.page.search{/lang}',
+ 'wcf.page.search.error.tooShort': '{lang}wcf.page.search.error.tooShort{/lang}',
+ 'wcf.page.search.error.noResults': '{lang}wcf.page.search.error.noResults{/lang}',
+ 'wcf.page.search.name': '{lang}wcf.page.search.name{/lang}',
+ 'wcf.page.search.results': '{lang}wcf.page.search.results{/lang}',
+ 'wcf.page.search.results.description': '{lang}wcf.page.search.results.description{/lang}'
+ });
+
+ buttonOptions.woltlabPage = { icon: 'fa-file-text-o', title: '{lang}wcf.editor.button.page{/lang}' };
+
+ buttons.push('woltlabPage');
+
+ config.plugins.push('WoltLabPage');
+ {/capture}
+
{include file='wysiwyg' wysiwygSelector='content'|concat:$languageID}
{elseif $pageType == 'html'}
{include file='codemirror' codemirrorMode='htmlmixed' codemirrorSelector='#content'|concat:$languageID}
config.plugins.push('WoltLabMedia');
{/if}
- // load the button plugin last to ensure all buttons have been initialized
- // already and we can safely add all icons
- config.plugins.push('WoltLabButton');
-
{if $__redactorConfig|isset}{@$__redactorConfig}{/if}
{assign var=$__redactorConfig value=''}
{event name='redactorConfig'}
+ // load the button plugin last to ensure all buttons have been initialized
+ // already and we can safely add all icons
+ config.plugins.push('WoltLabButton');
+
$(element).redactor(config);
});
});
--- /dev/null
+$.Redactor.prototype.WoltLabPage = function() {
+ "use strict";
+
+ return {
+ init: function() {
+ var button = this.button.add('woltlabPage', '');
+
+ require(['WoltLab/WCF/Ui/Redactor/Page'], (function (UiRedactorPage) {
+ new UiRedactorPage(this, button[0]);
+ }).bind(this));
+ }
+ };
+};
--- /dev/null
+define(['Ajax', 'EventKey', 'Language', 'StringUtil', 'Dom/Util', 'Ui/Dialog'], function(Ajax, EventKey, Language, StringUtil, DomUtil, UiDialog) {
+ "use strict";
+
+ var _callbackSelect, _resultContainer, _resultList, _searchInput = null;
+
+ return {
+ open: function(callbackSelect) {
+ _callbackSelect = callbackSelect;
+
+ UiDialog.open(this);
+ },
+
+ _search: function (event) {
+ event.preventDefault();
+
+ var inputContainer = _searchInput.parentNode;
+ var innerError = inputContainer.nextSibling;
+ if (innerError && innerError.nodeName === 'SMALL') elRemove(innerError);
+
+ var value = _searchInput.value.trim();
+ if (value.length < 3) {
+ innerError = elCreate('small');
+ innerError.className = 'innerError';
+ innerError.textContent = Language.get('wcf.page.search.error.tooShort');
+ DomUtil.insertAfter(innerError, inputContainer);
+ return;
+ }
+
+ Ajax.api(this, {
+ parameters: {
+ searchString: value
+ }
+ });
+ },
+
+ _click: function (event) {
+ event.preventDefault();
+
+ _callbackSelect(elData(event.currentTarget, 'page-id'));
+
+ UiDialog.close(this);
+ },
+
+ _ajaxSuccess: function(data) {
+ var html = '', page;
+ //noinspection JSUnresolvedVariable
+ for (var i = 0, length = data.returnValues.length; i < length; i++) {
+ //noinspection JSUnresolvedVariable
+ page = data.returnValues[i];
+
+ html += '<li>'
+ + '<div class="containerHeadline pointer" data-page-id="' + page.pageID + '">'
+ + '<h3>' + StringUtil.escapeHTML(page.name) + '</h3>'
+ + '<small>' + StringUtil.escapeHTML(page.displayLink) + '</small>'
+ + '</div>'
+ + '</li>';
+ }
+
+ _resultList.innerHTML = html;
+
+ window[html ? 'elShow' : 'elHide'](_resultContainer);
+
+ if (html) {
+ elBySelAll('.containerHeadline', _resultList, (function(item) {
+ item.addEventListener(WCF_CLICK_EVENT, this._click.bind(this));
+ }).bind(this));
+ }
+ else {
+ var innerError = elCreate('small');
+ innerError.className = 'innerError';
+ innerError.textContent = Language.get('wcf.page.search.error.noResults');
+ DomUtil.insertAfter(innerError, _searchInput.parentNode);
+ }
+ },
+
+ _ajaxSetup: function () {
+ return {
+ data: {
+ actionName: 'search',
+ className: 'wcf\\data\\page\\PageAction'
+ }
+ };
+ },
+
+ _dialogSetup: function() {
+ return {
+ id: 'wcfUiPageSearch',
+ options: {
+ onSetup: (function() {
+ var callbackSearch = this._search.bind(this);
+
+ _searchInput = elById('wcfUiPageSearchInput');
+ _searchInput.addEventListener('keydown', function(event) {
+ if (EventKey.Enter(event)) {
+ callbackSearch(event);
+ }
+ });
+
+ _searchInput.nextElementSibling.addEventListener(WCF_CLICK_EVENT, callbackSearch);
+
+ _resultContainer = elById('wcfUiPageSearchResultContainer');
+ _resultList = elById('wcfUiPageSearchResultList');
+ }).bind(this),
+ onShow: function() {
+ _searchInput.focus();
+ },
+ title: Language.get('wcf.page.search')
+ },
+ source: '<div class="section">'
+ + '<dl>'
+ + '<dt><label for="wcfUiPageSearchInput">' + Language.get('wcf.page.search.name') + '</label></dt>'
+ + '<dd>'
+ + '<div class="inputAddon">'
+ + '<input type="text" id="wcfUiPageSearchInput" class="long">'
+ + '<a href="#" class="inputSuffix"><span class="icon icon16 fa-search"></span></a>'
+ + '</div>'
+ + '</dd>'
+ + '</dl>'
+ + '</div>'
+ + '<section id="wcfUiPageSearchResultContainer" class="section" style="display: none;">'
+ + '<header class="sectionHeader">'
+ + '<h2 class="sectionTitle">' + Language.get('wcf.page.search.results') + '</h2>'
+ + '<p class="sectionDescription">' + Language.get('wcf.page.search.results.description') + '</p>'
+ + '</header>'
+ + '<ol id="wcfUiPageSearchResultList" class="containerList"></ol>'
+ + '</section>'
+ };
+ }
+ };
+});
--- /dev/null
+/**
+ * Converts `<woltlab-metacode>` into the bbcode representation.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2016 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLab/WCF/Ui/Redactor/Metacode
+ */
+define(['WoltLab/WCF/Ui/Page/Search'], function(UiPageSearch) {
+ "use strict";
+
+ function UiRedactorPage(editor, button) { this.init(editor, button); }
+ UiRedactorPage.prototype = {
+ init: function (editor, button) {
+ this._editor = editor;
+
+ button.addEventListener(WCF_CLICK_EVENT, this._click.bind(this))
+ },
+
+ _click: function (event) {
+ event.preventDefault();
+
+ UiPageSearch.open(this._insert.bind(this));
+ },
+
+ _insert: function (pageID) {
+ this._editor.buffer.set();
+
+ this._editor.insert.text("[wsp='" + pageID + "'][/wsp]");
+ }
+ };
+
+ return UiRedactorPage;
+});
/**
* @inheritDoc
*/
- protected $requireACP = ['create', 'delete', 'toggle', 'update'];
+ protected $requireACP = ['create', 'delete', 'getSearchResultList', 'search', 'toggle', 'update'];
/**
* @inheritDoc
return $this->pageEditor->getHandler()->lookup($this->parameters['data']['searchString']);
}
+ /**
+ * Validates parameters to search for a page by its internal name.
+ */
+ public function validateSearch() {
+ $this->readString('searchString');
+ }
+
+ /**
+ * Searches for a page by its internal name.
+ *
+ * @return array list of matching pages
+ */
+ public function search() {
+ $sql = "SELECT pageID
+ FROM wcf".WCF_N."_page
+ WHERE name LIKE ?
+ AND requireObjectID = ?
+ ORDER BY name";
+ $statement = WCF::getDB()->prepareStatement($sql, 5);
+ $statement->execute([
+ '%' . $this->parameters['searchString'] . '%',
+ 0
+ ]);
+
+ $pages = [];
+ while ($pageID = $statement->fetchColumn()) {
+ $page = PageCache::getInstance()->getPage($pageID);
+ $pages[] = [
+ 'displayLink' => $page->getDisplayLink(),
+ 'name' => $page->name,
+ 'pageID' => $pageID
+ ];
+ }
+
+ return $pages;
+ }
+
/**
* @inheritDoc
*/
public function getFormattedContent() {
// assign embedded objects
MessageEmbeddedObjectManager::getInstance()->setActiveMessage('com.woltlab.wcf.page.content', $this->pageContentID);
+ MessageEmbeddedObjectManager::getInstance()->loadObjects('com.woltlab.wcf.page.content', [$this->pageContentID]);
$processor = new HtmlOutputProcessor();
$processor->process($this->content, 'com.woltlab.wcf.page.content', $this->pageContentID);
--- /dev/null
+<?php
+namespace wcf\system\bbcode;
+use wcf\data\page\Page;
+use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
+use wcf\util\StringUtil;
+
+/**
+ * Parses the [wsp] bbcode tag.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2016 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Bbcode
+ * @since 3.0
+ */
+class WoltLabSuitePageBBCode extends AbstractBBCode {
+ /**
+ * @inheritDoc
+ */
+ public function getParsedTag(array $openingTag, $content, array $closingTag, BBCodeParser $parser) {
+ $pageID = (!empty($openingTag['attributes'][0])) ? intval($openingTag['attributes'][0]) : 0;
+ if (!$pageID) {
+ return '';
+ }
+
+ /** @var Page $page */
+ $page = MessageEmbeddedObjectManager::getInstance()->getObject('com.woltlab.wcf.page', $pageID);
+ if ($page !== null) {
+ return StringUtil::getAnchorTag($page->getLink(), $page->getTitle());
+ }
+
+ return '';
+ }
+}
namespace wcf\system\message\embedded\object;
use wcf\data\page\Page;
use wcf\data\page\PageCache;
+use wcf\system\html\input\HtmlInputProcessor;
/**
* Parses embedded pages and outputs their link or title.
* @package WoltLabSuite\Core\System\Message\Embedded\Object
*/
class PageMessageEmbeddedObjectHandler extends AbstractSimpleMessageEmbeddedObjectHandler {
+ /**
+ * @inheritDoc
+ */
+ public function parse(HtmlInputProcessor $htmlInputProcessor, array $embeddedData) {
+ $pageIDs = [];
+ if (!empty($embeddedData['wsp'])) {
+ for ($i = 0, $length = count($embeddedData['wsp']); $i < $length; $i++) {
+ $pageIDs[] = intval($embeddedData['wsp'][$i][0]);
+ }
+ }
+
+ return array_unique($pageIDs);
+ }
+
/**
* @inheritDoc
*/