Fixed handling of "empty" messages
authorAlexander Ebert <ebert@woltlab.com>
Fri, 18 Nov 2016 11:29:01 +0000 (12:29 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Fri, 18 Nov 2016 11:29:01 +0000 (12:29 +0100)
wcfsetup/install/files/js/3rdParty/redactor2/plugins/WoltLabClean.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Message/Reply.js
wcfsetup/install/files/lib/form/MessageForm.class.php
wcfsetup/install/files/lib/system/html/input/HtmlInputProcessor.class.php
wcfsetup/install/files/lib/system/html/input/node/HtmlInputNodeProcessor.class.php
wcfsetup/install/files/lib/system/message/QuickReplyManager.class.php

index ab3b26466b15ab6babc6101932f0403a7d534d8f..40070d235f5f2f4d002ee76d582c09b9afbda511 100644 (file)
@@ -15,12 +15,21 @@ $.Redactor.prototype.WoltLabClean = function() {
                                // restore ampersands
                                html = html.replace(/@@@WCF_AMPERSAND@@@/g, '&amp;');
                                
+                               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;
index 453a71f978df1cce0be01a03aa6a2a203bf21edd..a9b2e1c7f849a465f122f6b0870c70fa1ab76a30 100644 (file)
@@ -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);
                },
index beba3dbfd435631a939c64995e476bdf0c4c6f55..ff5105046c5611d212202aaca3893b1e4c929070 100644 (file)
@@ -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');
index 10719fbbc7cf70c13fd5f8ef290900dae8cf8059..0af91b523a7bc71b10edf5703ed62fe41995ffa7 100644 (file)
@@ -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.
         *
index 34bb064d41102c3094beda8fac9c4a1e6f501dcf..dbd63c9775d33e0828d737df9de452f6cff5e2c1 100644 (file)
@@ -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.
         */
index 127cac79fdb502365a86c65c888c2d2d1ae681c6..fbf334207882e833c955c9af8e771fcede30f285 100644 (file)
@@ -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']);