]> BookStack Code Mirror - bookstack/blob - resources/assets/js/vues/components/comments/comment.js
4b0c0a50bdf6b84437041444cfa78c0e0a64b00b
[bookstack] / resources / assets / js / vues / components / comments / comment.js
1 const commentReply = require('./comment-reply');
2
3 const template = `
4 <div class="comment-box">
5   <div class='page-comment' :id="commentId">
6   <div class="user-image">
7       <img :src="comment.created_by.avatar_url" alt="user avatar">
8   </div>
9   <div class="comment-container">
10       <div class="comment-header">
11           <a :href="comment.created_by.profile_url">{{comment.created_by.name}}</a>
12       </div>
13       <div v-html="comment.html" v-if="comment.active" class="comment-body" v-bind:class="{ 'comment-inactive' : !comment.active }">
14
15       </div>
16       <div v-if="!comment.active" class="comment-body comment-inactive">
17           {{ trans('entities.comment_deleted') }}
18       </div>
19       <div class="comment-actions">
20           <ul>
21               <li v-if="(level < 4 && canComment)">
22                 <a href="#" comment="comment" v-on:click.prevent="replyComment">{{ trans('entities.comment_reply') }}</a>
23               </li>
24               <li v-if="canEditOrDelete('update')">
25                 <a href="#" comment="comment" v-on:click.prevent="editComment">{{ trans('entities.comment_edit') }}</a>
26               </li>
27               <li v-if="canEditOrDelete('delete')">
28                 <a href="#" comment="comment" v-on:click.prevent="deleteComment">{{ trans('entities.comment_delete') }}</a>
29               </li>
30               <li>{{ trans('entities.comment_create') }}
31                 <a :title="comment.created.day_time_str" :href="commentHref">{{comment.created.diff}}</a>
32               </li>
33               <li v-if="comment.updated">
34                 <span :title="comment.updated.day_time_str">{{trans('entities.comment_updated_text', { updateDiff: comment.updated.diff }) }}
35                       <a :href="comment.updated_by.profile_url">{{comment.updated_by.name}}</a>
36                 </span>
37               </li>
38           </ul>
39       </div>
40       <div v-if="showEditor && level <= 3">
41         <comment-reply :page-id="comment.page_id" :comment-obj="comment"
42           v-on:editor-removed.stop.prevent="hideComment"
43           v-on:comment-replied.stop="commentReplied(...arguments)"
44           v-on:comment-edited.stop="commentEdited(...arguments)"
45           v-on:comment-added.stop="commentAdded"
46            :is-reply="isReply" :is-edit="isEdit">
47         </comment-reply>
48       </div>
49       <comment v-for="(comment, index) in comments" :initial-comment="comment" :index="index"
50         :level="nextLevel" :key="comment.id" :permissions="permissions" :current-user-id="currentUserId"
51         v-on:comment-added.stop="commentAdded"></comment>
52
53   </div>
54   </div>
55 </div>
56 `;
57
58 const props = ['initialComment', 'index', 'level', 'permissions', 'currentUserId'];
59
60 function data () {
61   return {
62     trans: trans,
63     commentHref: null,
64     comments: [],
65     showEditor: false,
66     comment: this.initialComment,
67     nextLevel: this.level + 1
68   };
69 }
70
71 const methods = {
72   deleteComment: function () {
73     var resp = window.confirm(trans('entities.comment_delete_confirm'));
74     if (!resp) {
75         return;
76     }
77     this.$http.delete(window.baseUrl(`/ajax/comment/${this.comment.id}`)).then(resp => {
78       if (!isCommentOpSuccess(resp)) {
79           return;
80       }
81       updateComment(this.comment, resp.data, true);
82     }, function (resp) {
83       if (isCommentOpSuccess(resp)) {
84           this.$events.emit('success', trans('entities.comment_deleted'));
85       } else {
86           this.$events.emit('error', trans('error.comment_delete'));
87       }
88     });
89   },
90   replyComment: function () {
91     this.toggleEditor(false);
92   },
93   editComment: function () {
94     this.toggleEditor(true);
95   },
96   hideComment: function () {
97     this.showEditor = false;
98   },
99   toggleEditor: function (isEdit) {
100     this.showEditor = false;
101     this.isEdit = isEdit;
102     this.isReply = !isEdit;
103     this.showEditor = true;
104   },
105   commentReplied: function (event, comment) {
106     this.comments.push(comment);
107     this.showEditor = false;
108   },
109   commentEdited: function (event, comment) {
110     this.comment = comment;
111     this.showEditor = false;
112   },
113   commentAdded: function (event, comment) {
114     // this is to handle non-parent child relationship
115     // we want to make it go up.
116     this.$emit('comment-added', event);
117   },
118   canEditOrDelete: function (prop) {
119     if (!this.comment.active) {
120       return false;
121     }
122
123     if (!this.permissions) {
124       return false;
125     }
126
127     let propAll = 'comment_' + prop + '_all';
128     let propOwn = 'comment_' + prop + '_own';
129
130     if (this.permissions[propAll]) {
131         return true;
132     }
133
134     if (this.permissions[propOwn] && this.comment.created_by.id === this.currentUserId) {
135         return true;
136     }
137
138     return false;
139   },
140   canComment: function () {
141     if (!this.permissions) {
142       return false;
143     }
144     return this.permissions.comment_create === true;
145   }
146 };
147
148 const computed = {
149   commentId: {
150     get: function () {
151       return `comment-${this.comment.page_id}-${this.comment.id}`;
152     },
153     set: function () {
154       this.commentHref = `#?cm=${this.commentId}`
155     }
156   }
157 };
158
159 function mounted () {
160   if (this.comment.sub_comments && this.comment.sub_comments.length) {
161     // set this so that we can render the next set of sub comments.
162     this.comments = this.comment.sub_comments;
163   }
164 }
165
166 function isCommentOpSuccess(resp) {
167   if (resp && resp.data && resp.data.status === 'success') {
168       return true;
169   }
170   return false;
171 }
172
173 function updateComment(comment, resp, isDelete) {
174   comment.text = resp.comment.text;
175   comment.updated = resp.comment.updated;
176   comment.updated_by = resp.comment.updated_by;
177   comment.active = resp.comment.active;
178   if (isDelete && !resp.comment.active) {
179       comment.html = trans('entities.comment_deleted');
180   } else {
181       comment.html = resp.comment.html;
182   }
183 }
184
185 module.exports = {
186   name: 'comment',
187   template, data, props, methods, computed, mounted, components: {
188   commentReply
189 }};
190