From 76535aa682b82a14e4d0ddb902d3545d08fb2940 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Fri, 18 Nov 2016 12:29:01 +0100 Subject: [PATCH] Fixed handling of "empty" messages --- .../redactor2/plugins/WoltLabClean.js | 13 +++++- .../js/WoltLabSuite/Core/Ui/Message/Reply.js | 2 +- .../files/lib/form/MessageForm.class.php | 3 ++ .../html/input/HtmlInputProcessor.class.php | 9 ++++ .../node/HtmlInputNodeProcessor.class.php | 46 +++++++++++++++++++ .../message/QuickReplyManager.class.php | 3 ++ 6 files changed, 73 insertions(+), 3 deletions(-) diff --git a/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabClean.js b/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabClean.js index ab3b26466b..40070d235f 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabClean.js +++ b/wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabClean.js @@ -15,12 +15,21 @@ $.Redactor.prototype.WoltLabClean = function() { // restore ampersands html = html.replace(/@@@WCF_AMPERSAND@@@/g, '&'); + var div = elCreate('div'); + div.innerHTML = html; + // remove iframes smuggled into the HTML by the user // they're removed on the server anyway, but keeping // them in the wysiwyg may lead to false impressions - var div = elCreate('div'); - div.innerHTML = html; elBySelAll('iframe', div, elRemove); + + // strip script tags + elBySelAll('pre', div, function (pre) { + if (pre.classList.contains('redactor-script-tag')) { + elRemove(pre); + } + }); + html = div.innerHTML; return html; diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Message/Reply.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Message/Reply.js index 453a71f978..a9b2e1c7f8 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Message/Reply.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Message/Reply.js @@ -197,7 +197,7 @@ define(['Ajax', 'Core', 'EventHandler', 'Language', 'Dom/ChangeListener', 'Dom/U throwError: function(element, message) { var error = elCreate('small'); error.className = 'innerError'; - error.textContent = message; + error.textContent = (message === 'empty' ? Language.get('wcf.global.form.error.empty') : message); DomUtil.insertAfter(error, element); }, diff --git a/wcfsetup/install/files/lib/form/MessageForm.class.php b/wcfsetup/install/files/lib/form/MessageForm.class.php index beba3dbfd4..ff5105046c 100644 --- a/wcfsetup/install/files/lib/form/MessageForm.class.php +++ b/wcfsetup/install/files/lib/form/MessageForm.class.php @@ -218,6 +218,9 @@ abstract class MessageForm extends AbstractCaptchaForm { $this->htmlInputProcessor->process($this->text, $this->messageObjectType, 0); // check text length + if ($this->htmlInputProcessor->appearsToBeEmpty()) { + throw new UserInputException('text'); + } $message = $this->htmlInputProcessor->getTextContent(); if ($this->maxTextLength != 0 && mb_strlen($message) > $this->maxTextLength) { throw new UserInputException('text', 'tooLong'); 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 10719fbbc7..0af91b523a 100644 --- a/wcfsetup/install/files/lib/system/html/input/HtmlInputProcessor.class.php +++ b/wcfsetup/install/files/lib/system/html/input/HtmlInputProcessor.class.php @@ -134,6 +134,15 @@ class HtmlInputProcessor extends AbstractHtmlProcessor { return $this->getHtmlInputNodeProcessor()->getTextContent(); } + /** + * Returns true if the message appears to be empty. + * + * @return boolean true if message appears to be empty + */ + public function appearsToBeEmpty() { + return $this->getHtmlInputNodeProcessor()->appearsToBeEmpty(); + } + /** * Returns the all embedded content data. * 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 34bb064d41..dbd63c9775 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 @@ -33,6 +33,29 @@ class HtmlInputNodeProcessor extends AbstractHtmlNodeProcessor { 'td' => ['text-center', 'text-justify', 'text-right'] ]; + /** + * list of HTML elements that are treated as empty, that means + * they don't generate any (indirect) output at all + * + * @var string[] + */ + public static $emptyTags = [ + // typical wrappers + 'div', 'p', 'span', + + // headlines + 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', + + // tables + 'table', 'tbody', 'thead', 'tr', 'th', 'td', 'colgroup', 'col', + + // lists + 'ul', 'ol', 'li', + + // other + 'a', 'kbd', 'woltlab-quote', 'woltlab-spoiler', 'pre', 'sub', 'sup' + ]; + /** * list of embedded content grouped by type * @var array @@ -316,6 +339,29 @@ class HtmlInputNodeProcessor extends AbstractHtmlNodeProcessor { return StringUtil::trim($this->getDocument()->getElementsByTagName('body')->item(0)->textContent); } + /** + * Returns true if the message appears to be empty. + * + * @return boolean true if message appears to be empty + */ + public function appearsToBeEmpty() { + if ($this->getTextContent() !== '') { + return false; + } + + /** @var \DOMElement $body */ + $body = $this->getDocument()->getElementsByTagName('body')->item(0); + + /** @var \DOMElement $element */ + foreach ($body->getElementsByTagName('*') as $element) { + if (!in_array($element->nodeName, self::$emptyTags)) { + return false; + } + } + + return true; + } + /** * Processes embedded content. */ diff --git a/wcfsetup/install/files/lib/system/message/QuickReplyManager.class.php b/wcfsetup/install/files/lib/system/message/QuickReplyManager.class.php index 127cac79fd..fbf3342078 100644 --- a/wcfsetup/install/files/lib/system/message/QuickReplyManager.class.php +++ b/wcfsetup/install/files/lib/system/message/QuickReplyManager.class.php @@ -142,6 +142,9 @@ class QuickReplyManager extends SingletonFactory { unset($parameters['data']['message']); $parameters['htmlInputProcessor']->validate(); + if ($parameters['htmlInputProcessor']->appearsToBeEmpty()) { + throw new UserInputException('message'); + } // validate message $object->validateMessage($this->container, $parameters['htmlInputProcessor']); -- 2.20.1