Overhauled message preview
authorAlexander Ebert <ebert@woltlab.com>
Thu, 28 Jul 2016 09:04:29 +0000 (11:04 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Thu, 28 Jul 2016 09:04:35 +0000 (11:04 +0200)
com.woltlab.wcf/templates/messageFormPreviewButton.tpl
wcfsetup/install/files/js/WCF.Message.js
wcfsetup/install/files/js/WoltLab/WCF/Ui/Message/Reply.js
wcfsetup/install/files/lib/data/bbcode/MessagePreviewAction.class.php
wcfsetup/install/files/lib/system/html/output/HtmlOutputProcessor.class.php
wcfsetup/install/files/lib/system/message/embedded/object/MessageEmbeddedObjectManager.class.php

index cd1bc984a429f8dff2a7bba75748c2604bb8a94e..d9c7eadd820100b4a9b9d5e8a886ace3b7213143 100644 (file)
@@ -1,4 +1,9 @@
-<button id="previewButton" class="jsOnly" accesskey="p">{lang}wcf.global.button.preview{/lang}</button>
+{if !$previewMessageFieldID|isset}{assign var=previewMessageFieldID value='text'}{/if}
+{if !$previewButtonID|isset}{assign var=previewButtonID value='buttonMessagePreview'}{/if}
+{if !$previewMessageObjectType|isset}{assign var=previewMessageObjectType value=''}{/if}
+{if !$previewMessageObjectID|isset}{assign var=previewMessageObjectID value=0}{/if}
+
+<button id="{$previewButtonID}" class="jsOnly">{lang}wcf.global.button.preview{/lang}</button>
 
 <script data-relocate="true">
        //<![CDATA[
@@ -7,7 +12,12 @@
                        'wcf.global.preview': '{lang}wcf.global.preview{/lang}' 
                });
                
-               new WCF.Message.DefaultPreview({if MODULE_ATTACHMENT && $attachmentHandler !== null}'{@$attachmentObjectType}', '{@$attachmentObjectID}', '{$tmpHash|encodeJS}'{/if});
+               new WCF.Message.DefaultPreview({
+                       messageFieldID: '{$previewMessageFieldID}',
+                       previewButtonID: '{$previewButtonID}',
+                       messageObjectType: '{$previewMessageObjectType}',
+                       messageObjectID: '{$previewMessageObjectID}'
+               });
        });
        //]]>
 </script>
\ No newline at end of file
index 76777380cbf6859846c3f6b908a712c98ddae0a9..c225b6f09f11ef4c1cdeb61fa2f37b70c10545f6 100644 (file)
@@ -388,14 +388,7 @@ WCF.Message.Preview = Class.extend({
         * @return      string
         */
        _getMessage: function() {
-               if (!$.browser.redactor) {
-                       return $.trim(this._textarea.val());
-               }
-               else if (this._textarea.data('redactor')) {
-                       return this._textarea.redactor('wutil.getText');
-               }
-               
-               return null;
+               return this._textarea.redactor('code.get');
        },
        
        /**
@@ -456,33 +449,39 @@ WCF.Message.Preview = Class.extend({
  * @see        WCF.Message.Preview
  */
 WCF.Message.DefaultPreview = WCF.Message.Preview.extend({
-       _attachmentObjectType: null,
-       _attachmentObjectID: null,
-       _tmpHash: null,
+       _dialog: null,
+       _options: {},
        
        /**
         * @see WCF.Message.Preview.init()
         */
-       init: function(attachmentObjectType, attachmentObjectID, tmpHash) {
-               this._super('wcf\\data\\bbcode\\MessagePreviewAction', 'text', 'previewButton');
+       init: function(options) {
+               if (arguments.length > 1 && typeof options === 'string') {
+                       throw new Error("Outdated API call, please update your implementation.");
+               }
+               
+               this._options = $.extend({
+                       disallowedBBCodesPermission: 'user.message.disallowedBBCodes',
+                       messageFieldID: '',
+                       previewButtonID: '',
+                       messageObjectType: '',
+                       messageObjectID: 0
+               }, options);
                
-               this._attachmentObjectType = attachmentObjectType || null;
-               this._attachmentObjectID = attachmentObjectID || null;
-               this._tmpHash = tmpHash || null;
+               if (!this._options.messageObjectType) {
+                       throw new Error("Field 'messageObjectType' cannot be empty.");
+               }
+               
+               this._super('wcf\\data\\bbcode\\MessagePreviewAction', this._options.messageFieldID, this._options.previewButtonID);
        },
        
        /**
         * @see WCF.Message.Preview._handleResponse()
         */
        _handleResponse: function(data) {
-               var $preview = $('#previewContainer');
-               if (!$preview.length) {
-                       $preview = $('<section class="section" id="previewContainer"><h2 class="sectionTitle">' + WCF.Language.get('wcf.global.preview') + '</h2><div class="messageTextPreview"></div></section>').prependTo($('#messageContainer')).wcfFadeIn();
-               }
-               
-               $preview.find('div:eq(0)').html(data.returnValues.message);
-               
-               new WCF.Effect.Scroll().scrollTo($preview);
+               require(['WoltLab/WCF/Ui/Dialog'], (function(UiDialog) {
+                       UiDialog.open(this, '<div class="htmlContent">' + data.returnValues.message + '</div>');
+               }).bind(this));
        },
        
        /**
@@ -491,13 +490,23 @@ WCF.Message.DefaultPreview = WCF.Message.Preview.extend({
        _getParameters: function(message) {
                var $parameters = this._super(message);
                
-               if (this._attachmentObjectType != null) {
-                       $parameters.attachmentObjectType = this._attachmentObjectType;
-                       $parameters.attachmentObjectID = this._attachmentObjectID;
-                       $parameters.tmpHash = this._tmpHash;
+               for (var key in this._options) {
+                       if (this._options.hasOwnProperty(key) && key !== 'messageFieldID' && key !== 'previewButtonID') {
+                               $parameters[key] = this._options[key];
+                       }
                }
                
                return $parameters;
+       },
+       
+       _dialogSetup: function() {
+               return {
+                       id: 'messagePreview',
+                       options: {
+                               title: WCF.Language.get('wcf.global.preview')
+                       },
+                       source: null
+               }
        }
 });
 
index 72cae533a037a67eddf8d3d9ba9010d9c9fda40a..06f98c6d2fe0424e2577ad1a63f8f51ba1ed6a7a 100644 (file)
@@ -6,7 +6,7 @@
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @module     WoltLab/WCF/Ui/Message/Reply
  */
-define(['Ajax', 'Core', 'EventHandler', 'Language', 'Dom/ChangeListener', 'Dom/Util', 'Dom/Traverse', 'Ui/Dialog', 'Ui/Notification', '../Scroll', 'EventKey', 'User', 'WoltLab/WCF/Controller/Captcha'],
+define(['Ajax', 'Core', 'EventHandler', 'Language', 'Dom/ChangeListener', 'Dom/Util', 'Dom/Traverse', 'Ui/Dialog', 'Ui/Notification', 'WoltLab/WCF/Ui/Scroll', 'EventKey', 'User', 'WoltLab/WCF/Controller/Captcha'],
        function(Ajax, Core, EventHandler, Language, DomChangeListener, DomUtil, DomTraverse, UiDialog, UiNotification, UiScroll, EventKey, User, ControllerCaptcha) {
        "use strict";
        
@@ -54,8 +54,6 @@ define(['Ajax', 'Core', 'EventHandler', 'Language', 'Dom/ChangeListener', 'Dom/U
                                        }).bind(this));
                                }).bind(this));
                        }
-                       
-                       // TODO: add event listener for submit through keyboard in Redactor
                },
                
                /**
@@ -74,7 +72,7 @@ define(['Ajax', 'Core', 'EventHandler', 'Language', 'Dom/ChangeListener', 'Dom/U
                        if (usernameInput.value === '') {
                                var error = DomTraverse.nextByClass(usernameInput, 'innerError');
                                if (!error) {
-                                       var error = elCreate('small');
+                                       error = elCreate('small');
                                        error.className = 'innerError';
                                        error.innerText = Language.get('wcf.global.form.error.empty');
                                        
@@ -94,6 +92,7 @@ define(['Ajax', 'Core', 'EventHandler', 'Language', 'Dom/ChangeListener', 'Dom/U
                                }
                        };
                        
+                       //noinspection JSCheckFunctionSignatures
                        var captchaId = elData(event.currentTarget, 'captcha-id');
                        if (ControllerCaptcha.has(captchaId)) {
                                parameters = Core.extend(parameters, ControllerCaptcha.getData(captchaId));
@@ -306,6 +305,10 @@ define(['Ajax', 'Core', 'EventHandler', 'Language', 'Dom/ChangeListener', 'Dom/U
                        }
                },
                
+               /**
+                * @param {{returnValues:{guestDialog:string,guestDialogID:string}}} data
+                * @protected
+                */
                _ajaxSuccess: function(data) {
                        if (!User.userId && !data.returnValues.guestDialogID) {
                                throw new Error("Missing 'guestDialogID' return value for guest.");
index 6e0ba8551b3f4c057ed703c27cc2b24e47b125cb..191cd1aa8304798f09f3377cc2be8cb7bea222df 100644 (file)
@@ -1,17 +1,17 @@
 <?php
 namespace wcf\data\bbcode;
-use wcf\system\bbcode\MessageParser;
-use wcf\system\bbcode\PreParser;
+use wcf\system\bbcode\BBCodeHandler;
 use wcf\system\exception\UserInputException;
+use wcf\system\html\input\HtmlInputProcessor;
+use wcf\system\html\output\HtmlOutputProcessor;
 use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
 use wcf\system\WCF;
 use wcf\util\ArrayUtil;
-use wcf\util\StringUtil;
 
 /**
  * Provides a default message preview action.
  * 
- * @author     Marcel Werk
+ * @author     Alexander Ebert
  * @copyright  2001-2016 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package    WoltLabSuite\Core\Data\Message
@@ -26,13 +26,9 @@ class MessagePreviewAction extends BBCodeAction {
         * Validates parameters for message preview.
         */
        public function validateGetMessagePreview() {
-               if (!isset($this->parameters['data']['message'])) {
-                       throw new UserInputException('message');
-               }
-               
-               if (!isset($this->parameters['options'])) {
-                       throw new UserInputException('options');
-               }
+               $this->readString('message', false, 'data');
+               $this->readString('messageObjectType');
+               $this->readInteger('messageObjectID', true);
        }
        
        /**
@@ -42,22 +38,14 @@ class MessagePreviewAction extends BBCodeAction {
         * @throws      UserInputException
         */
        public function getMessagePreview() {
-               // get options
-               $enableBBCodes = (isset($this->parameters['options']['enableBBCodes'])) ? 1 : 0;
-               $enableHtml = (isset($this->parameters['options']['enableHtml'])) ? 1 : 0;
-               $enableSmilies = (isset($this->parameters['options']['enableSmilies'])) ? 1 : 0;
-               $preParse = (isset($this->parameters['options']['preParse'])) ? 1 : 0;
-               
-               $allowedBBCodesPermission = (isset($this->parameters['allowedBBCodesPermission'])) ? $this->parameters['allowedBBCodesPermission'] : 'user.message.allowedBBCodes';
-               
-               // validate permissions for options
-               if ($enableBBCodes && !WCF::getSession()->getPermission('user.message.canUseBBCodes')) $enableBBCodes = 0;
-               if ($enableHtml && !WCF::getSession()->getPermission('user.message.canUseHtml')) $enableHtml = 0;
-               if ($enableSmilies && !WCF::getSession()->getPermission('user.message.canUseSmilies')) $enableSmilies = 0;
+               $htmlInputProcessor = new HtmlInputProcessor();
+               $htmlInputProcessor->process($this->parameters['data']['message'], $this->parameters['messageObjectType'], $this->parameters['messageObjectID']);
                
                // check if disallowed bbcode are used
-               if ($enableBBCodes && $allowedBBCodesPermission) {
-                       $disallowedBBCodes = MessageParser::getInstance()->validateBBCodes($this->parameters['data']['message'], ArrayUtil::trim(explode(',', WCF::getSession()->getPermission($allowedBBCodesPermission))));
+               $disallowedBBCodesPermission = (isset($this->parameters['disallowedBBCodesPermission'])) ? $this->parameters['disallowedBBCodesPermission'] : 'user.message.disallowedBBCodes';
+               if ($disallowedBBCodesPermission) {
+                       BBCodeHandler::getInstance()->setDisallowedBBCodes(ArrayUtil::trim(explode(',', WCF::getSession()->getPermission($disallowedBBCodesPermission))));
+                       $disallowedBBCodes = $htmlInputProcessor->validate();
                        if (!empty($disallowedBBCodes)) {
                                throw new UserInputException('message', WCF::getLanguage()->getDynamicVariable('wcf.message.error.disallowedBBCodes', [
                                        'disallowedBBCodes' => $disallowedBBCodes
@@ -65,27 +53,14 @@ class MessagePreviewAction extends BBCodeAction {
                        }
                }
                
-               // get message
-               $message = StringUtil::trim($this->parameters['data']['message']);
-               
-               // get embedded objects
-               MessageEmbeddedObjectManager::getInstance()->parseTemporaryMessage($message);
-               
-               // parse URLs
-               if ($preParse && $enableBBCodes) {
-                       if ($allowedBBCodesPermission) {
-                               $message = PreParser::getInstance()->parse($message, ArrayUtil::trim(explode(',', WCF::getSession()->getPermission($allowedBBCodesPermission))));
-                       }
-                       else {
-                               $message = PreParser::getInstance()->parse($message);
-                       }
-               }
+               MessageEmbeddedObjectManager::getInstance()->registerTemporaryMessage($htmlInputProcessor);
                
-               // parse message
-               $preview = MessageParser::getInstance()->parse($message, $enableSmilies, $enableHtml, $enableBBCodes, false);
+               $htmlOutputProcessor = new HtmlOutputProcessor();
+               $htmlOutputProcessor->process($htmlInputProcessor->getHtml(), $this->parameters['messageObjectType'], $this->parameters['messageObjectID']);
                
                return [
-                       'message' => $preview
+                       'message' => $htmlOutputProcessor->getHtml(),
+                       'raw' => $htmlInputProcessor->getHtml()
                ];
        }
 }
index 78e1598d164cd5e63d7debc39db5fe41ba983188..6e28572b9f6d75cc8a9f3bd95faf288b064389df 100644 (file)
@@ -67,10 +67,6 @@ class HtmlOutputProcessor extends AbstractHtmlProcessor {
         * @throws \InvalidArgumentException
         */
        public function setContext($objectType, $objectID) {
-               if (!$objectID) {
-                       throw new \InvalidArgumentException("Output processor requires a valid objectID.");
-               }
-               
                parent::setContext($objectType, $objectID);
                
                MessageEmbeddedObjectManager::getInstance()->setActiveMessage($objectType, $objectID);
index f17d4ef64c957a82552421ef2e9c8456c65291c0..c00c57bb7e9dd0fc11123f153f85ce86c19f3730 100644 (file)
@@ -272,6 +272,34 @@ class MessageEmbeddedObjectManager extends SingletonFactory {
                }
        }
        
+       /**
+        * Temporarily registers a message, the parsed data will not be stored.
+        * 
+        * @param       HtmlInputProcessor      $htmlInputProcessor     html input processor
+        */
+       public function registerTemporaryMessage(HtmlInputProcessor $htmlInputProcessor) {
+               $context = $htmlInputProcessor->getContext();
+               
+               // set active message information
+               $this->activeMessageObjectTypeID = $context['objectTypeID'];
+               $this->activeMessageID = $context['objectID'];
+               
+               $embeddedData = $htmlInputProcessor->getEmbeddedContent();
+               
+               /** @var IMessageEmbeddedObjectHandler $handler */
+               foreach ($this->getEmbeddedObjectHandlers() as $handler) {
+                       $objectIDs = $handler->parse($htmlInputProcessor, $embeddedData);
+                       
+                       if (!empty($objectIDs)) {
+                               // save assignments
+                               $this->messageEmbeddedObjects[$this->activeMessageObjectTypeID][$this->activeMessageID][$handler->objectTypeID] = $objectIDs;
+                               
+                               // loads objects
+                               $this->embeddedObjects[$handler->objectTypeID] = $handler->loadObjects($objectIDs);
+                       }
+               }
+       }
+       
        /**
         * @return      ISimpleMessageEmbeddedObjectHandler[];
         */