From: Alexander Ebert Date: Thu, 26 May 2016 08:35:06 +0000 (+0200) Subject: Improved HTML parser + editor integration X-Git-Tag: 3.0.0_Beta_1~1630^2~1 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=d85b1843c712605f4fb3fee8622cc6b3d206fc85;p=GitHub%2FWoltLab%2FWCF.git Improved HTML parser + editor integration --- diff --git a/com.woltlab.wcf/templates/messageFormSettings.tpl b/com.woltlab.wcf/templates/messageFormSettings.tpl index 2da8f1a448..fce986a290 100644 --- a/com.woltlab.wcf/templates/messageFormSettings.tpl +++ b/com.woltlab.wcf/templates/messageFormSettings.tpl @@ -1,41 +1,10 @@ -
-
- {if $__wcf->getSession()->getPermission($permissionCanUseBBCodes)} -
-
- - {lang}wcf.message.settings.preParse.description{/lang} -
- {/if} - {if MODULE_SMILEY && $__wcf->getSession()->getPermission($permissionCanUseSmilies)} -
-
- - {lang}wcf.message.settings.enableSmilies.description{/lang} -
- {/if} - {if $__wcf->getSession()->getPermission($permissionCanUseBBCodes)} -
-
- - {lang}wcf.message.settings.enableBBCodes.description{/lang} -
- {/if} - {if $__wcf->getSession()->getPermission($permissionCanUseHtml)} -
-
- - {lang}wcf.message.settings.enableHtml.description{/lang} -
- {/if} - {if MODULE_USER_SIGNATURE && !$showSignatureSetting|empty && $__wcf->user->userID} -
-
- - {lang}wcf.message.settings.showSignature.description{/lang} -
- {/if} - - {event name='settings'} -
-
+{capture assign='__messageFormSettings'} + {hascontent} +
+
+ {content}{event name='settings'}{/content} +
+
+ {/hascontent} +{/capture} +{assign var='__messageFormSettings' value=$__messageFormSettings|trim} diff --git a/com.woltlab.wcf/templates/messageFormTabs.tpl b/com.woltlab.wcf/templates/messageFormTabs.tpl index ad5e670912..188a0f06de 100644 --- a/com.woltlab.wcf/templates/messageFormTabs.tpl +++ b/com.woltlab.wcf/templates/messageFormTabs.tpl @@ -1,18 +1,21 @@ +{* the settings template does not generate direct ouput anymore, but captures it content *} +{include file='messageFormSettings'} +
- {if MODULE_SMILEY && $__wcf->getSession()->getPermission($permissionCanUseSmilies) && $smileyCategories|count}{include file='messageFormSmilies'}{/if} + {if MODULE_SMILEY && !$smileyCategories|empty}{include file='messageFormSmilies'}{/if} {if MODULE_ATTACHMENT && !$attachmentHandler|empty && $attachmentHandler->canUpload()}{include file='messageFormAttachments'}{/if} - {include file='messageFormSettings'} + {if $__messageFormSettings}{@$__messageFormSettings}{/if} {include file='__messageFormPoll'} {event name='tabMenuContents'} diff --git a/com.woltlab.wcf/templates/wysiwyg.tpl b/com.woltlab.wcf/templates/wysiwyg.tpl index 943c2cc7b4..1529901c03 100644 --- a/com.woltlab.wcf/templates/wysiwyg.tpl +++ b/com.woltlab.wcf/templates/wysiwyg.tpl @@ -8,74 +8,78 @@ } {* diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Dom/Util.js b/wcfsetup/install/files/js/WoltLab/WCF/Dom/Util.js index 4e582ca990..9cd587f56a 100644 --- a/wcfsetup/install/files/js/WoltLab/WCF/Dom/Util.js +++ b/wcfsetup/install/files/js/WoltLab/WCF/Dom/Util.js @@ -166,11 +166,11 @@ define(['Environment', 'StringUtil'], function(Environment, StringUtil) { * @param {Element} parentEl future containing element */ prepend: function(el, parentEl) { - if (parentEl.childElementCount === 0) { + if (parentEl.childNodes.length === 0) { parentEl.appendChild(el); } else { - parentEl.insertBefore(el, parentEl.children[0]); + parentEl.insertBefore(el, parentEl.childNodes[0]); } }, diff --git a/wcfsetup/install/files/js/WoltLab/WCF/Ui/Redactor/Metacode.js b/wcfsetup/install/files/js/WoltLab/WCF/Ui/Redactor/Metacode.js new file mode 100644 index 0000000000..a988193d8d --- /dev/null +++ b/wcfsetup/install/files/js/WoltLab/WCF/Ui/Redactor/Metacode.js @@ -0,0 +1,148 @@ +/** + * Converts `` into the bbcode representation. + * + * @author Alexander Ebert + * @copyright 2001-2016 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLab/WCF/Ui/Redactor/Metacode + */ +define(['Dom/Util'], function(DomUtil) { + "use strict"; + + /** + * @exports WoltLab/WCF/Ui/Redactor/Metacode + */ + return { + /** + * Converts `` into the bbcode representation. + * + * @param {Element} element textarea element + */ + convert: function(element) { + var div = elCreate('div'); + div.innerHTML = element.textContent; + + var attributes, metacode, metacodes = elByTag('woltlab-metacode', div), name, tagClose, tagOpen; + while (metacodes.length) { + metacode = metacodes[0]; + name = elData(metacode, 'name'); + attributes = elData(metacode, 'attributes'); + + tagOpen = this._getOpeningTag(name, attributes); + tagClose = this._getClosingTag(name); + + if (metacode.parentNode === div) { + DomUtil.prepend(tagOpen, this._getFirstParagraph(metacode)); + this._getLastParagraph(metacode).appendChild(tagClose); + } + else { + DomUtil.prepend(tagOpen, metacode); + metacode.appendChild(tagClose); + } + + DomUtil.unwrapChildNodes(metacode); + } + + element.textContent = div.innerHTML; + }, + + /** + * Returns a text node representing the opening bbcode tag. + * + * @param {string} name bbcode tag + * @param {string} attributes base64- and JSON-encoded attributes + * @returns {Text} text node containing the opening bbcode tag + * @protected + */ + _getOpeningTag: function(name, attributes) { + try { + attributes = JSON.parse(atob(attributes)); + } + catch (e) { /* invalid base64 data or invalid json */ } + + if (!Array.isArray(attributes)) { + attributes = []; + } + + var buffer = '[' + name; + if (attributes.length) { + buffer += '=' + attributes.join(','); + } + + return document.createTextNode(buffer + ']'); + }, + + /** + * Returns a text node representing the closing bbcode tag. + * + * @param {string} name bbcode tag + * @returns {Text} text node containing the closing bbcode tag + * @protected + */ + _getClosingTag: function(name) { + return document.createTextNode('[/' + name + ']'); + }, + + /** + * Returns the first paragraph of provided element. If there are no children or + * the first child is not a paragraph, a new paragraph is created and inserted + * as first child. + * + * @param {Element} element metacode element + * @returns {Element} paragraph that is the first child of provided element + * @protected + */ + _getFirstParagraph: function (element) { + var firstChild, paragraph; + + if (element.childElementCount === 0) { + paragraph = elCreate('p'); + element.appendChild(paragraph); + } + else { + firstChild = element.children[0]; + + if (firstChild.nodeName === 'P') { + paragraph = firstChild; + } + else { + paragraph = elCreate('p'); + element.insertBefore(paragraph, firstChild); + } + } + + return paragraph; + }, + + /** + * Returns the last paragraph of provided element. If there are no children or + * the last child is not a paragraph, a new paragraph is created and inserted + * as last child. + * + * @param {Element} element metacode element + * @returns {Element} paragraph that is the last child of provided element + * @protected + */ + _getLastParagraph: function (element) { + var count = element.childElementCount, lastChild, paragraph; + + if (count === 0) { + paragraph = elCreate('p'); + element.appendChild(paragraph); + } + else { + lastChild = element.children[count - 1]; + + if (lastChild.nodeName === 'P') { + paragraph = lastChild; + } + else { + paragraph = elCreate('p'); + element.appendChild(paragraph); + } + } + + return paragraph; + } + }; +}); diff --git a/wcfsetup/install/files/lib/data/IMessageQuickReplyAction.class.php b/wcfsetup/install/files/lib/data/IMessageQuickReplyAction.class.php index 6e72ff5d5d..03d592c038 100644 --- a/wcfsetup/install/files/lib/data/IMessageQuickReplyAction.class.php +++ b/wcfsetup/install/files/lib/data/IMessageQuickReplyAction.class.php @@ -1,5 +1,6 @@ subject = StringUtil::trim(MessageUtil::stripCrap($_POST['subject'])); if (isset($_POST['text'])) $this->text = StringUtil::trim(MessageUtil::stripCrap($_POST['text'])); - // settings - $this->enableSmilies = $this->enableHtml = $this->enableBBCodes = $this->preParse = $this->showSignature = 0; - if (isset($_POST['preParse'])) $this->preParse = intval($_POST['preParse']); - if (isset($_POST['enableSmilies']) && WCF::getSession()->getPermission($this->permissionCanUseSmilies)) $this->enableSmilies = intval($_POST['enableSmilies']); - if (isset($_POST['enableHtml']) && WCF::getSession()->getPermission($this->permissionCanUseHtml)) $this->enableHtml = intval($_POST['enableHtml']); - if (isset($_POST['enableBBCodes']) && WCF::getSession()->getPermission($this->permissionCanUseBBCodes)) $this->enableBBCodes = intval($_POST['enableBBCodes']); - if (isset($_POST['showSignature'])) $this->showSignature = intval($_POST['showSignature']); - // multilingualism if (isset($_POST['languageID'])) $this->languageID = intval($_POST['languageID']); } @@ -267,6 +210,12 @@ abstract class MessageForm extends AbstractCaptchaForm { throw new UserInputException('text', 'tooLong'); } + $this->htmlInputProcessor = new HtmlInputProcessor(); + $this->htmlInputProcessor->process($this->text); + + // TODO: add checks for disallowed bbcodes and stuff + $this->htmlInputProcessor->validate(); + /*if ($this->enableBBCodes && $this->allowedBBCodesPermission) { $disallowedBBCodes = BBCodeParser::getInstance()->validateBBCodes($this->text, ArrayUtil::trim(explode(',', WCF::getSession()->getPermission($this->allowedBBCodesPermission)))); if (!empty($disallowedBBCodes)) { @@ -305,8 +254,7 @@ abstract class MessageForm extends AbstractCaptchaForm { public function save() { parent::save(); - $htmlInputProcessor = new HtmlInputProcessor(); - $this->text = $htmlInputProcessor->process($this->text); + $this->text = $this->htmlInputProcessor->getHtml(); // parse URLs /* TODO @@ -338,11 +286,6 @@ abstract class MessageForm extends AbstractCaptchaForm { } if (empty($_POST)) { - $this->enableBBCodes = (ENABLE_BBCODES_DEFAULT_VALUE && WCF::getSession()->getPermission($this->permissionCanUseBBCodes)) ? 1 : 0; - $this->enableHtml = (ENABLE_HTML_DEFAULT_VALUE && WCF::getSession()->getPermission($this->permissionCanUseHtml)) ? 1 : 0; - $this->enableSmilies = (ENABLE_SMILIES_DEFAULT_VALUE && WCF::getSession()->getPermission($this->permissionCanUseSmilies)) ? 1 : 0; - $this->preParse = PRE_PARSE_DEFAULT_VALUE; - $this->showSignature = SHOW_SIGNATURE_DEFAULT_VALUE; $this->languageID = WCF::getLanguage()->languageID; } @@ -358,7 +301,7 @@ abstract class MessageForm extends AbstractCaptchaForm { } } - if ($this->enableBBCodes && $this->allowedBBCodesPermission) { + if ($this->allowedBBCodesPermission) { BBCodeHandler::getInstance()->setAllowedBBCodes(explode(',', WCF::getSession()->getPermission($this->allowedBBCodesPermission))); } } @@ -376,17 +319,8 @@ abstract class MessageForm extends AbstractCaptchaForm { 'attachmentParentObjectID' => $this->attachmentParentObjectID, 'availableContentLanguages' => $this->availableContentLanguages, 'defaultSmilies' => $this->defaultSmilies, - 'enableBBCodes' => $this->enableBBCodes, - 'enableHtml' => $this->enableHtml, - 'enableSmilies' => $this->enableSmilies, 'languageID' => ($this->languageID ?: 0), 'maxTextLength' => $this->maxTextLength, - 'permissionCanUseBBCodes' => $this->permissionCanUseBBCodes, - 'permissionCanUseHtml' => $this->permissionCanUseHtml, - 'permissionCanUseSmilies' => $this->permissionCanUseSmilies, - 'preParse' => $this->preParse, - 'showSignature' => $this->showSignature, - 'showSignatureSetting' => $this->showSignatureSetting, 'smileyCategories' => $this->smileyCategories, 'subject' => $this->subject, 'text' => $this->text, diff --git a/wcfsetup/install/files/lib/system/bbcode/HtmlBBCodeParser.class.php b/wcfsetup/install/files/lib/system/bbcode/HtmlBBCodeParser.class.php index cd2744a4ad..589db82648 100644 --- a/wcfsetup/install/files/lib/system/bbcode/HtmlBBCodeParser.class.php +++ b/wcfsetup/install/files/lib/system/bbcode/HtmlBBCodeParser.class.php @@ -256,6 +256,15 @@ class HtmlBBCodeParser extends BBCodeParser { $attributes = ''; if (!empty($tag['attributes'])) { + // strip outer quote tags + $tag['attributes'] = array_map(function($attribute) { + if (preg_match('~^([\'"])(?P.*)(\1)$~', $attribute, $matches)) { + return $matches['content']; + } + + return $attribute; + }, $tag['attributes']); + // uses base64 encoding to avoid an "escape" nightmare $attributes = ' data-attributes="' . base64_encode(JSON::encode($tag['attributes'])) . '"'; } diff --git a/wcfsetup/install/files/lib/system/html/input/HtmlInputProcessor.class.php b/wcfsetup/install/files/lib/system/html/input/HtmlInputProcessor.class.php index 7847d442fe..0f1ae9e0ae 100644 --- a/wcfsetup/install/files/lib/system/html/input/HtmlInputProcessor.class.php +++ b/wcfsetup/install/files/lib/system/html/input/HtmlInputProcessor.class.php @@ -4,6 +4,8 @@ use wcf\system\bbcode\HtmlBBCodeParser; use wcf\system\html\input\filter\IHtmlInputFilter; use wcf\system\html\input\filter\MessageHtmlInputFilter; use wcf\system\html\input\node\HtmlInputNodeProcessor; +use wcf\system\html\input\node\IHtmlInputNodeProcessor; +use wcf\system\html\node\IHtmlNodeProcessor; use wcf\util\StringUtil; /** @@ -11,13 +13,15 @@ use wcf\util\StringUtil; * @since 2.2 */ class HtmlInputProcessor { + protected $embeddedContent = []; + /** * @var IHtmlInputFilter */ protected $htmlInputFilter; /** - * @var HtmlInputNodeProcessor + * @var IHtmlInputNodeProcessor */ protected $htmlInputNodeProcessor; @@ -34,16 +38,19 @@ class HtmlInputProcessor { // pre-parse HTML $this->getHtmlInputNodeProcessor()->load($html); $this->getHtmlInputNodeProcessor()->process(); - - return $this->getHtmlInputNodeProcessor()->getHtml(); + $this->embeddedContent = $this->getHtmlInputNodeProcessor()->getEmbeddedContent(); } - public function setHtmlInputFilter(IHtmlInputFilter $htmlInputFilter) { - $this->htmlInputFilter = $htmlInputFilter; + public function validate() { + // TODO + } + + public function getHtml() { + return $this->getHtmlInputNodeProcessor()->getHtml(); } /** - * @return IHtmlInputFilter|MessageHtmlInputFilter + * @return IHtmlInputFilter */ public function getHtmlInputFilter() { if ($this->htmlInputFilter === null) { @@ -53,12 +60,12 @@ class HtmlInputProcessor { return $this->htmlInputFilter; } - public function setHtmlInputNodeProcessor(HtmlInputNodeProcessor $htmlInputNodeProcessor) { - $this->htmlInputNodeProcessor = $htmlInputNodeProcessor; + public function setHtmlInputFilter(IHtmlInputFilter $htmlInputFilter) { + $this->htmlInputFilter = $htmlInputFilter; } /** - * @return HtmlInputNodeProcessor + * @return IHtmlInputNodeProcessor */ public function getHtmlInputNodeProcessor() { if ($this->htmlInputNodeProcessor === null) { @@ -67,4 +74,12 @@ class HtmlInputProcessor { return $this->htmlInputNodeProcessor; } + + public function setHtmlInputNodeProcessor(IHtmlNodeProcessor $htmlInputNodeProcessor) { + $this->htmlInputNodeProcessor = $htmlInputNodeProcessor; + } + + public function getEmbeddedContent() { + return $this->embeddedContent; + } } diff --git a/wcfsetup/install/files/lib/system/html/input/node/HtmlInputNodeProcessor.class.php b/wcfsetup/install/files/lib/system/html/input/node/HtmlInputNodeProcessor.class.php index c4c6f9f464..a1d6ae6221 100644 --- a/wcfsetup/install/files/lib/system/html/input/node/HtmlInputNodeProcessor.class.php +++ b/wcfsetup/install/files/lib/system/html/input/node/HtmlInputNodeProcessor.class.php @@ -1,5 +1,6 @@ fireAction($this, 'beforeProcess'); + + $this->embeddedContent = []; + // process metacode markers first $this->invokeHtmlNode(new HtmlInputNodeWoltlabMetacodeMarker()); // handle static converters $this->invokeHtmlNode(new HtmlInputNodeWoltlabMetacode()); + // extract embedded content + $this->parseEmbeddedContent(); + // remove empty elements and join identical siblings if appropriate $this->cleanup(); + + EventHandler::getInstance()->fireAction($this, 'afterProcess'); + } + + /** + * @inheritDoc + */ + public function getEmbeddedContent() { + return $this->embeddedContent; + } + + public function addEmbeddedContent($type, array $data) { + $this->embeddedContent[$type] = $data; + } + + protected function parseEmbeddedContent() { + // handle `woltlab-metacode` + $elements = $this->getDocument()->getElementsByTagName('woltlab-metacode'); + $metacodesByName = []; + for ($i = 0, $length = $elements->length; $i < $length; $i++) { + /** @var \DOMElement $element */ + $element = $elements->item($i); + $name = $element->getAttribute('data-name'); + $attributes = $this->parseAttributes($element->getAttribute('data-attributes')); + + if (!isset($metacodesByName[$name])) $metacodesByName[$name] = []; + $metacodesByName[$name][] = $attributes; + } + + $this->embeddedContent = $metacodesByName; + + EventHandler::getInstance()->fireAction($this, 'parseEmbeddedContent'); } protected function cleanup() { diff --git a/wcfsetup/install/files/lib/system/html/input/node/IHtmlInputNodeProcessor.class.php b/wcfsetup/install/files/lib/system/html/input/node/IHtmlInputNodeProcessor.class.php new file mode 100644 index 0000000000..8911a8af6a --- /dev/null +++ b/wcfsetup/install/files/lib/system/html/input/node/IHtmlInputNodeProcessor.class.php @@ -0,0 +1,12 @@ + - * @package com.woltlab.wcf - * @subpackage system.message - * @category Community Framework - */ -class MessageFormSettingsHandler { - /** - * Computes the settings for BBCodes, Smilies and pre parsing. Optionally accepts the corresponding DatabaseObject - * whose values will be used in case the settings did not contain the individual values (legacy support). - * - * @param mixed[][] $parameters - * @param \wcf\data\DatabaseObject $object - * @param string $permissionCanUseBBCodes - * @param string $permissionCanUseSmilies - * @return array - */ - public static function getSettings(array $parameters, DatabaseObject $object = null, $permissionCanUseBBCodes = '', $permissionCanUseSmilies = '') { - $permissionCanUseBBCodes = ($permissionCanUseBBCodes) ?: 'user.message.canUseBBCodes'; - $permissionCanUseSmilies = ($permissionCanUseSmilies) ?: 'user.message.canUseSmilies'; - - $enableSmilies = 0; - $enableBBCodes = 0; - $preParse = 0; - - if (WCF::getSession()->getPermission($permissionCanUseSmilies)) { - if (isset($parameters['enableSmilies'])) { - $enableSmilies = ($parameters['enableSmilies']) ? 1 : 0; - } - else { - $enableSmilies = ($object === null) ? 1 : $object->enableSmilies; - } - } - else if ($object !== null) { - $enableSmilies = ($object->enableSmilies) ? 1 : 0; - } - - if (WCF::getSession()->getPermission($permissionCanUseBBCodes)) { - if (isset($parameters['enableBBCodes'])) { - $enableBBCodes = ($parameters['enableBBCodes']) ? 1 : 0; - } - else { - $enableBBCodes = ($object === null) ? 1 : $object->enableBBCodes; - } - - if (isset($parameters['preParse'])) { - $preParse = ($parameters['preParse'] && $enableBBCodes) ? 1 : 0; - } - else { - $preParse = $enableBBCodes; - } - } - else if ($object !== null) { - $enableBBCodes = $preParse = ($object->enableBBCodes) ? 1 : 0; - } - - return [ - 'enableSmilies' => $enableSmilies, - 'enableBBCodes' => $enableBBCodes, - 'preParse' => $preParse - ]; - } -} diff --git a/wcfsetup/install/files/lib/system/message/QuickReplyManager.class.php b/wcfsetup/install/files/lib/system/message/QuickReplyManager.class.php index 972be6253b..fb94481401 100644 --- a/wcfsetup/install/files/lib/system/message/QuickReplyManager.class.php +++ b/wcfsetup/install/files/lib/system/message/QuickReplyManager.class.php @@ -10,6 +10,7 @@ use wcf\system\event\EventHandler; use wcf\system\exception\ParentClassException; use wcf\system\exception\SystemException; use wcf\system\exception\UserInputException; +use wcf\system\html\input\HtmlInputProcessor; use wcf\system\SingletonFactory; use wcf\system\WCF; use wcf\util\ArrayUtil; @@ -145,8 +146,13 @@ class QuickReplyManager extends SingletonFactory { } $object->validateContainer($this->container); + $parameters['htmlInputProcessor'] = $object->getHtmlInputProcessor($parameters['data']['message']); + unset($parameters['data']['message']); + + $parameters['htmlInputProcessor']->validate(); + // validate message - $object->validateMessage($this->container, $parameters['data']['message']); + $object->validateMessage($this->container, $parameters['htmlInputProcessor']); // check for message quote ids $parameters['removeQuoteIDs'] = (isset($parameters['removeQuoteIDs']) && is_array($parameters['removeQuoteIDs'])) ? ArrayUtil::trim($parameters['removeQuoteIDs']) : []; @@ -158,12 +164,6 @@ class QuickReplyManager extends SingletonFactory { unset($parameters['data']['tmpHash']); } - // message settings - $parameters['data'] = array_merge($parameters['data'], MessageFormSettingsHandler::getSettings($parameters)); - - $parameters['data']['enableHtml'] = 0; - $parameters['data']['showSignature'] = (WCF::getUser()->userID ? WCF::getUser()->showSignature : 0); - EventHandler::getInstance()->fireAction($this, 'validateParameters', $parameters); } @@ -171,7 +171,7 @@ class QuickReplyManager extends SingletonFactory { * Creates a new message and returns the parsed template. * * @param \wcf\data\IMessageQuickReplyAction $object - * @param mixed[][] $parameters + * @param array $parameters * @param string $containerActionClassName * @param string $sortOrder * @param string $templateName @@ -189,10 +189,10 @@ class QuickReplyManager extends SingletonFactory { $parameters['data']['username'] = WCF::getUser()->username; // pre-parse message text - if ($parameters['data']['preParse']) { + /*if ($parameters['data']['preParse']) { $parameters['data']['message'] = PreParser::getInstance()->parse($parameters['data']['message'], $this->allowedBBodes); } - unset($parameters['data']['preParse']); + unset($parameters['data']['preParse']);*/ $parameters['data'] = array_merge($additionalFields, $parameters['data']); @@ -206,9 +206,7 @@ class QuickReplyManager extends SingletonFactory { EventHandler::getInstance()->fireAction($this, 'createdMessage', $eventParameters); if ($message instanceof IMessage && !$message->isVisible()) { - return [ - 'isVisible' => false - ]; + return ['isVisible' => false]; } // resolve the page no @@ -248,9 +246,7 @@ class QuickReplyManager extends SingletonFactory { } else { // redirect - return [ - 'url' => $object->getRedirectUrl($this->container, $message) - ]; + return ['url' => $object->getRedirectUrl($this->container, $message)]; } } diff --git a/wcfsetup/install/files/lib/system/template/plugin/LangCompilerTemplatePlugin.class.php b/wcfsetup/install/files/lib/system/template/plugin/LangCompilerTemplatePlugin.class.php index e8e4154921..f387ab870b 100644 --- a/wcfsetup/install/files/lib/system/template/plugin/LangCompilerTemplatePlugin.class.php +++ b/wcfsetup/install/files/lib/system/template/plugin/LangCompilerTemplatePlugin.class.php @@ -32,6 +32,34 @@ class LangCompilerTemplatePlugin implements ICompilerTemplatePlugin { */ public function executeEnd(TemplateScriptingCompiler $compiler) { $compiler->popTag('lang'); - return "tagStack[count(\$this->tagStack) - 1][1]['__literal']) ? wcf\system\WCF::getLanguage()->get(ob_get_clean(), \$this->tagStack[count(\$this->tagStack) - 1][1], (isset(\$this->tagStack[count(\$this->tagStack) - 1][1]['__optional']) ? \$this->tagStack[count(\$this->tagStack) - 1][1]['__optional'] : false)) : wcf\system\WCF::getLanguage()->getDynamicVariable(ob_get_clean(), \$this->tagStack[count(\$this->tagStack) - 1][1], (isset(\$this->tagStack[count(\$this->tagStack) - 1][1]['__optional']) ? \$this->tagStack[count(\$this->tagStack) - 1][1]['__optional'] : false))); array_pop(\$this->tagStack); ?>"; + return "tagStack[count(\$this->tagStack) - 1][1]['__literal']) + ? + wcf\system\WCF::getLanguage()->get( + ob_get_clean(), + \$this->tagStack[count(\$this->tagStack) - 1][1], + ( + isset(\$this->tagStack[count(\$this->tagStack) - 1][1]['__optional']) + ? + \$this->tagStack[count(\$this->tagStack) - 1][1]['__optional'] + : + false + ) + ) + : + wcf\system\WCF::getLanguage()->getDynamicVariable( + ob_get_clean(), + \$this->tagStack[count(\$this->tagStack) - 1][1], + ( + isset(\$this->tagStack[count(\$this->tagStack) - 1][1]['__optional']) + ? + \$this->tagStack[count(\$this->tagStack) - 1][1]['__optional'] + : + false + ) + ) + ); + array_pop(\$this->tagStack); ?>"; } } diff --git a/wcfsetup/install/files/lib/util/DOMUtil.class.php b/wcfsetup/install/files/lib/util/DOMUtil.class.php index a92ffb2c45..3ca32b7b3b 100644 --- a/wcfsetup/install/files/lib/util/DOMUtil.class.php +++ b/wcfsetup/install/files/lib/util/DOMUtil.class.php @@ -417,7 +417,12 @@ final class DOMUtil { $cloneNode = self::getParentBefore($node, $ancestor); if ($splitBefore) { - if (self::isFirstNode($node, $cloneNode)) { + if ($cloneNode === null) { + // target node is already a direct descendant of the ancestor + // node, no need to split anything + return $node; + } + else if (self::isFirstNode($node, $cloneNode)) { // target node is at the very start, we can safely move the // entire parent node around return $cloneNode; @@ -436,7 +441,12 @@ final class DOMUtil { } } else { - if (self::isLastNode($node, $cloneNode)) { + if ($cloneNode === null) { + // target node is already a direct descendant of the ancestor + // node, no need to split anything + return $node; + } + else if (self::isLastNode($node, $cloneNode)) { // target node is at the very end, we can safely move the // entire parent node around return $cloneNode;