Added comment editing
authorAlexander Ebert <ebert@woltlab.com>
Thu, 2 Mar 2017 16:25:00 +0000 (17:25 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Thu, 2 Mar 2017 16:25:00 +0000 (17:25 +0100)
See #2222

com.woltlab.wcf/templates/__commentJavaScript.tpl
com.woltlab.wcf/templates/commentEditor.tpl [new file with mode: 0644]
com.woltlab.wcf/templates/commentList.tpl
com.woltlab.wcf/templates/commentListAddComment.tpl
com.woltlab.wcf/templates/userProfileCommentList.tpl
wcfsetup/install/files/js/WCF.Comment.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Comment/Add.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Comment/Edit.js [new file with mode: 0644]
wcfsetup/install/files/lib/data/comment/CommentAction.class.php
wcfsetup/install/files/style/ui/comment.scss

index 3e119b978d1310b75873c7f53a8f2c80904b87a9..e8546a49050e6e44c99331309cbb1e98f6672c47 100644 (file)
@@ -9,6 +9,7 @@
                        'wcf.comment.more': '{lang}wcf.comment.more{/lang}',
                        'wcf.comment.response.add': '{lang}wcf.comment.response.add{/lang}',
                        'wcf.comment.response.more': '{lang}wcf.comment.response.more{/lang}',
+                       'wcf.message.error.editorAlreadyInUse': '{lang}wcf.message.error.editorAlreadyInUse{/lang}',
                        'wcf.moderation.report.reportContent': '{lang}wcf.moderation.report.reportContent{/lang}',
                        'wcf.moderation.report.success': '{lang}wcf.moderation.report.success{/lang}'
                });
diff --git a/com.woltlab.wcf/templates/commentEditor.tpl b/com.woltlab.wcf/templates/commentEditor.tpl
new file mode 100644 (file)
index 0000000..96288a0
--- /dev/null
@@ -0,0 +1,17 @@
+{capture assign='wysiwygSelector'}commentEditor{@$comment->commentID}{/capture}
+<textarea id="{$wysiwygSelector}" class="wysiwygTextarea"
+          data-disable-attachments="true"
+          data-disable-media="true"
+          data-support-mention="true"
+>{$comment->message}</textarea>
+{*include file='messageFormTabsInline'*}
+
+<div class="formSubmit">
+       <button class="buttonPrimary" data-type="save">{lang}wcf.global.button.submit{/lang}</button>
+       
+       {include file='messageFormPreviewButton' previewMessageFieldID=$wysiwygSelector previewButtonID=$wysiwygSelector|concat:'_PreviewButton' previewMessageObjectType='com.woltlab.wcf.comment' previewMessageObjectID=$comment->commentID}
+       
+       <button data-type="cancel">{lang}wcf.global.button.cancel{/lang}</button>
+</div>
+
+{include file='wysiwyg'}
index 35693c18b374d4cdf155a941fb24e4333d2dde89..33d18cf9c0813358fc6dcc362d2e792e0fd57e74 100644 (file)
@@ -10,7 +10,7 @@
                                {@$comment->getUserProfile()->getAvatar()->getImageTag(48)}
                        {/if}
                        
-                       <div itemprop="comment" itemscope itemtype="http://schema.org/Comment">
+                       <div class="commentContentContainer" itemprop="comment" itemscope itemtype="http://schema.org/Comment">
                                <div class="commentContent">
                                        <meta itemprop="dateCreated" content="{@$comment->time|date:'c'}">
                                        
index 831c6420a8ca052b65a9a113f76f8afd367d070a..4fc1d2836a4b6dd7239b01bb9c699122bbd87123 100644 (file)
@@ -2,17 +2,17 @@
        {@$__wcf->getUserProfileHandler()->getAvatar()->getImageTag(48)}
        <div class="commentListAddComment collapsed" data-placeholder="{lang}wcf.comment.add{/lang}">
                <div class="commentListAddCommentEditorContainer">
-                       <textarea id="text" name="text" class="wysiwygTextarea"
+                       <textarea id="{$wysiwygSelector}" name="text" class="wysiwygTextarea"
                                  data-disable-attachments="true"
                                  data-disable-media="true"
                                  data-support-mention="true"
                        ></textarea>
-                       {include file='wysiwyg' userProfileCommentList=$commentListContainerID}
+                       {include file='wysiwyg'}
                        
                        <div class="formSubmit">
                                <button class="buttonPrimary" data-type="save" accesskey="s">{lang}wcf.global.button.submit{/lang}</button>
                                
-                               {include file='messageFormPreviewButton' previewMessageObjectType='com.woltlab.wcf.comment' previewMessageObjectID=0}
+                               {include file='messageFormPreviewButton' previewMessageFieldID=$wysiwygSelector previewButtonID=$wysiwygSelector|concat:'_PreviewButton' previewMessageObjectType='com.woltlab.wcf.comment' previewMessageObjectID=0}
                        </div>
                </div>
        </div>
index 3c89728b992935f64b308adb8698ce736514337e..348596067ac9e6dc70c856ef657b2196de801904 100644 (file)
@@ -2,7 +2,7 @@
 
 {if $commentCanAdd}
        <ul id="userProfileCommentList" class="commentList containerList" data-can-add="true" data-object-id="{@$userID}" data-object-type-id="{@$commentObjectTypeID}" data-comments="{@$commentList->countObjects()}" data-last-comment-time="{@$lastCommentTime}">
-               {include file='commentListAddComment' commentListContainerID='userProfileCommentList'}
+               {include file='commentListAddComment' wysiwygSelector='userProfileCommentListAddComment'}
                {include file='commentList'}
        </ul>
 {else}
index fa5b4c472e04c583e750351e2c8d5c09718dc2a7..15440b9a796a1b8d39820cee4624b00107abee2a 100644 (file)
@@ -13,12 +13,6 @@ WCF.Comment = { };
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  */
 WCF.Comment.Handler = Class.extend({
-       /**
-        * input element to add a comment
-        * @var jQuery
-        */
-       _commentAdd: null,
-       
        /**
         * list of comment buttons per comment
         * @var object
@@ -105,7 +99,6 @@ WCF.Comment.Handler = Class.extend({
         * @param       string          userAvatarSmall
         */
        init: function(containerID, userAvatar, userAvatarSmall) {
-               this._commentAdd = null;
                this._commentButtonList = { };
                this._comments = { };
                this._containerID = containerID;
@@ -135,14 +128,16 @@ WCF.Comment.Handler = Class.extend({
                                console.debug("Missing WYSIWYG implementation, adding comments is not available.");
                        }
                        else {
-                               require(['WoltLabSuite/Core/Ui/Comment/Add'], (function (UICommentAdd) {
-                                       new UICommentAdd(elBySel('.jsCommentAdd',  this._container[0]), {
-                                               
-                                       });
+                               require(['WoltLabSuite/Core/Ui/Comment/Add'], (function (UiCommentAdd) {
+                                       new UiCommentAdd(elBySel('.jsCommentAdd',  this._container[0]));
                                }).bind(this));
                        }
                }
                
+               require(['WoltLabSuite/Core/Ui/Comment/Edit'], (function (UiCommentEdit) {
+                       new UiCommentEdit(this._container[0]);
+               }).bind(this));
+               
                WCF.DOMNodeInsertedHandler.execute();
                WCF.DOMNodeInsertedHandler.addCallback('WCF.Comment.Handler', $.proxy(this._domNodeInserted, this));
                
@@ -291,8 +286,8 @@ WCF.Comment.Handler = Class.extend({
                }
                
                if (comment.data('canEdit')) {
-                       var $editButton = $('<li><a href="#" class="jsTooltip" title="' + WCF.Language.get('wcf.global.button.edit') + '"><span class="icon icon16 fa-pencil" /> <span class="invisible">' + WCF.Language.get('wcf.global.button.edit') + '</span></a></li>');
-                       $editButton.data('commentID', commentID).appendTo(comment.find('ul.buttonList:eq(0)')).click($.proxy(this._prepareEdit, this));
+                       var $editButton = $('<li><a href="#" class="jsCommentEditButton jsTooltip" title="' + WCF.Language.get('wcf.global.button.edit') + '"><span class="icon icon16 fa-pencil" /> <span class="invisible">' + WCF.Language.get('wcf.global.button.edit') + '</span></a></li>');
+                       $editButton.appendTo(comment.find('ul.buttonList:eq(0)'));
                }
                
                if (comment.data('canDelete')) {
@@ -366,20 +361,18 @@ WCF.Comment.Handler = Class.extend({
         * @param       boolean         isResponse
         */
        _prepareEdit: function(event, isResponse) {
+               if (!isResponse) {
+                       throw new Error("Editing comments is no longer supported through this method.");
+               }
+               
                event.preventDefault();
                var $button = $(event.currentTarget);
                var $data = {
                        objectID: this._container.data('objectID'),
-                       objectTypeID: this._container.data('objectTypeID')
+                       objectTypeID: this._container.data('objectTypeID'),
+                       responseID: $button.data('responseID')
                };
                
-               if (isResponse === true) {
-                       $data.responseID = $button.data('responseID');
-               }
-               else {
-                       $data.commentID = $button.data('commentID');
-               }
-               
                this._proxy.setOption('data', {
                        actionName: 'prepareEdit',
                        className: 'wcf\\data\\comment\\CommentAction',
index 990678b8a1fd156bb0d1469c4b2d13dfd8a2a6aa..e3e899c1015754a05238a3ec59edb94e673b3644 100644 (file)
@@ -13,22 +13,14 @@ define(['Ajax', 'Core', 'EventHandler', 'Language', 'Dom/ChangeListener', 'Dom/U
        /**
         * @constructor
         */
-       function UiCommentAdd(container, options) { this.init(container, options); }
+       function UiCommentAdd(container) { this.init(container); }
        UiCommentAdd.prototype = {
                /**
                 * Initializes a new quick reply field.
                 * 
                 * @param       {Element}       container       container element
-                * @param       {Object}        options         configuration options
                 */
-               init: function(container, options) {
-                       this._options = Core.extend({
-                               ajax: {
-                                       className: ''
-                               },
-                               successMessage: 'wcf.global.success.add'
-                       }, options);
-                       
+               init: function(container) {
                        this._container = container;
                        this._content = elBySel('.commentListAddComment', this._container);
                        this._textarea = elBySel('.wysiwygTextarea', this._container);
@@ -265,7 +257,7 @@ define(['Ajax', 'Core', 'EventHandler', 'Language', 'Dom/ChangeListener', 'Dom/U
                        //noinspection JSCheckFunctionSignatures
                        DomUtil.insertHtml(data.returnValues.template, this._container, 'after');
                                        
-                       UiNotification.show(Language.get(this._options.successMessage));
+                       UiNotification.show(Language.get('wcf.global.success.add'));
                        
                        DomChangeListener.trigger();
                },
diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Comment/Edit.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Comment/Edit.js
new file mode 100644 (file)
index 0000000..c71f423
--- /dev/null
@@ -0,0 +1,370 @@
+/**
+ * Provides editing support for comments.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2017 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module     WoltLabSuite/Core/Ui/Comment/Edit
+ */
+define(
+       [
+               'Ajax',         'Core',            'Dictionary',          'Environment',
+               'EventHandler', 'Language',        'List',                'Dom/ChangeListener', 'Dom/Traverse',
+               'Dom/Util',     'Ui/Notification', 'Ui/ReusableDropdown', 'WoltLabSuite/Core/Ui/Scroll'
+       ],
+       function(
+               Ajax,            Core,              Dictionary,            Environment,
+               EventHandler,    Language,          List,                  DomChangeListener,    DomTraverse,
+               DomUtil,         UiNotification,    UiReusableDropdown,    UiScroll
+       )
+{
+       "use strict";
+       
+       /**
+        * @constructor
+        */
+       function UiCommentEdit(container) { this.init(container); }
+       UiCommentEdit.prototype = {
+               /**
+                * Initializes the comment edit manager.
+                * 
+                * @param       {Element}       container       container element
+                */
+               init: function(container) {
+                       this._activeElement = null;
+                       this._callbackClick = null;
+                       this._comments = new List();
+                       this._container = container;
+                       this._editorContainer = null;
+                       
+                       this.rebuild();
+                       
+                       DomChangeListener.add('Ui/Comment/Edit_' + DomUtil.identify(this._container), this.rebuild.bind(this));
+               },
+               
+               /**
+                * Initializes each applicable message, should be called whenever new
+                * messages are being displayed.
+                */
+               rebuild: function() {
+                       elBySelAll('.comment', this._container, (function (comment) {
+                               if (this._comments.has(comment)) {
+                                       return;
+                               }
+                               
+                               if (elDataBool(comment, 'can-edit')) {
+                                       var button = elBySel('.jsCommentEditButton', comment);
+                                       if (button !== null) {
+                                               if (this._callbackClick === null) {
+                                                       this._callbackClick = this._click.bind(this);
+                                               }
+                                               
+                                               button.addEventListener(WCF_CLICK_EVENT, this._callbackClick);
+                                       }
+                               }
+                               
+                               this._comments.add(comment);
+                       }).bind(this));
+               },
+               
+               /**
+                * Handles clicks on the edit button.
+                * 
+                * @param       {?Event}        event           event object
+                * @protected
+                */
+               _click: function(event) {
+                       event.preventDefault();
+                       
+                       if (this._activeElement === null) {
+                               this._activeElement = event.currentTarget.closest('.comment');
+                               
+                               this._prepare();
+                               
+                               Ajax.api(this, {
+                                       actionName: 'beginEdit',
+                                       objectIDs: [this._getObjectId(this._activeElement)]
+                               });
+                       }
+                       else {
+                               UiNotification.show('wcf.message.error.editorAlreadyInUse', null, 'warning');
+                       }
+               },
+               
+               /**
+                * Prepares the message for editor display.
+                * 
+                * @protected
+                */
+               _prepare: function() {
+                       this._editorContainer = elCreate('div');
+                       this._editorContainer.className = 'commentEditorContainer';
+                       this._editorContainer.innerHTML = '<span class="icon icon48 fa-spinner"></span>';
+                       
+                       var content = elBySel('.commentContentContainer', this._activeElement);
+                       content.insertBefore(this._editorContainer, content.firstChild);
+               },
+               
+               /**
+                * Shows the message editor.
+                * 
+                * @param       {Object}        data            ajax response data
+                * @protected
+                */
+               _showEditor: function(data) {
+                       var id = this._getEditorId();
+                       
+                       var icon = elBySel('.icon', this._editorContainer);
+                       elRemove(icon);
+                       
+                       var editor = elCreate('div');
+                       editor.className = 'editorContainer';
+                       //noinspection JSUnresolvedVariable
+                       DomUtil.setInnerHtml(editor, data.returnValues.template);
+                       this._editorContainer.appendChild(editor);
+                       
+                       // bind buttons
+                       var formSubmit = elBySel('.formSubmit', editor);
+                       
+                       var buttonSave = elBySel('button[data-type="save"]', formSubmit);
+                       buttonSave.addEventListener(WCF_CLICK_EVENT, this._save.bind(this));
+                       
+                       var buttonCancel = elBySel('button[data-type="cancel"]', formSubmit);
+                       buttonCancel.addEventListener(WCF_CLICK_EVENT, this._restoreMessage.bind(this));
+                       
+                       EventHandler.add('com.woltlab.wcf.redactor', 'submitEditor_' + id, (function(data) {
+                               data.cancel = true;
+                               
+                               this._save();
+                       }).bind(this));
+                       
+                       var editorElement = elById(id);
+                       if (Environment.editor() === 'redactor') {
+                               window.setTimeout((function() {
+                                       UiScroll.element(this._activeElement);
+                               }).bind(this), 250);
+                       }
+                       else {
+                               editorElement.focus();
+                       }
+               },
+               
+               /**
+                * Restores the message view.
+                * 
+                * @protected
+                */
+               _restoreMessage: function() {
+                       this._destroyEditor();
+                       
+                       elRemove(this._editorContainer);
+                       
+                       this._activeElement = null;
+               },
+               
+               /**
+                * Saves the editor message.
+                * 
+                * @protected
+                */
+               _save: function() {
+                       var parameters = {
+                               data: {
+                                       message: ''
+                               }
+                       };
+                       
+                       var id = this._getEditorId();
+                       
+                       EventHandler.fire('com.woltlab.wcf.redactor2', 'getText_' + id, parameters.data);
+                       
+                       if (!this._validate(parameters)) {
+                               // validation failed
+                               return;
+                       }
+                       
+                       EventHandler.fire('com.woltlab.wcf.redactor2', 'submit_' + id, parameters);
+                       
+                       Ajax.api(this, {
+                               actionName: 'save',
+                               objectIDs: [this._getObjectId(this._activeElement)],
+                               parameters: parameters
+                       });
+                       
+                       this._hideEditor();
+               },
+               
+               /**
+                * Validates the message and invokes listeners to perform additional validation.
+                *
+                * @param       {Object}        parameters      request parameters
+                * @return      {boolean}       validation result
+                * @protected
+                */
+               _validate: function(parameters) {
+                       // remove all existing error elements
+                       var errorMessages = elByClass('innerError', this._activeElement);
+                       while (errorMessages.length) {
+                               elRemove(errorMessages[0]);
+                       }
+                       
+                       var data = {
+                               api: this,
+                               parameters: parameters,
+                               valid: true
+                       };
+                       
+                       EventHandler.fire('com.woltlab.wcf.redactor2', 'validate_' + this._getEditorId(), data);
+                       
+                       return (data.valid !== false);
+               },
+               
+               /**
+                * Throws an error by adding an inline error to target element.
+                *
+                * @param       {Element}       element         erroneous element
+                * @param       {string}        message         error message
+                */
+               throwError: function(element, message) {
+                       var error = elCreate('small');
+                       error.className = 'innerError';
+                       error.textContent = message;
+                       
+                       DomUtil.insertAfter(error, element);
+               },
+               
+               /**
+                * Shows the update message.
+                * 
+                * @param       {Object}        data            ajax response data
+                * @protected
+                */
+               _showMessage: function(data) {
+                       // set new content
+                       //noinspection JSCheckFunctionSignatures
+                       DomUtil.setInnerHtml(elBySel('.commentContent .userMessage', this._editorContainer.parentNode), data.returnValues.message);
+                       
+                       this._restoreMessage();
+                       
+                       UiNotification.show();
+               },
+               
+               /**
+                * Hides the editor from view.
+                * 
+                * @protected
+                */
+               _hideEditor: function() {
+                       elHide(elBySel('.editorContainer', this._editorContainer));
+                       
+                       var icon = elCreate('span');
+                       icon.className = 'icon icon48 fa-spinner';
+                       this._editorContainer.appendChild(icon);
+               },
+               
+               /**
+                * Restores the previously hidden editor.
+                * 
+                * @protected
+                */
+               _restoreEditor: function() {
+                       var icon = elBySel('.fa-spinner', this._editorContainer);
+                       elRemove(icon);
+                       
+                       var editorContainer = elBySel('.editorContainer', this._editorContainer);
+                       if (editorContainer !== null) elShow(editorContainer);
+               },
+               
+               /**
+                * Destroys the editor instance.
+                * 
+                * @protected
+                */
+               _destroyEditor: function() {
+                       EventHandler.fire('com.woltlab.wcf.redactor2', 'autosaveDestroy_' + this._getEditorId());
+                       EventHandler.fire('com.woltlab.wcf.redactor2', 'destroy_' + this._getEditorId());
+               },
+               
+               /**
+                * Returns the unique editor id.
+                * 
+                * @return      {string}        editor id
+                * @protected
+                */
+               _getEditorId: function() {
+                       return 'commentEditor' + this._getObjectId(this._activeElement);
+               },
+               
+               /**
+                * Returns the element's `data-object-id` value.
+                * 
+                * @param       {Element}       element         target element
+                * @return      {int}
+                * @protected
+                */
+               _getObjectId: function(element) {
+                       return ~~elData(element, 'object-id');
+               },
+               
+               _ajaxFailure: function(data) {
+                       var editor = elBySel('.redactor-layer', this._editorContainer);
+                       
+                       // handle errors occurring on editor load
+                       if (editor === null) {
+                               this._restoreMessage();
+                               
+                               return true;
+                       }
+                       
+                       this._restoreEditor();
+                       
+                       //noinspection JSUnresolvedVariable
+                       if (!data || data.returnValues === undefined || data.returnValues.errorType === undefined) {
+                               return true;
+                       }
+                       
+                       var innerError = elBySel('.innerError', this._editorContainer);
+                       if (innerError === null) {
+                               innerError = elCreate('small');
+                               innerError.className = 'innerError';
+                               
+                               DomUtil.insertAfter(innerError, editor);
+                       }
+                       
+                       //noinspection JSUnresolvedVariable
+                       innerError.textContent = data.returnValues.errorType;
+                       
+                       return false;
+               },
+               
+               _ajaxSuccess: function(data) {
+                       switch (data.actionName) {
+                               case 'beginEdit':
+                                       this._showEditor(data);
+                                       break;
+                                       
+                               case 'save':
+                                       this._showMessage(data);
+                                       break;
+                       }
+               },
+               
+               _ajaxSetup: function() {
+                       var objectTypeId = ~~elData(this._container, 'object-type-id');
+                       
+                       return {
+                               data: {
+                                       className: 'wcf\\data\\comment\\CommentAction',
+                                       parameters: {
+                                               data: {
+                                                       objectTypeID: objectTypeId
+                                               }
+                                       }
+                               },
+                               silent: true
+                       };
+               }
+       };
+       
+       return UiCommentEdit;
+});
index afc33d0940f05356d7475db319d9885ffa568100..dc116785be0192f9f5d981be9267a315b2053b54 100644 (file)
@@ -251,7 +251,8 @@ class CommentAction extends AbstractDatabaseObjectAction {
                        'username' => WCF::getUser()->userID ? WCF::getUser()->username : $this->parameters['data']['username'],
                        'message' => $htmlInputProcessor->getHtml(),
                        'responses' => 0,
-                       'responseIDs' => serialize([])
+                       'responseIDs' => serialize([]),
+                       'enableHtml' => 1
                ]);
                
                // update counter
@@ -480,17 +481,12 @@ class CommentAction extends AbstractDatabaseObjectAction {
         * Validates parameters to edit a comment or a response.
         */
        public function validatePrepareEdit() {
-               // validate comment id or response id
+               // validate response id
                try {
-                       $this->validateCommentID();
+                       $this->validateResponseID();
                }
                catch (UserInputException $e) {
-                       try {
-                               $this->validateResponseID();
-                       }
-                       catch (UserInputException $e) {
-                               throw new UserInputException('objectIDs');
-                       }
+                       throw new UserInputException('objectIDs');
                }
                
                // validate object type id
@@ -498,15 +494,8 @@ class CommentAction extends AbstractDatabaseObjectAction {
                
                // validate object id and permissions
                $this->commentProcessor = $objectType->getProcessor();
-               if ($this->comment !== null) {
-                       if (!$this->commentProcessor->canEditComment($this->comment)) {
-                               throw new PermissionDeniedException();
-                       }
-               }
-               else {
-                       if (!$this->commentProcessor->canEditResponse($this->response)) {
-                               throw new PermissionDeniedException();
-                       }
+               if (!$this->commentProcessor->canEditResponse($this->response)) {
+                       throw new PermissionDeniedException();
                }
        }
        
@@ -516,25 +505,11 @@ class CommentAction extends AbstractDatabaseObjectAction {
         * @return      array
         */
        public function prepareEdit() {
-               if ($this->comment !== null) {
-                       $message = $this->comment->message;
-               }
-               else {
-                       $message = $this->response->message;
-               }
-               
-               $returnValues = [
+               return [
                        'action' => 'prepare',
-                       'message' => $message
+                       'message' => $this->response->message,
+                       'responseID' => $this->response->responseID
                ];
-               if ($this->comment !== null) {
-                       $returnValues['commentID'] = $this->comment->commentID;
-               }
-               else {
-                       $returnValues['responseID'] = $this->response->responseID;
-               }
-               
-               return $returnValues;
        }
        
        /**
@@ -552,28 +527,65 @@ class CommentAction extends AbstractDatabaseObjectAction {
         * @return      array
         */
        public function edit() {
-               $returnValues = ['action' => 'saved'];
+               $editor = new CommentResponseEditor($this->response);
+               $editor->update([
+                       'message' => $this->parameters['data']['message']
+               ]);
+               $response = new CommentResponse($this->response->responseID);
                
-               if ($this->response === null) {
-                       $editor = new CommentEditor($this->comment);
-                       $editor->update([
-                               'message' => $this->parameters['data']['message']
-                       ]);
-                       $comment = new Comment($this->comment->commentID);
-                       $returnValues['commentID'] = $this->comment->commentID;
-                       $returnValues['message'] = $comment->getFormattedMessage();
-               }
-               else {
-                       $editor = new CommentResponseEditor($this->response);
-                       $editor->update([
-                               'message' => $this->parameters['data']['message']
-                       ]);
-                       $response = new CommentResponse($this->response->responseID);
-                       $returnValues['responseID'] = $this->response->responseID;
-                       $returnValues['message'] = $response->getFormattedMessage();
+               return [
+                       'action' => 'saved',
+                       'message' => $response->getFormattedMessage(),
+                       'responseID' => $this->response->responseID
+               ];
+       }
+       
+       public function validateBeginEdit() {
+               $this->comment = $this->getSingleObject();
+               
+               // validate object type id
+               $objectType = $this->validateObjectType();
+               
+               // validate object id and permissions
+               $this->commentProcessor = $objectType->getProcessor();
+               if (!$this->commentProcessor->canEditComment($this->comment->getDecoratedObject())) {
+                       throw new PermissionDeniedException();
                }
+       }
+       
+       public function beginEdit() {
+               WCF::getTPL()->assign([
+                       'comment' => $this->comment,
+                       'wysiwygSelector' => 'commentEditor'.$this->comment->commentID
+               ]);
                
-               return $returnValues;
+               return [
+                       'actionName' => 'beginEdit',
+                       'template' => WCF::getTPL()->fetch('commentEditor', 'wcf')
+               ];
+       }
+       
+       public function validateSave() {
+               $this->validateBeginEdit();
+               
+               $this->validateMessage(true);
+       }
+       
+       public function save() {
+               /** @var HtmlInputProcessor $htmlInputProcessor */
+               $htmlInputProcessor = $this->parameters['htmlInputProcessor'];
+               
+               $action = new CommentAction([$this->comment], 'update', [
+                       'data' => [
+                               'message' => $htmlInputProcessor->getHtml()
+                       ]
+               ]);
+               $action->executeAction();
+               
+               return [
+                       'actionName' => 'save',
+                       'message' => (new Comment($this->comment->commentID))->getFormattedMessage()
+               ];
        }
        
        /**
@@ -726,6 +738,9 @@ class CommentAction extends AbstractDatabaseObjectAction {
        
        /**
         * Validates message parameter.
+        * 
+        * @param       bool  $isComment
+        * @throws      UserInputException
         */
        protected function validateMessage($isComment = false) {
                $this->readString('message', false, 'data');
index 8be172dfcb3bcb8461423199465d731d04b3c8f4..2bee7467ca7d1e321aeb0b56c6626c3ac079080d 100644 (file)
                }
        }
 }
+
+.commentEditorContainer {
+       > .icon {
+               left: calc(50% - 24px);
+               position: relative;
+       }
+       
+       ~ .commentContent,
+       ~ .commentOptionContainer {
+               display: none;
+       }
+}
+
+.commentListAddComment,
+.commentEditorContainer {
+       .formSubmit {
+               /* reduced margin, now in sync with the container padding */
+               margin-top: 20px;
+       }
+}