From ed375bfaf7c8563afe53397337841811feb36e49 Mon Sep 17 00:00:00 2001 From: Abijeet Date: Sun, 20 Aug 2017 20:21:27 +0530 Subject: [PATCH 1/9] Refactored Angular code to instead use VueJS, left with permissions, testing and load testing. --- config/app.php | 2 +- config/database.php | 6 +-- resources/assets/js/vues/vues.js | 1 + resources/views/comments/comments.blade.php | 27 ++++------- resources/views/comments/list-item.blade.php | 49 +++++++++++--------- 5 files changed, 41 insertions(+), 44 deletions(-) diff --git a/config/app.php b/config/app.php index a390eaf83..23aad7d48 100644 --- a/config/app.php +++ b/config/app.php @@ -18,7 +18,7 @@ return [ | */ - 'debug' => env('APP_DEBUG', false), + 'debug' => env('APP_DEBUG', true), /* |-------------------------------------------------------------------------- diff --git a/config/database.php b/config/database.php index 3883b5868..50f702e33 100644 --- a/config/database.php +++ b/config/database.php @@ -79,9 +79,9 @@ return [ 'mysql' => [ 'driver' => 'mysql', 'host' => $mysql_host, - 'database' => env('DB_DATABASE', 'forge'), - 'username' => env('DB_USERNAME', 'forge'), - 'password' => env('DB_PASSWORD', ''), + 'database' => env('DB_DATABASE', 'bookstack'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', 'Change123'), 'port' => $mysql_port, 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', diff --git a/resources/assets/js/vues/vues.js b/resources/assets/js/vues/vues.js index a3f6ec8e5..e5ab50c4d 100644 --- a/resources/assets/js/vues/vues.js +++ b/resources/assets/js/vues/vues.js @@ -10,6 +10,7 @@ let vueMapping = { 'code-editor': require('./code-editor'), 'image-manager': require('./image-manager'), 'tag-manager': require('./tag-manager'), + 'page-comments': require('./page-comments') }; window.vues = {}; diff --git a/resources/views/comments/comments.blade.php b/resources/views/comments/comments.blade.php index ffa75cfed..24a8b3e23 100644 --- a/resources/views/comments/comments.blade.php +++ b/resources/views/comments/comments.blade.php @@ -1,18 +1,11 @@ - - -
-

@{{vm.totalCommentsStr}}

-
-
-
- -
-
-
- @include('comments/comment-reply', ['pageId' => $pageId]) -
+
+

@{{totalCommentsStr}}

+
+ +
+ + +
\ No newline at end of file diff --git a/resources/views/comments/list-item.blade.php b/resources/views/comments/list-item.blade.php index f274d2ed2..72984d68d 100644 --- a/resources/views/comments/list-item.blade.php +++ b/resources/views/comments/list-item.blade.php @@ -1,30 +1,33 @@ -
+
+
- user avatar + user avatar
- -
+ +
+
+
+ {{ trans('entities.comment_deleted') }} +
+
+ +
+ +
+
-
- {{ trans('entities.comment_deleted') }} -
-
- -
-
-
-
-
+
+
\ No newline at end of file From 703d579561096fef7b8d44b981966475e4cd95a3 Mon Sep 17 00:00:00 2001 From: Abijeet Date: Sun, 20 Aug 2017 20:21:32 +0530 Subject: [PATCH 2/9] Refactored Angular code to instead use VueJS, left with permissions, testing and load testing. --- .../vues/components/comments/comment-reply.js | 125 +++++++++++++ .../js/vues/components/comments/comment.js | 174 ++++++++++++++++++ resources/assets/js/vues/page-comments.js | 109 +++++++++++ 3 files changed, 408 insertions(+) create mode 100644 resources/assets/js/vues/components/comments/comment-reply.js create mode 100644 resources/assets/js/vues/components/comments/comment.js create mode 100644 resources/assets/js/vues/page-comments.js diff --git a/resources/assets/js/vues/components/comments/comment-reply.js b/resources/assets/js/vues/components/comments/comment-reply.js new file mode 100644 index 000000000..83cbdf467 --- /dev/null +++ b/resources/assets/js/vues/components/comments/comment-reply.js @@ -0,0 +1,125 @@ +const MarkdownIt = require("markdown-it"); +const md = new MarkdownIt({html: true}); + +var template = ` +
+
+ + + + +
+
+`; + +const props = { + pageId: {}, + commentObj: {}, + isReply: { + default: false, + type: Boolean + }, isEdit: { + default: false, + type: Boolean + }}; + +function data () { + var comment = null; + // initialize comment if not passed. + if (!this.commentObj || this.isReply) { + comment = { + text: '' + }; + + if (this.isReply) { + comment.page_id = this.commentObj.page_id; + comment.id = this.commentObj.id; + } + } else { + comment = this.commentObj; + } + + return { + trans: trans, + parentId: null, + comment: comment + }; +} + +const methods = { + saveComment: function (event) { + let pageId = this.comment.page_id || this.pageId; + let commentText = this.comment.text; + if (!commentText) { + return this.$emit('evt.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)) { + 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.$emit('evt.comment-success', null, true); + } + + }, checkError); + }, + 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 checkError(msgKey) { + return function(response) { + let msg = null; + if (isCommentOpSuccess(response)) { + // all good + return; + } else if (response.data) { + msg = response.data.message; + } else { + msg = trans(msgKey); + } + if (msg) { + events.emit('success', msg); + } + } +} + +module.exports = {name: 'comment-reply', template, data, props, methods, computed}; + diff --git a/resources/assets/js/vues/components/comments/comment.js b/resources/assets/js/vues/components/comments/comment.js new file mode 100644 index 000000000..4152ba61f --- /dev/null +++ b/resources/assets/js/vues/components/comments/comment.js @@ -0,0 +1,174 @@ +const commentReply = require('./comment-reply'); + +const template = ` +
+
+
+ user avatar +
+
+ +
+ +
+
+ {{ trans('entities.comment_deleted') }} +
+
+ +
+
+ + +
+ + +
+
+
+`; + +const props = ['initialComment', 'index', 'level']; + +function data () { + return { + trans: trans, + commentHref: null, + 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)) { + return; + } + updateComment(this.comment, resp.data, true); + }, function (resp) { + if (isCommentOpSuccess(resp)) { + this.$events.emit('success', trans('entities.comment_deleted')); + } else { + 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); + } +}; + +const computed = { + commentId: { + get: function () { + return `comment-${this.comment.page_id}-${this.comment.id}`; + }, + set: function () { + this.commentHref = `#?cm=${this.commentId}` + } + }, + canUpdate: function () { + return true; + }, + canDelete: function () { + return true; + }, + canComment: function () { + return true; + }, + canUpdate: function () { + return true; + } +}; + +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; +} + +function updateComment(comment, resp, 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; + } +} + +module.exports = { + name: 'comment', + template, data, props, methods, computed, mounted, components: { + commentReply +}}; + diff --git a/resources/assets/js/vues/page-comments.js b/resources/assets/js/vues/page-comments.js new file mode 100644 index 000000000..fd0ed6826 --- /dev/null +++ b/resources/assets/js/vues/page-comments.js @@ -0,0 +1,109 @@ +const comment = require('./components/comments/comment'); +const commentReply = require('./components/comments/comment-reply'); + +// 1. Remove code from controllers +// 2. Remove code from services. +// 3. + +let data = { + totalCommentsStr: trans('entities.comments_loading'), + comments: [], + permissions: null, + current_user_id: 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 () { + return true; + } +} + +function mounted() { + this.pageId = Number(this.$el.getAttribute('page-id')); + // let linkedCommentId = this.$route.query.cm; + let linkedCommentId = null; + 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; + return; + } + this.comments = resp.data.comments; + this.totalComments = +resp.data.total; + this.permissions = resp.data.permissions; + this.current_user_id = resp.data.user_id; + if (!linkedCommentId) { + return; + } + $timeout(function() { + // wait for the UI to render. + focusLinkedComment(linkedCommentId); + }); + }, checkError('errors.comment_list')); +} + +function isCommentOpSuccess(resp) { + if (resp && resp.data && resp.data.status === 'success') { + return true; + } + return false; +} + +function checkError(msgKey) { + return function(response) { + let msg = null; + if (isCommentOpSuccess(response)) { + // all good + return; + } else if (response.data) { + msg = response.data.message; + } else { + msg = trans(msgKey); + } + if (msg) { + events.emit('success', msg); + } + } +} + +function created () { + this.$on('new-comment', function (event, comment) { + this.comments.push(comment); + }) +} + +function beforeDestroy() { + this.$off('new-comment'); +} + +module.exports = { + data, methods, mounted, computed, components : { + comment, commentReply + }, + created, beforeDestroy +}; \ No newline at end of file From 1f6994b62ce5d3989bc9997027cc6704e170013b Mon Sep 17 00:00:00 2001 From: Abijeet Date: Sun, 20 Aug 2017 21:26:44 +0530 Subject: [PATCH 3/9] Added code for permissions, removed unnecessary code. --- resources/assets/js/controllers.js | 209 ------------------ .../js/vues/components/comments/comment.js | 52 +++-- resources/assets/js/vues/page-comments.js | 13 +- resources/views/comments/comments.blade.php | 2 +- 4 files changed, 41 insertions(+), 235 deletions(-) diff --git a/resources/assets/js/controllers.js b/resources/assets/js/controllers.js index 8b37379fa..7f2e6cdb4 100644 --- a/resources/assets/js/controllers.js +++ b/resources/assets/js/controllers.js @@ -145,176 +145,8 @@ 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); @@ -324,46 +156,5 @@ module.exports = function (ngApp, events) { 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; - } }; diff --git a/resources/assets/js/vues/components/comments/comment.js b/resources/assets/js/vues/components/comments/comment.js index 4152ba61f..4b0c0a50b 100644 --- a/resources/assets/js/vues/components/comments/comment.js +++ b/resources/assets/js/vues/components/comments/comment.js @@ -18,13 +18,13 @@ const template = `
-
@@ -55,7 +55,7 @@ const template = `
`; -const props = ['initialComment', 'index', 'level']; +const props = ['initialComment', 'index', 'level', 'permissions', 'currentUserId']; function data () { return { @@ -114,6 +114,34 @@ const methods = { // 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; } }; @@ -125,18 +153,6 @@ const computed = { set: function () { this.commentHref = `#?cm=${this.commentId}` } - }, - canUpdate: function () { - return true; - }, - canDelete: function () { - return true; - }, - canComment: function () { - return true; - }, - canUpdate: function () { - return true; } }; diff --git a/resources/assets/js/vues/page-comments.js b/resources/assets/js/vues/page-comments.js index fd0ed6826..d3ce3006d 100644 --- a/resources/assets/js/vues/page-comments.js +++ b/resources/assets/js/vues/page-comments.js @@ -1,15 +1,11 @@ const comment = require('./components/comments/comment'); const commentReply = require('./components/comments/comment-reply'); -// 1. Remove code from controllers -// 2. Remove code from services. -// 3. - let data = { totalCommentsStr: trans('entities.comments_loading'), comments: [], permissions: null, - current_user_id: null, + currentUserId: null, trans: trans, commentCount: 0 }; @@ -39,7 +35,10 @@ let computed = { } }, canComment: function () { - return true; + if (!this.permissions) { + return false; + } + return this.permissions.comment_create === true; } } @@ -56,7 +55,7 @@ function mounted() { this.comments = resp.data.comments; this.totalComments = +resp.data.total; this.permissions = resp.data.permissions; - this.current_user_id = resp.data.user_id; + this.currentUserId = resp.data.user_id; if (!linkedCommentId) { return; } diff --git a/resources/views/comments/comments.blade.php b/resources/views/comments/comments.blade.php index 24a8b3e23..fcf284b26 100644 --- a/resources/views/comments/comments.blade.php +++ b/resources/views/comments/comments.blade.php @@ -3,7 +3,7 @@
+ :current-user-id="currentUserId" :key="comment.id" :permissions="permissions">
From e8fa58f201b062968bbe3a018a11cf7f29a86fed Mon Sep 17 00:00:00 2001 From: Abijeet Date: Mon, 21 Aug 2017 00:38:59 +0530 Subject: [PATCH 4/9] Removed code from the directives. --- resources/assets/js/directives.js | 124 ------------------------------ 1 file changed, 124 deletions(-) diff --git a/resources/assets/js/directives.js b/resources/assets/js/directives.js index fc92121ff..eb7d2776b 100644 --- a/resources/assets/js/directives.js +++ b/resources/assets/js/directives.js @@ -534,128 +534,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(''); - } else { - lnkFunc = $compile(''); - } - 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); - }); - } - }; - }]); }; From ac07cb41b64f66c921c5323a6ddc58980faf79b8 Mon Sep 17 00:00:00 2001 From: Abijeet Date: Mon, 21 Aug 2017 02:21:31 +0530 Subject: [PATCH 5/9] Fixed formatting and added error messages. --- .../vues/components/comments/comment-reply.js | 164 +++++++------- .../js/vues/components/comments/comment.js | 207 ++++++++---------- resources/assets/js/vues/page-comments.js | 141 ++++++------ resources/lang/en/errors.php | 2 +- 4 files changed, 240 insertions(+), 274 deletions(-) diff --git a/resources/assets/js/vues/components/comments/comment-reply.js b/resources/assets/js/vues/components/comments/comment-reply.js index 83cbdf467..0f65fc237 100644 --- a/resources/assets/js/vues/components/comments/comment-reply.js +++ b/resources/assets/js/vues/components/comments/comment-reply.js @@ -1,5 +1,5 @@ const MarkdownIt = require("markdown-it"); -const md = new MarkdownIt({html: true}); +const md = new MarkdownIt({ html: true }); var template = `
@@ -13,113 +13,101 @@ var template = ` `; const props = { - pageId: {}, - commentObj: {}, - isReply: { - default: false, - type: Boolean - }, isEdit: { - default: false, - type: Boolean - }}; + pageId: {}, + commentObj: {}, + isReply: { + default: false, + type: Boolean + }, isEdit: { + default: false, + type: Boolean + } +}; -function data () { - var comment = null; - // initialize comment if not passed. - if (!this.commentObj || this.isReply) { - comment = { - text: '' +function data() { + let comment = { + text: '' }; if (this.isReply) { - comment.page_id = this.commentObj.page_id; - comment.id = this.commentObj.id; + comment.page_id = this.commentObj.page_id; + comment.id = this.commentObj.id; + } else if (this.isEdit) { + comment = this.commentObj; } - } else { - comment = this.commentObj; - } - return { - trans: trans, - parentId: null, - comment: comment - }; + 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.$emit('evt.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)) { - return; + 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')) } - // 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); + 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; } - this.$emit('evt.comment-success', null, true); - } - - }, checkError); - }, - closeBox: function (event) { - this.$emit('editor-removed', event); - } + // 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; + if (resp && resp.data && resp.data.status === 'success') { + return true; + } + return false; } -function checkError(msgKey) { - return function(response) { - let msg = null; - if (isCommentOpSuccess(response)) { - // all good - return; - } else if (response.data) { - msg = response.data.message; +function getErrorMsg(response) { + if (response.data) { + return response.data.message; } else { - msg = trans(msgKey); + return trans('errors.comment_add'); } - if (msg) { - events.emit('success', msg); - } - } } -module.exports = {name: 'comment-reply', template, data, props, methods, computed}; +module.exports = { name: 'comment-reply', template, data, props, methods, computed }; diff --git a/resources/assets/js/vues/components/comments/comment.js b/resources/assets/js/vues/components/comments/comment.js index 4b0c0a50b..ada638526 100644 --- a/resources/assets/js/vues/components/comments/comment.js +++ b/resources/assets/js/vues/components/comments/comment.js @@ -37,7 +37,7 @@ const template = `
-
+
{ - if (!isCommentOpSuccess(resp)) { - return; - } - updateComment(this.comment, resp.data, true); - }, function (resp) { - if (isCommentOpSuccess(resp)) { - this.$events.emit('success', trans('entities.comment_deleted')); - } else { - 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; - } + 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; - } + if (!this.permissions) { + return false; + } - let propAll = 'comment_' + prop + '_all'; - let propOwn = 'comment_' + prop + '_own'; + let propAll = 'comment_' + prop + '_all'; + let propOwn = 'comment_' + prop + '_own'; - if (this.permissions[propAll]) { - return true; - } + if (this.permissions[propAll]) { + return true; + } - if (this.permissions[propOwn] && this.comment.created_by.id === this.currentUserId) { - 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 false; + }, + canComment: function () { + if (!this.permissions) { + return false; + } + return this.permissions.comment_create === true; } - return this.permissions.comment_create === true; - } }; const computed = { - commentId: { - get: function () { - return `comment-${this.comment.page_id}-${this.comment.id}`; - }, - set: function () { - this.commentHref = `#?cm=${this.commentId}` + commentId: { + get: function () { + return `comment-${this.comment.page_id}-${this.comment.id}`; + }, + set: function () { + this.commentHref = `#?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 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; -} - -function updateComment(comment, resp, 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 (resp && resp.data && resp.data.status === 'success') { + return true; + } + return false; } module.exports = { - name: 'comment', - template, data, props, methods, computed, mounted, components: { - commentReply -}}; + name: 'comment', + template, data, props, methods, computed, mounted, components: { + commentReply + } +}; diff --git a/resources/assets/js/vues/page-comments.js b/resources/assets/js/vues/page-comments.js index d3ce3006d..76d11ac6c 100644 --- a/resources/assets/js/vues/page-comments.js +++ b/resources/assets/js/vues/page-comments.js @@ -2,107 +2,98 @@ 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 + totalCommentsStr: trans('entities.comments_loading'), + comments: [], + permissions: null, + currentUserId: null, + trans: trans, + commentCount: 0 }; let methods = { - commentAdded: function () { - ++this.totalComments; - } + commentAdded: function () { + ++this.totalComments; + } } let computed = { - totalComments: { - get: function () { - return this.commentCount; + 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 + }); + } + } }, - 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; } - }, - 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 = this.$route.query.cm; - let linkedCommentId = null; - 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; - 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; - } - $timeout(function() { - // wait for the UI to render. + this.pageId = Number(this.$el.getAttribute('page-id')); + // let linkedCommentId = this.$route.query.cm; + let linkedCommentId = null; + 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; + } focusLinkedComment(linkedCommentId); + }).catch(err => { + this.$events.emit('error', 'errors.comment_list'); }); - }, checkError('errors.comment_list')); } function isCommentOpSuccess(resp) { - if (resp && resp.data && resp.data.status === 'success') { - return true; - } - return false; + if (resp && resp.data && resp.data.status === 'success') { + return true; + } + return false; } -function checkError(msgKey) { - return function(response) { - let msg = null; - if (isCommentOpSuccess(response)) { - // all good - return; - } else if (response.data) { - msg = response.data.message; +function getErrorMsg(response) { + if (response.data) { + return response.data.message; } else { - msg = trans(msgKey); + return trans('errors.comment_add'); } - if (msg) { - events.emit('success', msg); - } - } } -function created () { - this.$on('new-comment', function (event, comment) { - this.comments.push(comment); - }) +function created() { + this.$on('new-comment', function (event, comment) { + this.comments.push(comment); + }) } function beforeDestroy() { - this.$off('new-comment'); + this.$off('new-comment'); } module.exports = { - data, methods, mounted, computed, components : { - comment, commentReply - }, - created, beforeDestroy + data, methods, mounted, computed, components: { + comment, commentReply + }, + created, beforeDestroy }; \ No newline at end of file diff --git a/resources/lang/en/errors.php b/resources/lang/en/errors.php index 71bcd1f9a..09158caac 100644 --- a/resources/lang/en/errors.php +++ b/resources/lang/en/errors.php @@ -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.', From b5cd3bff3cd9ede7277a60927857101cf75614cc Mon Sep 17 00:00:00 2001 From: Abijeet Date: Tue, 22 Aug 2017 01:31:11 +0530 Subject: [PATCH 6/9] Added functionality to highlight a comment. --- resources/assets/js/controllers.js | 13 ---------- .../js/vues/components/comments/comment.js | 13 ++++------ resources/assets/js/vues/page-comments.js | 26 ++++++++++++++++--- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/resources/assets/js/controllers.js b/resources/assets/js/controllers.js index 7f2e6cdb4..32ff76fa1 100644 --- a/resources/assets/js/controllers.js +++ b/resources/assets/js/controllers.js @@ -144,17 +144,4 @@ module.exports = function (ngApp, events) { }; }]); - - // Controller used to fetch all comments for a page - ngApp.controller('CommentListController', ['$scope', '$http', '$timeout', '$location', function ($scope, $http, $timeout, $location) { - - function focusLinkedComment(linkedCommentId) { - let comment = angular.element('#' + linkedCommentId); - if (comment.length === 0) { - return; - } - - window.setupPageShow.goToText(linkedCommentId); - } - }]); }; diff --git a/resources/assets/js/vues/components/comments/comment.js b/resources/assets/js/vues/components/comments/comment.js index ada638526..419c0a5fa 100644 --- a/resources/assets/js/vues/components/comments/comment.js +++ b/resources/assets/js/vues/components/comments/comment.js @@ -59,7 +59,6 @@ const props = ['initialComment', 'index', 'level', 'permissions', 'currentUserId function data() { return { - commentHref: null, trans: trans, comments: [], showEditor: false, @@ -144,13 +143,11 @@ const methods = { }; const computed = { - commentId: { - get: function () { - return `comment-${this.comment.page_id}-${this.comment.id}`; - }, - set: function () { - this.commentHref = `#?cm=${this.commentId}` - } + commentId: function () { + return `comment-${this.comment.page_id}-${this.comment.id}`; + }, + commentHref: function () { + return `#?cm=${this.commentId}`; } }; diff --git a/resources/assets/js/vues/page-comments.js b/resources/assets/js/vues/page-comments.js index 76d11ac6c..4cdee05ed 100644 --- a/resources/assets/js/vues/page-comments.js +++ b/resources/assets/js/vues/page-comments.js @@ -45,7 +45,7 @@ let computed = { function mounted() { this.pageId = Number(this.$el.getAttribute('page-id')); // let linkedCommentId = this.$route.query.cm; - let linkedCommentId = null; + 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. @@ -60,9 +60,13 @@ function mounted() { if (!linkedCommentId) { return; } - focusLinkedComment(linkedCommentId); + + // adding a setTimeout to give comment list some time to render. + setTimeout(function() { + focusLinkedComment(linkedCommentId); + }); }).catch(err => { - this.$events.emit('error', 'errors.comment_list'); + this.$events.emit('error', trans('errors.comment_list')); }); } @@ -91,6 +95,22 @@ 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) { + return; + } + + window.setupPageShow.goToText(linkedCommentId); +} + module.exports = { data, methods, mounted, computed, components: { comment, commentReply From 6920d6eef10534ae360af685074c2b8e5428bc22 Mon Sep 17 00:00:00 2001 From: Abijeet Date: Tue, 22 Aug 2017 01:39:09 +0530 Subject: [PATCH 7/9] Fixes the comment check for linked comment. --- resources/assets/js/vues/page-comments.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/resources/assets/js/vues/page-comments.js b/resources/assets/js/vues/page-comments.js index 4cdee05ed..6a550e991 100644 --- a/resources/assets/js/vues/page-comments.js +++ b/resources/assets/js/vues/page-comments.js @@ -104,11 +104,9 @@ function getUrlParameter(name) { function focusLinkedComment(linkedCommentId) { let comment = document.getElementById(linkedCommentId); - if (comment && comment.length === 0) { - return; + if (comment && comment.length !== 0) { + window.setupPageShow.goToText(linkedCommentId); } - - window.setupPageShow.goToText(linkedCommentId); } module.exports = { From 769935f99e1cf9a6f6d4d1cba88fdcf60f1d04b9 Mon Sep 17 00:00:00 2001 From: Abijeet Date: Wed, 23 Aug 2017 00:52:50 +0530 Subject: [PATCH 8/9] Reverting database.php and app.php --- config/app.php | 2 +- config/database.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/app.php b/config/app.php index 23aad7d48..a390eaf83 100644 --- a/config/app.php +++ b/config/app.php @@ -18,7 +18,7 @@ return [ | */ - 'debug' => env('APP_DEBUG', true), + 'debug' => env('APP_DEBUG', false), /* |-------------------------------------------------------------------------- diff --git a/config/database.php b/config/database.php index 50f702e33..3883b5868 100644 --- a/config/database.php +++ b/config/database.php @@ -79,9 +79,9 @@ return [ 'mysql' => [ 'driver' => 'mysql', 'host' => $mysql_host, - 'database' => env('DB_DATABASE', 'bookstack'), - 'username' => env('DB_USERNAME', 'root'), - 'password' => env('DB_PASSWORD', 'Change123'), + 'database' => env('DB_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), 'port' => $mysql_port, 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', From 76ae5c7398955ff5c85f0f45ec950e4aee059a7c Mon Sep 17 00:00:00 2001 From: Abijeet Date: Wed, 23 Aug 2017 01:04:36 +0530 Subject: [PATCH 9/9] Removes some unused code. --- resources/assets/js/vues/page-comments.js | 4 +-- .../views/comments/comment-reply.blade.php | 12 ------- resources/views/comments/list-item.blade.php | 33 ------------------- 3 files changed, 2 insertions(+), 47 deletions(-) delete mode 100644 resources/views/comments/comment-reply.blade.php delete mode 100644 resources/views/comments/list-item.blade.php diff --git a/resources/assets/js/vues/page-comments.js b/resources/assets/js/vues/page-comments.js index 6a550e991..e42cdbf9c 100644 --- a/resources/assets/js/vues/page-comments.js +++ b/resources/assets/js/vues/page-comments.js @@ -44,7 +44,6 @@ let computed = { function mounted() { this.pageId = Number(this.$el.getAttribute('page-id')); - // let linkedCommentId = this.$route.query.cm; let linkedCommentId = getUrlParameter('cm'); this.$http.get(window.baseUrl(`/ajax/page/${this.pageId}/comments/`)).then(resp => { if (!isCommentOpSuccess(resp)) { @@ -61,7 +60,8 @@ function mounted() { return; } - // adding a setTimeout to give comment list some time to render. + // adding a setTimeout to give the comment list some time to render + // before focusing the comment. setTimeout(function() { focusLinkedComment(linkedCommentId); }); diff --git a/resources/views/comments/comment-reply.blade.php b/resources/views/comments/comment-reply.blade.php deleted file mode 100644 index 02535341c..000000000 --- a/resources/views/comments/comment-reply.blade.php +++ /dev/null @@ -1,12 +0,0 @@ -
-
- - - - -
-
- -@if($errors->has('markdown')) -
{{ $errors->first('markdown') }}
-@endif \ No newline at end of file diff --git a/resources/views/comments/list-item.blade.php b/resources/views/comments/list-item.blade.php deleted file mode 100644 index 72984d68d..000000000 --- a/resources/views/comments/list-item.blade.php +++ /dev/null @@ -1,33 +0,0 @@ -
-
-
- user avatar -
-
- -
- -
-
- {{ trans('entities.comment_deleted') }} -
-
- -
- -
-
-
-
-
-
-
\ No newline at end of file