');
- }
- 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);
- });
- }
- };
- }]);
};
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..0f65fc237
--- /dev/null
+++ b/resources/assets/js/vues/components/comments/comment-reply.js
@@ -0,0 +1,113 @@
+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() {
+ 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 };
+
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..419c0a5fa
--- /dev/null
+++ b/resources/assets/js/vues/components/comments/comment.js
@@ -0,0 +1,174 @@
+const commentReply = require('./comment-reply');
+
+const template = `
+
+`;
+
+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
+ }
+};
+
diff --git a/resources/assets/js/vues/page-comments.js b/resources/assets/js/vues/page-comments.js
new file mode 100644
index 000000000..e42cdbf9c
--- /dev/null
+++ b/resources/assets/js/vues/page-comments.js
@@ -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
+};
\ No newline at end of file
diff --git a/resources/assets/js/vues/vues.js b/resources/assets/js/vues/vues.js
index a70d32009..d4c4c4574 100644
--- a/resources/assets/js/vues/vues.js
+++ b/resources/assets/js/vues/vues.js
@@ -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 = {};
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.',
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/comments.blade.php b/resources/views/comments/comments.blade.php
index ffa75cfed..fcf284b26 100644
--- a/resources/views/comments/comments.blade.php
+++ b/resources/views/comments/comments.blade.php
@@ -1,18 +1,11 @@
-
-
-