Merge branch 'Abijeet-master' to convert comment system to vue
This commit is contained in:
		
						commit
						e3f2bde26d
					
				| 
						 | 
				
			
			@ -144,226 +144,4 @@ module.exports = function (ngApp, events) {
 | 
			
		|||
        };
 | 
			
		||||
 | 
			
		||||
    }]);
 | 
			
		||||
 | 
			
		||||
    // Controller used to reply to and add new comments
 | 
			
		||||
    ngApp.controller('CommentReplyController', ['$scope', '$http', '$timeout', function ($scope, $http, $timeout) {
 | 
			
		||||
        const MarkdownIt = require("markdown-it");
 | 
			
		||||
        const md = new MarkdownIt({html: true});
 | 
			
		||||
        let vm = this;
 | 
			
		||||
 | 
			
		||||
        vm.saveComment = function () {
 | 
			
		||||
            let pageId = $scope.comment.pageId || $scope.pageId;
 | 
			
		||||
            let comment = $scope.comment.text;
 | 
			
		||||
            if (!comment) {
 | 
			
		||||
                return events.emit('warning', trans('errors.empty_comment'));
 | 
			
		||||
            }
 | 
			
		||||
            let commentHTML = md.render($scope.comment.text);
 | 
			
		||||
            let serviceUrl = `/ajax/page/${pageId}/comment/`;
 | 
			
		||||
            let httpMethod = 'post';
 | 
			
		||||
            let reqObj = {
 | 
			
		||||
                text: comment,
 | 
			
		||||
                html: commentHTML
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            if ($scope.isEdit === true) {
 | 
			
		||||
                // this will be set when editing the comment.
 | 
			
		||||
                serviceUrl = `/ajax/page/${pageId}/comment/${$scope.comment.id}`;
 | 
			
		||||
                httpMethod = 'put';
 | 
			
		||||
            } else if ($scope.isReply === true) {
 | 
			
		||||
                // if its reply, get the parent comment id
 | 
			
		||||
                reqObj.parent_id = $scope.parentId;
 | 
			
		||||
            }
 | 
			
		||||
            $http[httpMethod](window.baseUrl(serviceUrl), reqObj).then(resp => {
 | 
			
		||||
                if (!isCommentOpSuccess(resp)) {
 | 
			
		||||
                     return;
 | 
			
		||||
                }
 | 
			
		||||
                // hide the comments first, and then retrigger the refresh
 | 
			
		||||
                if ($scope.isEdit) {
 | 
			
		||||
                    updateComment($scope.comment, resp.data);
 | 
			
		||||
                    $scope.$emit('evt.comment-success', $scope.comment.id);
 | 
			
		||||
                } else {
 | 
			
		||||
                    $scope.comment.text = '';
 | 
			
		||||
                    if ($scope.isReply === true && $scope.parent.sub_comments) {
 | 
			
		||||
                        $scope.parent.sub_comments.push(resp.data.comment);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $scope.$emit('evt.new-comment', resp.data.comment);
 | 
			
		||||
                    }
 | 
			
		||||
                    $scope.$emit('evt.comment-success', null, true);
 | 
			
		||||
                }
 | 
			
		||||
                $scope.comment.is_hidden = true;
 | 
			
		||||
                $timeout(function() {
 | 
			
		||||
                    $scope.comment.is_hidden = false;
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                events.emit('success', trans(resp.data.message));
 | 
			
		||||
 | 
			
		||||
            }, checkError);
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        function checkError(response) {
 | 
			
		||||
            let msg = null;
 | 
			
		||||
            if (isCommentOpSuccess(response)) {
 | 
			
		||||
                // all good
 | 
			
		||||
                return;
 | 
			
		||||
            } else if (response.data) {
 | 
			
		||||
                msg = response.data.message;
 | 
			
		||||
            } else {
 | 
			
		||||
                msg = trans('errors.comment_add');
 | 
			
		||||
            }
 | 
			
		||||
            if (msg) {
 | 
			
		||||
                events.emit('success', msg);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }]);
 | 
			
		||||
 | 
			
		||||
    // Controller used to delete comments
 | 
			
		||||
    ngApp.controller('CommentDeleteController', ['$scope', '$http', '$timeout', function ($scope, $http, $timeout) {
 | 
			
		||||
        let vm = this;
 | 
			
		||||
 | 
			
		||||
        vm.delete = function(comment) {
 | 
			
		||||
            $http.delete(window.baseUrl(`/ajax/comment/${comment.id}`)).then(resp => {
 | 
			
		||||
                if (!isCommentOpSuccess(resp)) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                updateComment(comment, resp.data, $timeout, true);
 | 
			
		||||
            }, function (resp) {
 | 
			
		||||
                if (isCommentOpSuccess(resp)) {
 | 
			
		||||
                    events.emit('success', trans('entities.comment_deleted'));
 | 
			
		||||
                } else {
 | 
			
		||||
                    events.emit('error', trans('error.comment_delete'));
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        };
 | 
			
		||||
    }]);
 | 
			
		||||
 | 
			
		||||
    // Controller used to fetch all comments for a page
 | 
			
		||||
    ngApp.controller('CommentListController', ['$scope', '$http', '$timeout', '$location', function ($scope, $http, $timeout, $location) {
 | 
			
		||||
        let vm = this;
 | 
			
		||||
        $scope.errors = {};
 | 
			
		||||
        // keep track of comment levels
 | 
			
		||||
        $scope.level = 1;
 | 
			
		||||
        vm.totalCommentsStr = trans('entities.comments_loading');
 | 
			
		||||
        vm.permissions = {};
 | 
			
		||||
        vm.trans = window.trans;
 | 
			
		||||
 | 
			
		||||
        $scope.$on('evt.new-comment', function (event, comment) {
 | 
			
		||||
            // add the comment to the comment list.
 | 
			
		||||
            vm.comments.push(comment);
 | 
			
		||||
            ++vm.totalComments;
 | 
			
		||||
            setTotalCommentMsg();
 | 
			
		||||
            event.stopPropagation();
 | 
			
		||||
            event.preventDefault();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        vm.canEditDelete = function (comment, prop) {
 | 
			
		||||
            if (!comment.active) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            let propAll = prop + '_all';
 | 
			
		||||
            let propOwn = prop + '_own';
 | 
			
		||||
 | 
			
		||||
            if (vm.permissions[propAll]) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (vm.permissions[propOwn] && comment.created_by.id === vm.current_user_id) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        vm.canComment = function () {
 | 
			
		||||
            return vm.permissions.comment_create;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // check if there are is any direct linking
 | 
			
		||||
        let linkedCommentId = $location.search().cm;
 | 
			
		||||
 | 
			
		||||
        $timeout(function() {
 | 
			
		||||
            $http.get(window.baseUrl(`/ajax/page/${$scope.pageId}/comments/`)).then(resp => {
 | 
			
		||||
                if (!isCommentOpSuccess(resp)) {
 | 
			
		||||
                    // just show that no comments are available.
 | 
			
		||||
                    vm.totalComments = 0;
 | 
			
		||||
                    setTotalCommentMsg();
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                vm.comments = resp.data.comments;
 | 
			
		||||
                vm.totalComments = +resp.data.total;
 | 
			
		||||
                vm.permissions = resp.data.permissions;
 | 
			
		||||
                vm.current_user_id = resp.data.user_id;
 | 
			
		||||
                setTotalCommentMsg();
 | 
			
		||||
                if (!linkedCommentId) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                $timeout(function() {
 | 
			
		||||
                    // wait for the UI to render.
 | 
			
		||||
                    focusLinkedComment(linkedCommentId);
 | 
			
		||||
                });
 | 
			
		||||
            }, checkError);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        function setTotalCommentMsg () {
 | 
			
		||||
            if (vm.totalComments === 0) {
 | 
			
		||||
                vm.totalCommentsStr = trans('entities.no_comments');
 | 
			
		||||
            } else if (vm.totalComments === 1) {
 | 
			
		||||
                vm.totalCommentsStr = trans('entities.one_comment');
 | 
			
		||||
            } else {
 | 
			
		||||
                vm.totalCommentsStr = trans('entities.x_comments', {
 | 
			
		||||
                    numComments: vm.totalComments
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function focusLinkedComment(linkedCommentId) {
 | 
			
		||||
            let comment = angular.element('#' + linkedCommentId);
 | 
			
		||||
            if (comment.length === 0) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            window.setupPageShow.goToText(linkedCommentId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function checkError(response) {
 | 
			
		||||
            let msg = null;
 | 
			
		||||
            if (isCommentOpSuccess(response)) {
 | 
			
		||||
                // all good
 | 
			
		||||
                return;
 | 
			
		||||
            } else if (response.data) {
 | 
			
		||||
                msg = response.data.message;
 | 
			
		||||
            } else {
 | 
			
		||||
                msg = trans('errors.comment_list');
 | 
			
		||||
            }
 | 
			
		||||
            if (msg) {
 | 
			
		||||
                events.emit('success', msg);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }]);
 | 
			
		||||
 | 
			
		||||
    function updateComment(comment, resp, $timeout, isDelete) {
 | 
			
		||||
        comment.text = resp.comment.text;
 | 
			
		||||
        comment.updated = resp.comment.updated;
 | 
			
		||||
        comment.updated_by = resp.comment.updated_by;
 | 
			
		||||
        comment.active = resp.comment.active;
 | 
			
		||||
        if (isDelete && !resp.comment.active) {
 | 
			
		||||
            comment.html = trans('entities.comment_deleted');
 | 
			
		||||
        } else {
 | 
			
		||||
            comment.html = resp.comment.html;
 | 
			
		||||
        }
 | 
			
		||||
        if (!$timeout) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        comment.is_hidden = true;
 | 
			
		||||
        $timeout(function() {
 | 
			
		||||
            comment.is_hidden = false;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function isCommentOpSuccess(resp) {
 | 
			
		||||
        if (resp && resp.data && resp.data.status === 'success') {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -386,128 +386,4 @@ module.exports = function (ngApp, events) {
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }]);
 | 
			
		||||
 | 
			
		||||
    ngApp.directive('commentReply', [function () {
 | 
			
		||||
        return {
 | 
			
		||||
            restrict: 'E',
 | 
			
		||||
            templateUrl: 'comment-reply.html',
 | 
			
		||||
            scope: {
 | 
			
		||||
              pageId: '=',
 | 
			
		||||
              parentId: '=',
 | 
			
		||||
              parent: '='
 | 
			
		||||
            },
 | 
			
		||||
            link: function (scope, element) {
 | 
			
		||||
                scope.isReply = true;
 | 
			
		||||
                element.find('textarea').focus();
 | 
			
		||||
                scope.$on('evt.comment-success', function (event) {
 | 
			
		||||
                    // no need for the event to do anything more.
 | 
			
		||||
                    event.stopPropagation();
 | 
			
		||||
                    event.preventDefault();
 | 
			
		||||
                    scope.closeBox();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                scope.closeBox = function () {
 | 
			
		||||
                    element.remove();
 | 
			
		||||
                    scope.$destroy();
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }]);
 | 
			
		||||
 | 
			
		||||
    ngApp.directive('commentEdit', [function () {
 | 
			
		||||
         return {
 | 
			
		||||
            restrict: 'E',
 | 
			
		||||
            templateUrl: 'comment-reply.html',
 | 
			
		||||
            scope: {
 | 
			
		||||
              comment: '='
 | 
			
		||||
            },
 | 
			
		||||
            link: function (scope, element) {
 | 
			
		||||
                scope.isEdit = true;
 | 
			
		||||
                element.find('textarea').focus();
 | 
			
		||||
                scope.$on('evt.comment-success', function (event, commentId) {
 | 
			
		||||
                   // no need for the event to do anything more.
 | 
			
		||||
                   event.stopPropagation();
 | 
			
		||||
                   event.preventDefault();
 | 
			
		||||
                   if (commentId === scope.comment.id && !scope.isNew) {
 | 
			
		||||
                       scope.closeBox();
 | 
			
		||||
                   }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                scope.closeBox = function () {
 | 
			
		||||
                    element.remove();
 | 
			
		||||
                    scope.$destroy();
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    ngApp.directive('commentReplyLink', ['$document', '$compile', function ($document, $compile) {
 | 
			
		||||
        return {
 | 
			
		||||
            scope: {
 | 
			
		||||
                comment: '='
 | 
			
		||||
            },
 | 
			
		||||
            link: function (scope, element, attr) {
 | 
			
		||||
                element.on('$destroy', function () {
 | 
			
		||||
                    element.off('click');
 | 
			
		||||
                    scope.$destroy();
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                element.on('click', function (e) {
 | 
			
		||||
                    e.preventDefault();
 | 
			
		||||
                    var $container = element.parents('.comment-actions').first();
 | 
			
		||||
                    if (!$container.length) {
 | 
			
		||||
                        console.error('commentReplyLink directive should be placed inside a container with class comment-box!');
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (attr.noCommentReplyDupe) {
 | 
			
		||||
                        removeDupe();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    compileHtml($container, scope, attr.isReply === 'true');
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        function compileHtml($container, scope, isReply) {
 | 
			
		||||
            let lnkFunc = null;
 | 
			
		||||
            if (isReply) {
 | 
			
		||||
                lnkFunc = $compile('<comment-reply page-id="comment.pageId" parent-id="comment.id" parent="comment"></comment-reply>');
 | 
			
		||||
            } else {
 | 
			
		||||
                lnkFunc = $compile('<comment-edit comment="comment"></comment-add>');
 | 
			
		||||
            }
 | 
			
		||||
            var compiledHTML = lnkFunc(scope);
 | 
			
		||||
            $container.append(compiledHTML);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function removeDupe() {
 | 
			
		||||
            let $existingElement = $document.find('.comments-list comment-reply, .comments-list comment-edit');
 | 
			
		||||
            if (!$existingElement.length) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $existingElement.remove();
 | 
			
		||||
        }
 | 
			
		||||
    }]);
 | 
			
		||||
 | 
			
		||||
    ngApp.directive('commentDeleteLink', ['$window', function ($window) {
 | 
			
		||||
        return {
 | 
			
		||||
            controller: 'CommentDeleteController',
 | 
			
		||||
            scope: {
 | 
			
		||||
                comment: '='
 | 
			
		||||
            },
 | 
			
		||||
            link: function (scope, element, attr, ctrl) {
 | 
			
		||||
 | 
			
		||||
                element.on('click', function(e) {
 | 
			
		||||
                    e.preventDefault();
 | 
			
		||||
                    var resp = $window.confirm(trans('entities.comment_delete_confirm'));
 | 
			
		||||
                    if (!resp) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    ctrl.delete(scope.comment);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }]);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,113 @@
 | 
			
		|||
const MarkdownIt = require("markdown-it");
 | 
			
		||||
const md = new MarkdownIt({ html: true });
 | 
			
		||||
 | 
			
		||||
var template = `
 | 
			
		||||
<div class="comment-editor" v-cloak>
 | 
			
		||||
<form novalidate>
 | 
			
		||||
    <textarea name="markdown" rows="3" v-model="comment.text" :placeholder="trans('entities.comment_placeholder')"></textarea>
 | 
			
		||||
    <input type="hidden" v-model="comment.pageId" name="comment.pageId" :value="pageId">
 | 
			
		||||
    <button type="button" v-if="isReply || isEdit" class="button muted" v-on:click="closeBox">{{ trans('entities.comment_cancel') }}</button>
 | 
			
		||||
    <button type="submit" class="button pos" v-on:click.prevent="saveComment">{{ trans('entities.comment_save') }}</button>
 | 
			
		||||
</form>
 | 
			
		||||
</div>
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
const props = {
 | 
			
		||||
    pageId: {},
 | 
			
		||||
    commentObj: {},
 | 
			
		||||
    isReply: {
 | 
			
		||||
        default: false,
 | 
			
		||||
        type: Boolean
 | 
			
		||||
    }, isEdit: {
 | 
			
		||||
        default: false,
 | 
			
		||||
        type: Boolean
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function data() {
 | 
			
		||||
    let comment = {
 | 
			
		||||
        text: ''
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (this.isReply) {
 | 
			
		||||
        comment.page_id = this.commentObj.page_id;
 | 
			
		||||
        comment.id = this.commentObj.id;
 | 
			
		||||
    } else if (this.isEdit) {
 | 
			
		||||
        comment = this.commentObj;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        comment: comment,
 | 
			
		||||
        trans: trans
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const methods = {
 | 
			
		||||
    saveComment: function (event) {
 | 
			
		||||
        let pageId = this.comment.page_id || this.pageId;
 | 
			
		||||
        let commentText = this.comment.text;
 | 
			
		||||
        if (!commentText) {
 | 
			
		||||
            return this.$events.emit('error', trans('errors.empty_comment'))
 | 
			
		||||
        }
 | 
			
		||||
        let commentHTML = md.render(commentText);
 | 
			
		||||
        let serviceUrl = `/ajax/page/${pageId}/comment/`;
 | 
			
		||||
        let httpMethod = 'post';
 | 
			
		||||
        let reqObj = {
 | 
			
		||||
            text: commentText,
 | 
			
		||||
            html: commentHTML
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (this.isEdit === true) {
 | 
			
		||||
            // this will be set when editing the comment.
 | 
			
		||||
            serviceUrl = `/ajax/page/${pageId}/comment/${this.comment.id}`;
 | 
			
		||||
            httpMethod = 'put';
 | 
			
		||||
        } else if (this.isReply === true) {
 | 
			
		||||
            // if its reply, get the parent comment id
 | 
			
		||||
            reqObj.parent_id = this.comment.id;
 | 
			
		||||
        }
 | 
			
		||||
        $http[httpMethod](window.baseUrl(serviceUrl), reqObj).then(resp => {
 | 
			
		||||
            if (!isCommentOpSuccess(resp)) {
 | 
			
		||||
                this.$events.emit('error', getErrorMsg(resp));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            // hide the comments first, and then retrigger the refresh
 | 
			
		||||
            if (this.isEdit) {
 | 
			
		||||
                this.$emit('comment-edited', event, resp.data.comment);
 | 
			
		||||
            } else {
 | 
			
		||||
                this.comment.text = '';
 | 
			
		||||
                this.$emit('comment-added', event);
 | 
			
		||||
                if (this.isReply === true) {
 | 
			
		||||
                    this.$emit('comment-replied', event, resp.data.comment);
 | 
			
		||||
                } else {
 | 
			
		||||
                    this.$parent.$emit('new-comment', event, resp.data.comment);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            this.$events.emit('success', resp.data.message);
 | 
			
		||||
        }).catch(err => {
 | 
			
		||||
            this.$events.emit('error', trans('errors.comment_add'))
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    closeBox: function (event) {
 | 
			
		||||
        this.$emit('editor-removed', event);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const computed = {};
 | 
			
		||||
 | 
			
		||||
function isCommentOpSuccess(resp) {
 | 
			
		||||
    if (resp && resp.data && resp.data.status === 'success') {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getErrorMsg(response) {
 | 
			
		||||
    if (response.data) {
 | 
			
		||||
        return response.data.message;
 | 
			
		||||
    } else {
 | 
			
		||||
        return trans('errors.comment_add');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = { name: 'comment-reply', template, data, props, methods, computed };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,174 @@
 | 
			
		|||
const commentReply = require('./comment-reply');
 | 
			
		||||
 | 
			
		||||
const template = `
 | 
			
		||||
<div class="comment-box">
 | 
			
		||||
  <div class='page-comment' :id="commentId">
 | 
			
		||||
  <div class="user-image">
 | 
			
		||||
      <img :src="comment.created_by.avatar_url" alt="user avatar">
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="comment-container">
 | 
			
		||||
      <div class="comment-header">
 | 
			
		||||
          <a :href="comment.created_by.profile_url">{{comment.created_by.name}}</a>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div v-html="comment.html" v-if="comment.active" class="comment-body" v-bind:class="{ 'comment-inactive' : !comment.active }">
 | 
			
		||||
 | 
			
		||||
      </div>
 | 
			
		||||
      <div v-if="!comment.active" class="comment-body comment-inactive">
 | 
			
		||||
          {{ trans('entities.comment_deleted') }}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="comment-actions">
 | 
			
		||||
          <ul>
 | 
			
		||||
              <li v-if="(level < 4 && canComment)">
 | 
			
		||||
                <a href="#" comment="comment" v-on:click.prevent="replyComment">{{ trans('entities.comment_reply') }}</a>
 | 
			
		||||
              </li>
 | 
			
		||||
              <li v-if="canEditOrDelete('update')">
 | 
			
		||||
                <a href="#" comment="comment" v-on:click.prevent="editComment">{{ trans('entities.comment_edit') }}</a>
 | 
			
		||||
              </li>
 | 
			
		||||
              <li v-if="canEditOrDelete('delete')">
 | 
			
		||||
                <a href="#" comment="comment" v-on:click.prevent="deleteComment">{{ trans('entities.comment_delete') }}</a>
 | 
			
		||||
              </li>
 | 
			
		||||
              <li>{{ trans('entities.comment_create') }}
 | 
			
		||||
                <a :title="comment.created.day_time_str" :href="commentHref">{{comment.created.diff}}</a>
 | 
			
		||||
              </li>
 | 
			
		||||
              <li v-if="comment.updated">
 | 
			
		||||
                <span :title="comment.updated.day_time_str">{{trans('entities.comment_updated_text', { updateDiff: comment.updated.diff }) }}
 | 
			
		||||
                      <a :href="comment.updated_by.profile_url">{{comment.updated_by.name}}</a>
 | 
			
		||||
                </span>
 | 
			
		||||
              </li>
 | 
			
		||||
          </ul>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div v-if="showEditor">
 | 
			
		||||
        <comment-reply :page-id="comment.page_id" :comment-obj="comment"
 | 
			
		||||
          v-on:editor-removed.stop.prevent="hideComment"
 | 
			
		||||
          v-on:comment-replied.stop="commentReplied(...arguments)"
 | 
			
		||||
          v-on:comment-edited.stop="commentEdited(...arguments)"
 | 
			
		||||
          v-on:comment-added.stop="commentAdded"
 | 
			
		||||
           :is-reply="isReply" :is-edit="isEdit">
 | 
			
		||||
        </comment-reply>
 | 
			
		||||
      </div>
 | 
			
		||||
      <comment v-for="(comment, index) in comments" :initial-comment="comment" :index="index"
 | 
			
		||||
        :level="nextLevel" :key="comment.id" :permissions="permissions" :current-user-id="currentUserId"
 | 
			
		||||
        v-on:comment-added.stop="commentAdded"></comment>
 | 
			
		||||
 | 
			
		||||
  </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
const props = ['initialComment', 'index', 'level', 'permissions', 'currentUserId'];
 | 
			
		||||
 | 
			
		||||
function data() {
 | 
			
		||||
    return {
 | 
			
		||||
        trans: trans,
 | 
			
		||||
        comments: [],
 | 
			
		||||
        showEditor: false,
 | 
			
		||||
        comment: this.initialComment,
 | 
			
		||||
        nextLevel: this.level + 1
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const methods = {
 | 
			
		||||
    deleteComment: function () {
 | 
			
		||||
        var resp = window.confirm(trans('entities.comment_delete_confirm'));
 | 
			
		||||
        if (!resp) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this.$http.delete(window.baseUrl(`/ajax/comment/${this.comment.id}`)).then(resp => {
 | 
			
		||||
            if (!isCommentOpSuccess(resp)) {
 | 
			
		||||
                this.$events.emit('error', trans('error.comment_delete'));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            this.$events.emit('success', trans('entities.comment_deleted'));
 | 
			
		||||
            this.comment = resp.data.comment;
 | 
			
		||||
        }).catch(err => {
 | 
			
		||||
            this.$events.emit('error', trans('error.comment_delete'));
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    replyComment: function () {
 | 
			
		||||
        this.toggleEditor(false);
 | 
			
		||||
    },
 | 
			
		||||
    editComment: function () {
 | 
			
		||||
        this.toggleEditor(true);
 | 
			
		||||
    },
 | 
			
		||||
    hideComment: function () {
 | 
			
		||||
        this.showEditor = false;
 | 
			
		||||
    },
 | 
			
		||||
    toggleEditor: function (isEdit) {
 | 
			
		||||
        this.showEditor = false;
 | 
			
		||||
        this.isEdit = isEdit;
 | 
			
		||||
        this.isReply = !isEdit;
 | 
			
		||||
        this.showEditor = true;
 | 
			
		||||
    },
 | 
			
		||||
    commentReplied: function (event, comment) {
 | 
			
		||||
        this.comments.push(comment);
 | 
			
		||||
        this.showEditor = false;
 | 
			
		||||
    },
 | 
			
		||||
    commentEdited: function (event, comment) {
 | 
			
		||||
        this.comment = comment;
 | 
			
		||||
        this.showEditor = false;
 | 
			
		||||
    },
 | 
			
		||||
    commentAdded: function (event, comment) {
 | 
			
		||||
        // this is to handle non-parent child relationship
 | 
			
		||||
        // we want to make it go up.
 | 
			
		||||
        this.$emit('comment-added', event);
 | 
			
		||||
    },
 | 
			
		||||
    canEditOrDelete: function (prop) {
 | 
			
		||||
        if (!this.comment.active) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this.permissions) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let propAll = 'comment_' + prop + '_all';
 | 
			
		||||
        let propOwn = 'comment_' + prop + '_own';
 | 
			
		||||
 | 
			
		||||
        if (this.permissions[propAll]) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.permissions[propOwn] && this.comment.created_by.id === this.currentUserId) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
    canComment: function () {
 | 
			
		||||
        if (!this.permissions) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        return this.permissions.comment_create === true;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const computed = {
 | 
			
		||||
    commentId: function () {
 | 
			
		||||
        return `comment-${this.comment.page_id}-${this.comment.id}`;
 | 
			
		||||
    },
 | 
			
		||||
    commentHref: function () {
 | 
			
		||||
        return `#?cm=${this.commentId}`;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function mounted() {
 | 
			
		||||
    if (this.comment.sub_comments && this.comment.sub_comments.length) {
 | 
			
		||||
        // set this so that we can render the next set of sub comments.
 | 
			
		||||
        this.comments = this.comment.sub_comments;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function isCommentOpSuccess(resp) {
 | 
			
		||||
    if (resp && resp.data && resp.data.status === 'success') {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    name: 'comment',
 | 
			
		||||
    template, data, props, methods, computed, mounted, components: {
 | 
			
		||||
        commentReply
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,117 @@
 | 
			
		|||
const comment = require('./components/comments/comment');
 | 
			
		||||
const commentReply = require('./components/comments/comment-reply');
 | 
			
		||||
 | 
			
		||||
let data = {
 | 
			
		||||
    totalCommentsStr: trans('entities.comments_loading'),
 | 
			
		||||
    comments: [],
 | 
			
		||||
    permissions: null,
 | 
			
		||||
    currentUserId: null,
 | 
			
		||||
    trans: trans,
 | 
			
		||||
    commentCount: 0
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let methods = {
 | 
			
		||||
    commentAdded: function () {
 | 
			
		||||
        ++this.totalComments;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let computed = {
 | 
			
		||||
    totalComments: {
 | 
			
		||||
        get: function () {
 | 
			
		||||
            return this.commentCount;
 | 
			
		||||
        },
 | 
			
		||||
        set: function (value) {
 | 
			
		||||
            this.commentCount = value;
 | 
			
		||||
            if (value === 0) {
 | 
			
		||||
                this.totalCommentsStr = trans('entities.no_comments');
 | 
			
		||||
            } else if (value === 1) {
 | 
			
		||||
                this.totalCommentsStr = trans('entities.one_comment');
 | 
			
		||||
            } else {
 | 
			
		||||
                this.totalCommentsStr = trans('entities.x_comments', {
 | 
			
		||||
                    numComments: value
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    canComment: function () {
 | 
			
		||||
        if (!this.permissions) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        return this.permissions.comment_create === true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function mounted() {
 | 
			
		||||
    this.pageId = Number(this.$el.getAttribute('page-id'));
 | 
			
		||||
    let linkedCommentId = getUrlParameter('cm');
 | 
			
		||||
    this.$http.get(window.baseUrl(`/ajax/page/${this.pageId}/comments/`)).then(resp => {
 | 
			
		||||
        if (!isCommentOpSuccess(resp)) {
 | 
			
		||||
            // just show that no comments are available.
 | 
			
		||||
            vm.totalComments = 0;
 | 
			
		||||
            this.$events.emit('error', getErrorMsg(resp));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this.comments = resp.data.comments;
 | 
			
		||||
        this.totalComments = +resp.data.total;
 | 
			
		||||
        this.permissions = resp.data.permissions;
 | 
			
		||||
        this.currentUserId = resp.data.user_id;
 | 
			
		||||
        if (!linkedCommentId) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // adding a setTimeout to give the comment list some time to render
 | 
			
		||||
        // before focusing the comment.
 | 
			
		||||
        setTimeout(function() {
 | 
			
		||||
            focusLinkedComment(linkedCommentId);
 | 
			
		||||
        });
 | 
			
		||||
    }).catch(err => {
 | 
			
		||||
        this.$events.emit('error', trans('errors.comment_list'));
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function isCommentOpSuccess(resp) {
 | 
			
		||||
    if (resp && resp.data && resp.data.status === 'success') {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getErrorMsg(response) {
 | 
			
		||||
    if (response.data) {
 | 
			
		||||
        return response.data.message;
 | 
			
		||||
    } else {
 | 
			
		||||
        return trans('errors.comment_add');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function created() {
 | 
			
		||||
    this.$on('new-comment', function (event, comment) {
 | 
			
		||||
        this.comments.push(comment);
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function beforeDestroy() {
 | 
			
		||||
    this.$off('new-comment');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getUrlParameter(name) {
 | 
			
		||||
    name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
 | 
			
		||||
    var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
 | 
			
		||||
    var results = regex.exec(location.hash);
 | 
			
		||||
    return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function focusLinkedComment(linkedCommentId) {
 | 
			
		||||
    let comment = document.getElementById(linkedCommentId);
 | 
			
		||||
    if (comment && comment.length !== 0) {
 | 
			
		||||
        window.setupPageShow.goToText(linkedCommentId);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    data, methods, mounted, computed, components: {
 | 
			
		||||
        comment, commentReply
 | 
			
		||||
    },
 | 
			
		||||
    created, beforeDestroy
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -11,6 +11,7 @@ let vueMapping = {
 | 
			
		|||
    'image-manager': require('./image-manager'),
 | 
			
		||||
    'tag-manager': require('./tag-manager'),
 | 
			
		||||
    'attachment-manager': require('./attachment-manager'),
 | 
			
		||||
    'page-comments': require('./page-comments')
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
window.vues = {};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,7 +63,7 @@ return [
 | 
			
		|||
    // Comments
 | 
			
		||||
    'comment_list' => 'An error occurred while fetching the comments.',
 | 
			
		||||
    'cannot_add_comment_to_draft' => 'You cannot add comments to a draft.',
 | 
			
		||||
    'comment_add' => 'An error occurred while adding the comment.',
 | 
			
		||||
    'comment_add' => 'An error occurred while adding / updating the comment.',
 | 
			
		||||
    'comment_delete' => 'An error occurred while deleting the comment.',
 | 
			
		||||
    'empty_comment' => 'Cannot add an empty comment.',
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +0,0 @@
 | 
			
		|||
<div class="comment-editor" ng-controller="CommentReplyController as vm" ng-cloak>
 | 
			
		||||
    <form novalidate>
 | 
			
		||||
        <textarea name="markdown" rows="3" ng-model="comment.text" placeholder="{{ trans('entities.comment_placeholder') }}"></textarea>
 | 
			
		||||
        <input type="hidden" ng-model="comment.pageId" name="comment.pageId" value="{{$pageId}}" ng-init="comment.pageId = {{$pageId }}">
 | 
			
		||||
        <button type="button" ng-if="::(isReply || isEdit)" class="button muted" ng-click="closeBox()">{{ trans('entities.comment_cancel') }}</button>
 | 
			
		||||
        <button type="submit" class="button pos" ng-click="vm.saveComment()">{{ trans('entities.comment_save') }}</button>
 | 
			
		||||
    </form>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@if($errors->has('markdown'))
 | 
			
		||||
    <div class="text-neg text-small">{{ $errors->first('markdown') }}</div>
 | 
			
		||||
@endif
 | 
			
		||||
| 
						 | 
				
			
			@ -1,18 +1,11 @@
 | 
			
		|||
<script type="text/ng-template" id="comment-list-item.html">
 | 
			
		||||
    @include('comments/list-item')
 | 
			
		||||
</script>
 | 
			
		||||
<script type="text/ng-template" id="comment-reply.html">
 | 
			
		||||
    @include('comments/comment-reply', ['pageId' => $pageId])
 | 
			
		||||
</script>
 | 
			
		||||
<div ng-controller="CommentListController as vm" ng-init="pageId = <?= $page->id ?>" class="comments-list" ng-cloak>
 | 
			
		||||
<h3>@{{vm.totalCommentsStr}}</h3>
 | 
			
		||||
<hr>
 | 
			
		||||
    <div class="comment-box" ng-repeat="comment in vm.comments track by comment.id">
 | 
			
		||||
        <div ng-include src="'comment-list-item.html'">
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div ng-if="::vm.canComment()">
 | 
			
		||||
        @include('comments/comment-reply', ['pageId' => $pageId])
 | 
			
		||||
    </div>
 | 
			
		||||
<div id="page-comments" page-id="<?= $page->id ?>" class="comments-list" v-cloak>
 | 
			
		||||
  <h3>@{{totalCommentsStr}}</h3>
 | 
			
		||||
  <hr>
 | 
			
		||||
  <comment v-for="(comment, index) in comments" :initial-comment="comment" :index="index" :level=1
 | 
			
		||||
     v-on:comment-added.stop="commentAdded"
 | 
			
		||||
     :current-user-id="currentUserId" :key="comment.id" :permissions="permissions"></comment>
 | 
			
		||||
  <div v-if="canComment">
 | 
			
		||||
     <comment-reply v-on:comment-added.stop="commentAdded" :page-id="<?= $page->id ?>">
 | 
			
		||||
     </comment-reply>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,30 +0,0 @@
 | 
			
		|||
<div class='page-comment' id="comment-@{{::pageId}}-@{{::comment.id}}">
 | 
			
		||||
    <div class="user-image">
 | 
			
		||||
        <img ng-src="@{{::comment.created_by.avatar_url}}" alt="user avatar">
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="comment-container">
 | 
			
		||||
        <div class="comment-header">
 | 
			
		||||
            <a href="@{{::comment.created_by.profile_url}}">@{{ ::comment.created_by.name }}</a>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div ng-bind-html="comment.html" ng-if="::comment.active" class="comment-body" ng-class="!comment.active ? 'comment-inactive' : ''">
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
        <div ng-if="::!comment.active" class="comment-body comment-inactive">
 | 
			
		||||
            {{ trans('entities.comment_deleted') }}
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="comment-actions">
 | 
			
		||||
            <ul ng-if="!comment.is_hidden">
 | 
			
		||||
                <li ng-if="::(level < 3 && vm.canComment())"><a href="#" comment-reply-link no-comment-reply-dupe="true" comment="comment" is-reply="true">{{ trans('entities.comment_reply') }}</a></li>
 | 
			
		||||
                <li ng-if="::vm.canEditDelete(comment, 'comment_update')"><a href="#" comment-reply-link no-comment-reply-dupe="true" comment="comment" >{{ trans('entities.comment_edit') }}</a></li>
 | 
			
		||||
                <li ng-if="::vm.canEditDelete(comment, 'comment_delete')"><a href="#" comment-delete-link comment="comment" >{{ trans('entities.comment_delete') }}</a></li>
 | 
			
		||||
                <li>{{ trans('entities.comment_create') }} <a title="@{{::comment.created.day_time_str}}" href="#?cm=comment-@{{::pageId}}-@{{::comment.id}}">@{{::comment.created.diff}}</a></li>
 | 
			
		||||
                <li ng-if="::comment.updated"><span title="@{{::comment.updated.day_time_str}}">@{{ ::vm.trans('entities.comment_updated_text', { updateDiff: comment.updated.diff }) }}
 | 
			
		||||
                        <a href="@{{::comment.updated_by.profile_url}}">@{{::comment.updated_by.name}}</a></span></li>
 | 
			
		||||
            </ul>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="comment-box" ng-repeat="comment in comments = comment.sub_comments track by comment.id" ng-init="level = level + 1">
 | 
			
		||||
            <div ng-include src="'comment-list-item.html'">
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
		Loading…
	
		Reference in New Issue