Added option to configure allowed quote nesting depth
authorAlexander Ebert <ebert@woltlab.com>
Thu, 15 Sep 2016 21:52:26 +0000 (23:52 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Thu, 15 Sep 2016 21:52:33 +0000 (23:52 +0200)
com.woltlab.wcf/option.xml
constants.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/quote/MessageQuoteManager.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index 3dcc445f0a68829b0f2a3e8d86bd8523b9822c08..86d9637b3661ef7025749964d29d459674463122 100644 (file)
@@ -1393,6 +1393,14 @@ DESC:wcf.global.sortOrder.descending</selectoptions>
                                <maxvalue>255</maxvalue>
                                <options>module_tagging</options>
                        </option>
+                       
+                       <option name="message_max_quote_depth">
+                               <categoryname>message.general</categoryname>
+                               <optiontype>integer</optiontype>
+                               <defaultvalue>1</defaultvalue>
+                               <minvalue>0</minvalue>
+                               <maxvalue>255</maxvalue>
+                       </option>
                        <!-- /message.general -->
                        
                        <option name="search_results_per_page">
index e3a79a04bac31901d5a8c8e7e4250dc3cb58dd26..1d34370db80a714e22a57489fa20061b292c2e69 100644 (file)
@@ -191,6 +191,7 @@ define('MESSAGE_SIDEBAR_ENABLE_ACTIVITY_POINTS', 0);
 define('MESSAGE_SIDEBAR_ENABLE_USER_ONLINE_MARKING', 1);
 define('MESSAGE_SIDEBAR_USER_OPTIONS', '');
 define('TAGGING_MAX_TAG_LENGTH', 30);
+define('MESSAGE_MAX_QUOTE_DEPTH', 1);
 define('SEARCH_RESULTS_PER_PAGE', 20);
 define('SEARCH_DEFAULT_SORT_FIELD', 'time');
 define('SEARCH_DEFAULT_SORT_ORDER', 'DESC');
index d916646b5dc66ddd190c41a6c190df7bf59382eb..0a1019cc113979af52485247b650b1b0fd93792b 100644 (file)
@@ -105,6 +105,10 @@ class HtmlInputProcessor extends AbstractHtmlProcessor {
                return $this->getHtmlInputNodeProcessor()->validate();
        }
        
+       public function enforceQuoteDepth($depth) {
+               $this->getHtmlInputNodeProcessor()->enforceQuoteDepth($depth);
+       }
+       
        /**
         * Returns the parsed HTML ready to store.
         * 
index 2f29a6822b51520943405cb5e4c5308a74a2f751..9efc34809812dac1ba1d77ed1af4293dbb7a09d8 100644 (file)
@@ -42,6 +42,11 @@ class HtmlInputNodeProcessor extends AbstractHtmlNodeProcessor {
                
                // handle static converters
                $this->invokeHtmlNode(new HtmlInputNodeWoltlabMetacode());
+               
+               if (MESSAGE_MAX_QUOTE_DEPTH) {
+                       $this->enforceQuoteDepth(MESSAGE_MAX_QUOTE_DEPTH);
+               }
+               
                $this->invokeHtmlNode(new HtmlInputNodeImg());
                
                // dynamic node handlers
@@ -60,6 +65,39 @@ class HtmlInputNodeProcessor extends AbstractHtmlNodeProcessor {
                EventHandler::getInstance()->fireAction($this, 'afterProcess');
        }
        
+       public function enforceQuoteDepth($depth) {
+               $quotes = [];
+               /** @var \DOMElement $quote */
+               foreach ($this->getDocument()->getElementsByTagName('woltlab-quote') as $quote) {
+                       $quotes[] = $quote;
+               }
+               
+               foreach ($quotes as $quote) {
+                       if (!$quote->parentNode) {
+                               continue;
+                       }
+                       
+                       if ($depth === 0) {
+                               DOMUtil::removeNode($quote);
+                       }
+                       else {
+                               $level = 0;
+                               $parent = $quote;
+                               while ($parent = $parent->parentNode) {
+                                       if ($parent->nodeName === 'woltlab-quote') {
+                                               $level++;
+                                       }
+                               }
+                               
+                               if ($level < $depth) {
+                                       continue;
+                               }
+                               
+                               DOMUtil::removeNode($quote);
+                       }
+               }
+       }
+       
        /**
         * Fixes malformed HTML with metacode markers and text being placed
         * outside of paragraphs.
index 1e3137d9763076e47dbc479dc5644e0a0bdd9ad7..a7d9e289b9f976af47f4d83ceebf53d33100d184 100644 (file)
@@ -130,13 +130,11 @@ class MessageQuoteManager extends SingletonFactory {
                        $this->quoteData[$quoteID.'_pID'] = $parentObjectID;
                        
                        if (!empty($fullQuote)) {
-                               // strip quotes container in full quote
                                $htmlInputProcessor = new HtmlInputProcessor();
                                $htmlInputProcessor->processIntermediate($fullQuote);
                                
-                               $elements = $htmlInputProcessor->getHtmlInputNodeProcessor()->getDocument()->getElementsByTagName('woltlab-quote');
-                               while ($elements->length) {
-                                       DOMUtil::removeNode($elements->item(0));
+                               if (MESSAGE_MAX_QUOTE_DEPTH) {
+                                       $htmlInputProcessor->enforceQuoteDepth(MESSAGE_MAX_QUOTE_DEPTH - 1);
                                }
                                
                                $this->quotes[$objectType][$objectID][$quoteID] = 1;
index e95680acaa353c41d9e849d0daa690578da68061..73d7c79bf113f4e72e82f6ffbf6cb008c1fca3a9 100644 (file)
                <item name="wcf.acp.option.module_tagging"><![CDATA[Tagging]]></item>
                <item name="wcf.acp.option.module_tagging.description"><![CDATA[Aktiviert die Funktion für das Taggen von Inhalten.]]></item>
                <item name="wcf.acp.option.tagging_max_tag_length"><![CDATA[Maximale Tag-Länge]]></item>
+               <item name="wcf.acp.option.message_max_quote_depth"><![CDATA[Maximale Zitat-Tiefe]]></item>
+               <item name="wcf.acp.option.message_max_quote_depth.description"><![CDATA[Der Wert „1“ erlaubt nur direkte Zitate, aber keine Zitate in Zitaten. Bei „2“ oder mehr dürfen Zitate selbst Zitate enthalten bis zur angegebenen Tiefe. Mit dem Wert „0“ wird diese Funktion deaktiviert und es können beliebig oft Zitate ineinander verschachtelt werden.]]></item>
                <item name="wcf.acp.option.category.message.search"><![CDATA[Suchfunktion]]></item>
                <item name="wcf.acp.option.search_results_per_page"><![CDATA[Ergebnisse pro Seite]]></item>
                <item name="wcf.acp.option.search_default_sort_field"><![CDATA[Standardsortierung]]></item>
index 2735cb8ab0651372ee27e1dbee455b668a59b7cf..beecc198c4a646db6f337f8593ee638cbf1802dd 100644 (file)
@@ -1092,6 +1092,8 @@ Examples for medium ID detection:
                <item name="wcf.acp.option.module_tagging"><![CDATA[Tags]]></item>
                <item name="wcf.acp.option.module_tagging.description"><![CDATA[Enables the use of tags for content.]]></item>
                <item name="wcf.acp.option.tagging_max_tag_length"><![CDATA[Maximum Tag Length]]></item>
+               <item name="wcf.acp.option.message_max_quote_depth"><![CDATA[Maximum Quote Nesting]]></item>
+               <item name="wcf.acp.option.message_max_quote_depth.description"><![CDATA[The value “1” allows for direct quotes only, quotes contained within quotes will be removed. Setting this to “2” or more allows quotes to be nested up to the configured depth. You can disable this by setting the value to “0”, allowing for infinite quote nesting.]]></item>
                <item name="wcf.acp.option.category.message.search"><![CDATA[Search]]></item>
                <item name="wcf.acp.option.search_results_per_page"><![CDATA[Results per Page]]></item>
                <item name="wcf.acp.option.search_default_sort_field"><![CDATA[Sort by]]></item>