##// END OF EJS Templates
comments: fixed attach general comments.
milka -
r4574:888f0c7f stable
parent child Browse files
Show More
@@ -1,1502 +1,1502 b''
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 var firefoxAnchorFix = function() {
19 var firefoxAnchorFix = function() {
20 // hack to make anchor links behave properly on firefox, in our inline
20 // hack to make anchor links behave properly on firefox, in our inline
21 // comments generation when comments are injected firefox is misbehaving
21 // comments generation when comments are injected firefox is misbehaving
22 // when jumping to anchor links
22 // when jumping to anchor links
23 if (location.href.indexOf('#') > -1) {
23 if (location.href.indexOf('#') > -1) {
24 location.href += '';
24 location.href += '';
25 }
25 }
26 };
26 };
27
27
28 var linkifyComments = function(comments) {
28 var linkifyComments = function(comments) {
29 var firstCommentId = null;
29 var firstCommentId = null;
30 if (comments) {
30 if (comments) {
31 firstCommentId = $(comments[0]).data('comment-id');
31 firstCommentId = $(comments[0]).data('comment-id');
32 }
32 }
33
33
34 if (firstCommentId){
34 if (firstCommentId){
35 $('#inline-comments-counter').attr('href', '#comment-' + firstCommentId);
35 $('#inline-comments-counter').attr('href', '#comment-' + firstCommentId);
36 }
36 }
37 };
37 };
38
38
39 var bindToggleButtons = function() {
39 var bindToggleButtons = function() {
40 $('.comment-toggle').on('click', function() {
40 $('.comment-toggle').on('click', function() {
41 $(this).parent().nextUntil('tr.line').toggle('inline-comments');
41 $(this).parent().nextUntil('tr.line').toggle('inline-comments');
42 });
42 });
43 };
43 };
44
44
45
45
46
46
47 var _submitAjaxPOST = function(url, postData, successHandler, failHandler) {
47 var _submitAjaxPOST = function(url, postData, successHandler, failHandler) {
48 failHandler = failHandler || function() {};
48 failHandler = failHandler || function() {};
49 postData = toQueryString(postData);
49 postData = toQueryString(postData);
50 var request = $.ajax({
50 var request = $.ajax({
51 url: url,
51 url: url,
52 type: 'POST',
52 type: 'POST',
53 data: postData,
53 data: postData,
54 headers: {'X-PARTIAL-XHR': true}
54 headers: {'X-PARTIAL-XHR': true}
55 })
55 })
56 .done(function (data) {
56 .done(function (data) {
57 successHandler(data);
57 successHandler(data);
58 })
58 })
59 .fail(function (data, textStatus, errorThrown) {
59 .fail(function (data, textStatus, errorThrown) {
60 failHandler(data, textStatus, errorThrown)
60 failHandler(data, textStatus, errorThrown)
61 });
61 });
62 return request;
62 return request;
63 };
63 };
64
64
65
65
66
66
67
67
68 /* Comment form for main and inline comments */
68 /* Comment form for main and inline comments */
69 (function(mod) {
69 (function(mod) {
70
70
71 if (typeof exports == "object" && typeof module == "object") {
71 if (typeof exports == "object" && typeof module == "object") {
72 // CommonJS
72 // CommonJS
73 module.exports = mod();
73 module.exports = mod();
74 }
74 }
75 else {
75 else {
76 // Plain browser env
76 // Plain browser env
77 (this || window).CommentForm = mod();
77 (this || window).CommentForm = mod();
78 }
78 }
79
79
80 })(function() {
80 })(function() {
81 "use strict";
81 "use strict";
82
82
83 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId, edit, comment_id) {
83 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId, edit, comment_id) {
84
84
85 if (!(this instanceof CommentForm)) {
85 if (!(this instanceof CommentForm)) {
86 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId, edit, comment_id);
86 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId, edit, comment_id);
87 }
87 }
88
88
89 // bind the element instance to our Form
89 // bind the element instance to our Form
90 $(formElement).get(0).CommentForm = this;
90 $(formElement).get(0).CommentForm = this;
91
91
92 this.withLineNo = function(selector) {
92 this.withLineNo = function(selector) {
93 var lineNo = this.lineNo;
93 var lineNo = this.lineNo;
94 if (lineNo === undefined) {
94 if (lineNo === undefined) {
95 return selector
95 return selector
96 } else {
96 } else {
97 return selector + '_' + lineNo;
97 return selector + '_' + lineNo;
98 }
98 }
99 };
99 };
100
100
101 this.commitId = commitId;
101 this.commitId = commitId;
102 this.pullRequestId = pullRequestId;
102 this.pullRequestId = pullRequestId;
103 this.lineNo = lineNo;
103 this.lineNo = lineNo;
104 this.initAutocompleteActions = initAutocompleteActions;
104 this.initAutocompleteActions = initAutocompleteActions;
105
105
106 this.previewButton = this.withLineNo('#preview-btn');
106 this.previewButton = this.withLineNo('#preview-btn');
107 this.previewContainer = this.withLineNo('#preview-container');
107 this.previewContainer = this.withLineNo('#preview-container');
108
108
109 this.previewBoxSelector = this.withLineNo('#preview-box');
109 this.previewBoxSelector = this.withLineNo('#preview-box');
110
110
111 this.editButton = this.withLineNo('#edit-btn');
111 this.editButton = this.withLineNo('#edit-btn');
112 this.editContainer = this.withLineNo('#edit-container');
112 this.editContainer = this.withLineNo('#edit-container');
113 this.cancelButton = this.withLineNo('#cancel-btn');
113 this.cancelButton = this.withLineNo('#cancel-btn');
114 this.commentType = this.withLineNo('#comment_type');
114 this.commentType = this.withLineNo('#comment_type');
115
115
116 this.resolvesId = null;
116 this.resolvesId = null;
117 this.resolvesActionId = null;
117 this.resolvesActionId = null;
118
118
119 this.closesPr = '#close_pull_request';
119 this.closesPr = '#close_pull_request';
120
120
121 this.cmBox = this.withLineNo('#text');
121 this.cmBox = this.withLineNo('#text');
122 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
122 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
123
123
124 this.statusChange = this.withLineNo('#change_status');
124 this.statusChange = this.withLineNo('#change_status');
125
125
126 this.submitForm = formElement;
126 this.submitForm = formElement;
127
127
128 this.submitButton = $(this.submitForm).find('.submit-comment-action');
128 this.submitButton = $(this.submitForm).find('.submit-comment-action');
129 this.submitButtonText = this.submitButton.val();
129 this.submitButtonText = this.submitButton.val();
130
130
131 this.submitDraftButton = $(this.submitForm).find('.submit-draft-action');
131 this.submitDraftButton = $(this.submitForm).find('.submit-draft-action');
132 this.submitDraftButtonText = this.submitDraftButton.val();
132 this.submitDraftButtonText = this.submitDraftButton.val();
133
133
134 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
134 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
135 {'repo_name': templateContext.repo_name,
135 {'repo_name': templateContext.repo_name,
136 'commit_id': templateContext.commit_data.commit_id});
136 'commit_id': templateContext.commit_data.commit_id});
137
137
138 if (edit){
138 if (edit){
139 this.submitDraftButton.hide();
139 this.submitDraftButton.hide();
140 this.submitButtonText = _gettext('Update Comment');
140 this.submitButtonText = _gettext('Update Comment');
141 $(this.commentType).prop('disabled', true);
141 $(this.commentType).prop('disabled', true);
142 $(this.commentType).addClass('disabled');
142 $(this.commentType).addClass('disabled');
143 var editInfo =
143 var editInfo =
144 '';
144 '';
145 $(editInfo).insertBefore($(this.editButton).parent());
145 $(editInfo).insertBefore($(this.editButton).parent());
146 }
146 }
147
147
148 if (resolvesCommentId){
148 if (resolvesCommentId){
149 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
149 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
150 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
150 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
151 $(this.commentType).prop('disabled', true);
151 $(this.commentType).prop('disabled', true);
152 $(this.commentType).addClass('disabled');
152 $(this.commentType).addClass('disabled');
153
153
154 // disable select
154 // disable select
155 setTimeout(function() {
155 setTimeout(function() {
156 $(self.statusChange).select2('readonly', true);
156 $(self.statusChange).select2('readonly', true);
157 }, 10);
157 }, 10);
158
158
159 var resolvedInfo = (
159 var resolvedInfo = (
160 '<li class="resolve-action">' +
160 '<li class="resolve-action">' +
161 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
161 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
162 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
162 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
163 '</li>'
163 '</li>'
164 ).format(resolvesCommentId, _gettext('resolve comment'));
164 ).format(resolvesCommentId, _gettext('resolve comment'));
165 $(resolvedInfo).insertAfter($(this.commentType).parent());
165 $(resolvedInfo).insertAfter($(this.commentType).parent());
166 }
166 }
167
167
168 // based on commitId, or pullRequestId decide where do we submit
168 // based on commitId, or pullRequestId decide where do we submit
169 // out data
169 // out data
170 if (this.commitId){
170 if (this.commitId){
171 var pyurl = 'repo_commit_comment_create';
171 var pyurl = 'repo_commit_comment_create';
172 if(edit){
172 if(edit){
173 pyurl = 'repo_commit_comment_edit';
173 pyurl = 'repo_commit_comment_edit';
174 }
174 }
175 this.submitUrl = pyroutes.url(pyurl,
175 this.submitUrl = pyroutes.url(pyurl,
176 {'repo_name': templateContext.repo_name,
176 {'repo_name': templateContext.repo_name,
177 'commit_id': this.commitId,
177 'commit_id': this.commitId,
178 'comment_id': comment_id});
178 'comment_id': comment_id});
179 this.selfUrl = pyroutes.url('repo_commit',
179 this.selfUrl = pyroutes.url('repo_commit',
180 {'repo_name': templateContext.repo_name,
180 {'repo_name': templateContext.repo_name,
181 'commit_id': this.commitId});
181 'commit_id': this.commitId});
182
182
183 } else if (this.pullRequestId) {
183 } else if (this.pullRequestId) {
184 var pyurl = 'pullrequest_comment_create';
184 var pyurl = 'pullrequest_comment_create';
185 if(edit){
185 if(edit){
186 pyurl = 'pullrequest_comment_edit';
186 pyurl = 'pullrequest_comment_edit';
187 }
187 }
188 this.submitUrl = pyroutes.url(pyurl,
188 this.submitUrl = pyroutes.url(pyurl,
189 {'repo_name': templateContext.repo_name,
189 {'repo_name': templateContext.repo_name,
190 'pull_request_id': this.pullRequestId,
190 'pull_request_id': this.pullRequestId,
191 'comment_id': comment_id});
191 'comment_id': comment_id});
192 this.selfUrl = pyroutes.url('pullrequest_show',
192 this.selfUrl = pyroutes.url('pullrequest_show',
193 {'repo_name': templateContext.repo_name,
193 {'repo_name': templateContext.repo_name,
194 'pull_request_id': this.pullRequestId});
194 'pull_request_id': this.pullRequestId});
195
195
196 } else {
196 } else {
197 throw new Error(
197 throw new Error(
198 'CommentForm requires pullRequestId, or commitId to be specified.')
198 'CommentForm requires pullRequestId, or commitId to be specified.')
199 }
199 }
200
200
201 // FUNCTIONS and helpers
201 // FUNCTIONS and helpers
202 var self = this;
202 var self = this;
203
203
204 this.isInline = function(){
204 this.isInline = function(){
205 return this.lineNo && this.lineNo != 'general';
205 return this.lineNo && this.lineNo != 'general';
206 };
206 };
207
207
208 this.getCmInstance = function(){
208 this.getCmInstance = function(){
209 return this.cm
209 return this.cm
210 };
210 };
211
211
212 this.setPlaceholder = function(placeholder) {
212 this.setPlaceholder = function(placeholder) {
213 var cm = this.getCmInstance();
213 var cm = this.getCmInstance();
214 if (cm){
214 if (cm){
215 cm.setOption('placeholder', placeholder);
215 cm.setOption('placeholder', placeholder);
216 }
216 }
217 };
217 };
218
218
219 this.getCommentStatus = function() {
219 this.getCommentStatus = function() {
220 return $(this.submitForm).find(this.statusChange).val();
220 return $(this.submitForm).find(this.statusChange).val();
221 };
221 };
222
222
223 this.getCommentType = function() {
223 this.getCommentType = function() {
224 return $(this.submitForm).find(this.commentType).val();
224 return $(this.submitForm).find(this.commentType).val();
225 };
225 };
226
226
227 this.getDraftState = function () {
227 this.getDraftState = function () {
228 var submitterElem = $(this.submitForm).find('input[type="submit"].submitter');
228 var submitterElem = $(this.submitForm).find('input[type="submit"].submitter');
229 var data = $(submitterElem).data('isDraft');
229 var data = $(submitterElem).data('isDraft');
230 return data
230 return data
231 }
231 }
232
232
233 this.getResolvesId = function() {
233 this.getResolvesId = function() {
234 return $(this.submitForm).find(this.resolvesId).val() || null;
234 return $(this.submitForm).find(this.resolvesId).val() || null;
235 };
235 };
236
236
237 this.getClosePr = function() {
237 this.getClosePr = function() {
238 return $(this.submitForm).find(this.closesPr).val() || null;
238 return $(this.submitForm).find(this.closesPr).val() || null;
239 };
239 };
240
240
241 this.markCommentResolved = function(resolvedCommentId){
241 this.markCommentResolved = function(resolvedCommentId){
242 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
242 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
243 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
243 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
244 };
244 };
245
245
246 this.isAllowedToSubmit = function() {
246 this.isAllowedToSubmit = function() {
247 var commentDisabled = $(this.submitButton).prop('disabled');
247 var commentDisabled = $(this.submitButton).prop('disabled');
248 var draftDisabled = $(this.submitDraftButton).prop('disabled');
248 var draftDisabled = $(this.submitDraftButton).prop('disabled');
249 return !commentDisabled && !draftDisabled;
249 return !commentDisabled && !draftDisabled;
250 };
250 };
251
251
252 this.initStatusChangeSelector = function(){
252 this.initStatusChangeSelector = function(){
253 var formatChangeStatus = function(state, escapeMarkup) {
253 var formatChangeStatus = function(state, escapeMarkup) {
254 var originalOption = state.element;
254 var originalOption = state.element;
255 var tmpl = '<i class="icon-circle review-status-{0}"></i><span>{1}</span>'.format($(originalOption).data('status'), escapeMarkup(state.text));
255 var tmpl = '<i class="icon-circle review-status-{0}"></i><span>{1}</span>'.format($(originalOption).data('status'), escapeMarkup(state.text));
256 return tmpl
256 return tmpl
257 };
257 };
258 var formatResult = function(result, container, query, escapeMarkup) {
258 var formatResult = function(result, container, query, escapeMarkup) {
259 return formatChangeStatus(result, escapeMarkup);
259 return formatChangeStatus(result, escapeMarkup);
260 };
260 };
261
261
262 var formatSelection = function(data, container, escapeMarkup) {
262 var formatSelection = function(data, container, escapeMarkup) {
263 return formatChangeStatus(data, escapeMarkup);
263 return formatChangeStatus(data, escapeMarkup);
264 };
264 };
265
265
266 $(this.submitForm).find(this.statusChange).select2({
266 $(this.submitForm).find(this.statusChange).select2({
267 placeholder: _gettext('Status Review'),
267 placeholder: _gettext('Status Review'),
268 formatResult: formatResult,
268 formatResult: formatResult,
269 formatSelection: formatSelection,
269 formatSelection: formatSelection,
270 containerCssClass: "drop-menu status_box_menu",
270 containerCssClass: "drop-menu status_box_menu",
271 dropdownCssClass: "drop-menu-dropdown",
271 dropdownCssClass: "drop-menu-dropdown",
272 dropdownAutoWidth: true,
272 dropdownAutoWidth: true,
273 minimumResultsForSearch: -1
273 minimumResultsForSearch: -1
274 });
274 });
275
275
276 $(this.submitForm).find(this.statusChange).on('change', function() {
276 $(this.submitForm).find(this.statusChange).on('change', function() {
277 var status = self.getCommentStatus();
277 var status = self.getCommentStatus();
278
278
279 if (status && !self.isInline()) {
279 if (status && !self.isInline()) {
280 $(self.submitButton).prop('disabled', false);
280 $(self.submitButton).prop('disabled', false);
281 $(self.submitDraftButton).prop('disabled', false);
281 $(self.submitDraftButton).prop('disabled', false);
282 }
282 }
283
283
284 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
284 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
285 self.setPlaceholder(placeholderText)
285 self.setPlaceholder(placeholderText)
286 })
286 })
287 };
287 };
288
288
289 // reset the comment form into it's original state
289 // reset the comment form into it's original state
290 this.resetCommentFormState = function(content) {
290 this.resetCommentFormState = function(content) {
291 content = content || '';
291 content = content || '';
292
292
293 $(this.editContainer).show();
293 $(this.editContainer).show();
294 $(this.editButton).parent().addClass('active');
294 $(this.editButton).parent().addClass('active');
295
295
296 $(this.previewContainer).hide();
296 $(this.previewContainer).hide();
297 $(this.previewButton).parent().removeClass('active');
297 $(this.previewButton).parent().removeClass('active');
298
298
299 this.setActionButtonsDisabled(true);
299 this.setActionButtonsDisabled(true);
300 self.cm.setValue(content);
300 self.cm.setValue(content);
301 self.cm.setOption("readOnly", false);
301 self.cm.setOption("readOnly", false);
302
302
303 if (this.resolvesId) {
303 if (this.resolvesId) {
304 // destroy the resolve action
304 // destroy the resolve action
305 $(this.resolvesId).parent().remove();
305 $(this.resolvesId).parent().remove();
306 }
306 }
307 // reset closingPR flag
307 // reset closingPR flag
308 $('.close-pr-input').remove();
308 $('.close-pr-input').remove();
309
309
310 $(this.statusChange).select2('readonly', false);
310 $(this.statusChange).select2('readonly', false);
311 };
311 };
312
312
313 this.globalSubmitSuccessCallback = function(comment){
313 this.globalSubmitSuccessCallback = function(comment){
314 // default behaviour is to call GLOBAL hook, if it's registered.
314 // default behaviour is to call GLOBAL hook, if it's registered.
315 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
315 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
316 commentFormGlobalSubmitSuccessCallback(comment);
316 commentFormGlobalSubmitSuccessCallback(comment);
317 }
317 }
318 };
318 };
319
319
320 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
320 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
321 return _submitAjaxPOST(url, postData, successHandler, failHandler);
321 return _submitAjaxPOST(url, postData, successHandler, failHandler);
322 };
322 };
323
323
324 // overwrite a submitHandler, we need to do it for inline comments
324 // overwrite a submitHandler, we need to do it for inline comments
325 this.setHandleFormSubmit = function(callback) {
325 this.setHandleFormSubmit = function(callback) {
326 this.handleFormSubmit = callback;
326 this.handleFormSubmit = callback;
327 };
327 };
328
328
329 // overwrite a submitSuccessHandler
329 // overwrite a submitSuccessHandler
330 this.setGlobalSubmitSuccessCallback = function(callback) {
330 this.setGlobalSubmitSuccessCallback = function(callback) {
331 this.globalSubmitSuccessCallback = callback;
331 this.globalSubmitSuccessCallback = callback;
332 };
332 };
333
333
334 // default handler for for submit for main comments
334 // default handler for for submit for main comments
335 this.handleFormSubmit = function() {
335 this.handleFormSubmit = function() {
336 var text = self.cm.getValue();
336 var text = self.cm.getValue();
337 var status = self.getCommentStatus();
337 var status = self.getCommentStatus();
338 var commentType = self.getCommentType();
338 var commentType = self.getCommentType();
339 var isDraft = self.getDraftState();
339 var isDraft = self.getDraftState();
340 var resolvesCommentId = self.getResolvesId();
340 var resolvesCommentId = self.getResolvesId();
341 var closePullRequest = self.getClosePr();
341 var closePullRequest = self.getClosePr();
342
342
343 if (text === "" && !status) {
343 if (text === "" && !status) {
344 return;
344 return;
345 }
345 }
346
346
347 var excludeCancelBtn = false;
347 var excludeCancelBtn = false;
348 var submitEvent = true;
348 var submitEvent = true;
349 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
349 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
350 self.cm.setOption("readOnly", true);
350 self.cm.setOption("readOnly", true);
351
351
352 var postData = {
352 var postData = {
353 'text': text,
353 'text': text,
354 'changeset_status': status,
354 'changeset_status': status,
355 'comment_type': commentType,
355 'comment_type': commentType,
356 'csrf_token': CSRF_TOKEN
356 'csrf_token': CSRF_TOKEN
357 };
357 };
358
358
359 if (resolvesCommentId) {
359 if (resolvesCommentId) {
360 postData['resolves_comment_id'] = resolvesCommentId;
360 postData['resolves_comment_id'] = resolvesCommentId;
361 }
361 }
362
362
363 if (closePullRequest) {
363 if (closePullRequest) {
364 postData['close_pull_request'] = true;
364 postData['close_pull_request'] = true;
365 }
365 }
366
366
367 // submitSuccess for general comments
367 // submitSuccess for general comments
368 var submitSuccessCallback = function(json_data) {
368 var submitSuccessCallback = function(json_data) {
369 // reload page if we change status for single commit.
369 // reload page if we change status for single commit.
370 if (status && self.commitId) {
370 if (status && self.commitId) {
371 location.reload(true);
371 location.reload(true);
372 } else {
372 } else {
373 // inject newly created comments, json_data is {<comment_id>: {}}
373 // inject newly created comments, json_data is {<comment_id>: {}}
374 self.attachGeneralComment(json_data)
374 Rhodecode.comments.attachGeneralComment(json_data)
375
375
376 self.resetCommentFormState();
376 self.resetCommentFormState();
377 timeagoActivate();
377 timeagoActivate();
378 tooltipActivate();
378 tooltipActivate();
379
379
380 // mark visually which comment was resolved
380 // mark visually which comment was resolved
381 if (resolvesCommentId) {
381 if (resolvesCommentId) {
382 self.markCommentResolved(resolvesCommentId);
382 self.markCommentResolved(resolvesCommentId);
383 }
383 }
384 }
384 }
385
385
386 // run global callback on submit
386 // run global callback on submit
387 self.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id});
387 self.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id});
388
388
389 };
389 };
390 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
390 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
391 var prefix = "Error while submitting comment.\n"
391 var prefix = "Error while submitting comment.\n"
392 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
392 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
393 ajaxErrorSwal(message);
393 ajaxErrorSwal(message);
394 self.resetCommentFormState(text);
394 self.resetCommentFormState(text);
395 };
395 };
396 self.submitAjaxPOST(
396 self.submitAjaxPOST(
397 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
397 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
398 };
398 };
399
399
400 this.previewSuccessCallback = function(o) {
400 this.previewSuccessCallback = function(o) {
401 $(self.previewBoxSelector).html(o);
401 $(self.previewBoxSelector).html(o);
402 $(self.previewBoxSelector).removeClass('unloaded');
402 $(self.previewBoxSelector).removeClass('unloaded');
403
403
404 // swap buttons, making preview active
404 // swap buttons, making preview active
405 $(self.previewButton).parent().addClass('active');
405 $(self.previewButton).parent().addClass('active');
406 $(self.editButton).parent().removeClass('active');
406 $(self.editButton).parent().removeClass('active');
407
407
408 // unlock buttons
408 // unlock buttons
409 self.setActionButtonsDisabled(false);
409 self.setActionButtonsDisabled(false);
410 };
410 };
411
411
412 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
412 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
413 excludeCancelBtn = excludeCancelBtn || false;
413 excludeCancelBtn = excludeCancelBtn || false;
414 submitEvent = submitEvent || false;
414 submitEvent = submitEvent || false;
415
415
416 $(this.editButton).prop('disabled', state);
416 $(this.editButton).prop('disabled', state);
417 $(this.previewButton).prop('disabled', state);
417 $(this.previewButton).prop('disabled', state);
418
418
419 if (!excludeCancelBtn) {
419 if (!excludeCancelBtn) {
420 $(this.cancelButton).prop('disabled', state);
420 $(this.cancelButton).prop('disabled', state);
421 }
421 }
422
422
423 var submitState = state;
423 var submitState = state;
424 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
424 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
425 // if the value of commit review status is set, we allow
425 // if the value of commit review status is set, we allow
426 // submit button, but only on Main form, isInline means inline
426 // submit button, but only on Main form, isInline means inline
427 submitState = false
427 submitState = false
428 }
428 }
429
429
430 $(this.submitButton).prop('disabled', submitState);
430 $(this.submitButton).prop('disabled', submitState);
431 $(this.submitDraftButton).prop('disabled', submitState);
431 $(this.submitDraftButton).prop('disabled', submitState);
432
432
433 if (submitEvent) {
433 if (submitEvent) {
434 var isDraft = self.getDraftState();
434 var isDraft = self.getDraftState();
435
435
436 if (isDraft) {
436 if (isDraft) {
437 $(this.submitDraftButton).val(_gettext('Saving Draft...'));
437 $(this.submitDraftButton).val(_gettext('Saving Draft...'));
438 } else {
438 } else {
439 $(this.submitButton).val(_gettext('Submitting...'));
439 $(this.submitButton).val(_gettext('Submitting...'));
440 }
440 }
441
441
442 } else {
442 } else {
443 $(this.submitButton).val(this.submitButtonText);
443 $(this.submitButton).val(this.submitButtonText);
444 $(this.submitDraftButton).val(this.submitDraftButtonText);
444 $(this.submitDraftButton).val(this.submitDraftButtonText);
445 }
445 }
446
446
447 };
447 };
448
448
449 // lock preview/edit/submit buttons on load, but exclude cancel button
449 // lock preview/edit/submit buttons on load, but exclude cancel button
450 var excludeCancelBtn = true;
450 var excludeCancelBtn = true;
451 this.setActionButtonsDisabled(true, excludeCancelBtn);
451 this.setActionButtonsDisabled(true, excludeCancelBtn);
452
452
453 // anonymous users don't have access to initialized CM instance
453 // anonymous users don't have access to initialized CM instance
454 if (this.cm !== undefined){
454 if (this.cm !== undefined){
455 this.cm.on('change', function(cMirror) {
455 this.cm.on('change', function(cMirror) {
456 if (cMirror.getValue() === "") {
456 if (cMirror.getValue() === "") {
457 self.setActionButtonsDisabled(true, excludeCancelBtn)
457 self.setActionButtonsDisabled(true, excludeCancelBtn)
458 } else {
458 } else {
459 self.setActionButtonsDisabled(false, excludeCancelBtn)
459 self.setActionButtonsDisabled(false, excludeCancelBtn)
460 }
460 }
461 });
461 });
462 }
462 }
463
463
464 $(this.editButton).on('click', function(e) {
464 $(this.editButton).on('click', function(e) {
465 e.preventDefault();
465 e.preventDefault();
466
466
467 $(self.previewButton).parent().removeClass('active');
467 $(self.previewButton).parent().removeClass('active');
468 $(self.previewContainer).hide();
468 $(self.previewContainer).hide();
469
469
470 $(self.editButton).parent().addClass('active');
470 $(self.editButton).parent().addClass('active');
471 $(self.editContainer).show();
471 $(self.editContainer).show();
472
472
473 });
473 });
474
474
475 $(this.previewButton).on('click', function(e) {
475 $(this.previewButton).on('click', function(e) {
476 e.preventDefault();
476 e.preventDefault();
477 var text = self.cm.getValue();
477 var text = self.cm.getValue();
478
478
479 if (text === "") {
479 if (text === "") {
480 return;
480 return;
481 }
481 }
482
482
483 var postData = {
483 var postData = {
484 'text': text,
484 'text': text,
485 'renderer': templateContext.visual.default_renderer,
485 'renderer': templateContext.visual.default_renderer,
486 'csrf_token': CSRF_TOKEN
486 'csrf_token': CSRF_TOKEN
487 };
487 };
488
488
489 // lock ALL buttons on preview
489 // lock ALL buttons on preview
490 self.setActionButtonsDisabled(true);
490 self.setActionButtonsDisabled(true);
491
491
492 $(self.previewBoxSelector).addClass('unloaded');
492 $(self.previewBoxSelector).addClass('unloaded');
493 $(self.previewBoxSelector).html(_gettext('Loading ...'));
493 $(self.previewBoxSelector).html(_gettext('Loading ...'));
494
494
495 $(self.editContainer).hide();
495 $(self.editContainer).hide();
496 $(self.previewContainer).show();
496 $(self.previewContainer).show();
497
497
498 // by default we reset state of comment preserving the text
498 // by default we reset state of comment preserving the text
499 var previewFailCallback = function(jqXHR, textStatus, errorThrown) {
499 var previewFailCallback = function(jqXHR, textStatus, errorThrown) {
500 var prefix = "Error while preview of comment.\n"
500 var prefix = "Error while preview of comment.\n"
501 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
501 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
502 ajaxErrorSwal(message);
502 ajaxErrorSwal(message);
503
503
504 self.resetCommentFormState(text)
504 self.resetCommentFormState(text)
505 };
505 };
506 self.submitAjaxPOST(
506 self.submitAjaxPOST(
507 self.previewUrl, postData, self.previewSuccessCallback,
507 self.previewUrl, postData, self.previewSuccessCallback,
508 previewFailCallback);
508 previewFailCallback);
509
509
510 $(self.previewButton).parent().addClass('active');
510 $(self.previewButton).parent().addClass('active');
511 $(self.editButton).parent().removeClass('active');
511 $(self.editButton).parent().removeClass('active');
512 });
512 });
513
513
514 $(this.submitForm).submit(function(e) {
514 $(this.submitForm).submit(function(e) {
515 e.preventDefault();
515 e.preventDefault();
516 var allowedToSubmit = self.isAllowedToSubmit();
516 var allowedToSubmit = self.isAllowedToSubmit();
517 if (!allowedToSubmit){
517 if (!allowedToSubmit){
518 return false;
518 return false;
519 }
519 }
520
520
521 self.handleFormSubmit();
521 self.handleFormSubmit();
522 });
522 });
523
523
524 }
524 }
525
525
526 return CommentForm;
526 return CommentForm;
527 });
527 });
528
528
529 /* selector for comment versions */
529 /* selector for comment versions */
530 var initVersionSelector = function(selector, initialData) {
530 var initVersionSelector = function(selector, initialData) {
531
531
532 var formatResult = function(result, container, query, escapeMarkup) {
532 var formatResult = function(result, container, query, escapeMarkup) {
533
533
534 return renderTemplate('commentVersion', {
534 return renderTemplate('commentVersion', {
535 show_disabled: true,
535 show_disabled: true,
536 version: result.comment_version,
536 version: result.comment_version,
537 user_name: result.comment_author_username,
537 user_name: result.comment_author_username,
538 gravatar_url: result.comment_author_gravatar,
538 gravatar_url: result.comment_author_gravatar,
539 size: 16,
539 size: 16,
540 timeago_component: result.comment_created_on,
540 timeago_component: result.comment_created_on,
541 })
541 })
542 };
542 };
543
543
544 $(selector).select2({
544 $(selector).select2({
545 placeholder: "Edited",
545 placeholder: "Edited",
546 containerCssClass: "drop-menu-comment-history",
546 containerCssClass: "drop-menu-comment-history",
547 dropdownCssClass: "drop-menu-dropdown",
547 dropdownCssClass: "drop-menu-dropdown",
548 dropdownAutoWidth: true,
548 dropdownAutoWidth: true,
549 minimumResultsForSearch: -1,
549 minimumResultsForSearch: -1,
550 data: initialData,
550 data: initialData,
551 formatResult: formatResult,
551 formatResult: formatResult,
552 });
552 });
553
553
554 $(selector).on('select2-selecting', function (e) {
554 $(selector).on('select2-selecting', function (e) {
555 // hide the mast as we later do preventDefault()
555 // hide the mast as we later do preventDefault()
556 $("#select2-drop-mask").click();
556 $("#select2-drop-mask").click();
557 e.preventDefault();
557 e.preventDefault();
558 e.choice.action();
558 e.choice.action();
559 });
559 });
560
560
561 $(selector).on("select2-open", function() {
561 $(selector).on("select2-open", function() {
562 timeagoActivate();
562 timeagoActivate();
563 });
563 });
564 };
564 };
565
565
566 /* comments controller */
566 /* comments controller */
567 var CommentsController = function() {
567 var CommentsController = function() {
568 var mainComment = '#text';
568 var mainComment = '#text';
569 var self = this;
569 var self = this;
570
570
571 this.showVersion = function (comment_id, comment_history_id) {
571 this.showVersion = function (comment_id, comment_history_id) {
572
572
573 var historyViewUrl = pyroutes.url(
573 var historyViewUrl = pyroutes.url(
574 'repo_commit_comment_history_view',
574 'repo_commit_comment_history_view',
575 {
575 {
576 'repo_name': templateContext.repo_name,
576 'repo_name': templateContext.repo_name,
577 'commit_id': comment_id,
577 'commit_id': comment_id,
578 'comment_history_id': comment_history_id,
578 'comment_history_id': comment_history_id,
579 }
579 }
580 );
580 );
581 successRenderCommit = function (data) {
581 successRenderCommit = function (data) {
582 SwalNoAnimation.fire({
582 SwalNoAnimation.fire({
583 html: data,
583 html: data,
584 title: '',
584 title: '',
585 });
585 });
586 };
586 };
587 failRenderCommit = function () {
587 failRenderCommit = function () {
588 SwalNoAnimation.fire({
588 SwalNoAnimation.fire({
589 html: 'Error while loading comment history',
589 html: 'Error while loading comment history',
590 title: '',
590 title: '',
591 });
591 });
592 };
592 };
593 _submitAjaxPOST(
593 _submitAjaxPOST(
594 historyViewUrl, {'csrf_token': CSRF_TOKEN},
594 historyViewUrl, {'csrf_token': CSRF_TOKEN},
595 successRenderCommit,
595 successRenderCommit,
596 failRenderCommit
596 failRenderCommit
597 );
597 );
598 };
598 };
599
599
600 this.getLineNumber = function(node) {
600 this.getLineNumber = function(node) {
601 var $node = $(node);
601 var $node = $(node);
602 var lineNo = $node.closest('td').attr('data-line-no');
602 var lineNo = $node.closest('td').attr('data-line-no');
603 if (lineNo === undefined && $node.data('commentInline')){
603 if (lineNo === undefined && $node.data('commentInline')){
604 lineNo = $node.data('commentLineNo')
604 lineNo = $node.data('commentLineNo')
605 }
605 }
606
606
607 return lineNo
607 return lineNo
608 };
608 };
609
609
610 this.scrollToComment = function(node, offset, outdated) {
610 this.scrollToComment = function(node, offset, outdated) {
611 if (offset === undefined) {
611 if (offset === undefined) {
612 offset = 0;
612 offset = 0;
613 }
613 }
614 var outdated = outdated || false;
614 var outdated = outdated || false;
615 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
615 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
616
616
617 if (!node) {
617 if (!node) {
618 node = $('.comment-selected');
618 node = $('.comment-selected');
619 if (!node.length) {
619 if (!node.length) {
620 node = $('comment-current')
620 node = $('comment-current')
621 }
621 }
622 }
622 }
623
623
624 $wrapper = $(node).closest('div.comment');
624 $wrapper = $(node).closest('div.comment');
625
625
626 // show hidden comment when referenced.
626 // show hidden comment when referenced.
627 if (!$wrapper.is(':visible')){
627 if (!$wrapper.is(':visible')){
628 $wrapper.show();
628 $wrapper.show();
629 }
629 }
630
630
631 $comment = $(node).closest(klass);
631 $comment = $(node).closest(klass);
632 $comments = $(klass);
632 $comments = $(klass);
633
633
634 $('.comment-selected').removeClass('comment-selected');
634 $('.comment-selected').removeClass('comment-selected');
635
635
636 var nextIdx = $(klass).index($comment) + offset;
636 var nextIdx = $(klass).index($comment) + offset;
637 if (nextIdx >= $comments.length) {
637 if (nextIdx >= $comments.length) {
638 nextIdx = 0;
638 nextIdx = 0;
639 }
639 }
640 var $next = $(klass).eq(nextIdx);
640 var $next = $(klass).eq(nextIdx);
641
641
642 var $cb = $next.closest('.cb');
642 var $cb = $next.closest('.cb');
643 $cb.removeClass('cb-collapsed');
643 $cb.removeClass('cb-collapsed');
644
644
645 var $filediffCollapseState = $cb.closest('.filediff').prev();
645 var $filediffCollapseState = $cb.closest('.filediff').prev();
646 $filediffCollapseState.prop('checked', false);
646 $filediffCollapseState.prop('checked', false);
647 $next.addClass('comment-selected');
647 $next.addClass('comment-selected');
648 scrollToElement($next);
648 scrollToElement($next);
649 return false;
649 return false;
650 };
650 };
651
651
652 this.nextComment = function(node) {
652 this.nextComment = function(node) {
653 return self.scrollToComment(node, 1);
653 return self.scrollToComment(node, 1);
654 };
654 };
655
655
656 this.prevComment = function(node) {
656 this.prevComment = function(node) {
657 return self.scrollToComment(node, -1);
657 return self.scrollToComment(node, -1);
658 };
658 };
659
659
660 this.nextOutdatedComment = function(node) {
660 this.nextOutdatedComment = function(node) {
661 return self.scrollToComment(node, 1, true);
661 return self.scrollToComment(node, 1, true);
662 };
662 };
663
663
664 this.prevOutdatedComment = function(node) {
664 this.prevOutdatedComment = function(node) {
665 return self.scrollToComment(node, -1, true);
665 return self.scrollToComment(node, -1, true);
666 };
666 };
667
667
668 this.cancelComment = function (node) {
668 this.cancelComment = function (node) {
669 var $node = $(node);
669 var $node = $(node);
670 var edit = $(this).attr('edit');
670 var edit = $(this).attr('edit');
671 var $inlineComments = $node.closest('div.inline-comments');
671 var $inlineComments = $node.closest('div.inline-comments');
672
672
673 if (edit) {
673 if (edit) {
674 var $general_comments = null;
674 var $general_comments = null;
675 if (!$inlineComments.length) {
675 if (!$inlineComments.length) {
676 $general_comments = $('#comments');
676 $general_comments = $('#comments');
677 var $comment = $general_comments.parent().find('div.comment:hidden');
677 var $comment = $general_comments.parent().find('div.comment:hidden');
678 // show hidden general comment form
678 // show hidden general comment form
679 $('#cb-comment-general-form-placeholder').show();
679 $('#cb-comment-general-form-placeholder').show();
680 } else {
680 } else {
681 var $comment = $inlineComments.find('div.comment:hidden');
681 var $comment = $inlineComments.find('div.comment:hidden');
682 }
682 }
683 $comment.show();
683 $comment.show();
684 }
684 }
685 var $replyWrapper = $node.closest('.comment-inline-form').closest('.reply-thread-container-wrapper')
685 var $replyWrapper = $node.closest('.comment-inline-form').closest('.reply-thread-container-wrapper')
686 $replyWrapper.removeClass('comment-form-active');
686 $replyWrapper.removeClass('comment-form-active');
687
687
688 var lastComment = $inlineComments.find('.comment-inline').last();
688 var lastComment = $inlineComments.find('.comment-inline').last();
689 if ($(lastComment).hasClass('comment-outdated')) {
689 if ($(lastComment).hasClass('comment-outdated')) {
690 $replyWrapper.hide();
690 $replyWrapper.hide();
691 }
691 }
692
692
693 $node.closest('.comment-inline-form').remove();
693 $node.closest('.comment-inline-form').remove();
694 return false;
694 return false;
695 };
695 };
696
696
697 this._deleteComment = function(node) {
697 this._deleteComment = function(node) {
698 var $node = $(node);
698 var $node = $(node);
699 var $td = $node.closest('td');
699 var $td = $node.closest('td');
700 var $comment = $node.closest('.comment');
700 var $comment = $node.closest('.comment');
701 var comment_id = $($comment).data('commentId');
701 var comment_id = $($comment).data('commentId');
702 var isDraft = $($comment).data('commentDraft');
702 var isDraft = $($comment).data('commentDraft');
703
703
704 var pullRequestId = templateContext.pull_request_data.pull_request_id;
704 var pullRequestId = templateContext.pull_request_data.pull_request_id;
705 var commitId = templateContext.commit_data.commit_id;
705 var commitId = templateContext.commit_data.commit_id;
706
706
707 if (pullRequestId) {
707 if (pullRequestId) {
708 var url = pyroutes.url('pullrequest_comment_delete', {"comment_id": comment_id, "repo_name": templateContext.repo_name, "pull_request_id": pullRequestId})
708 var url = pyroutes.url('pullrequest_comment_delete', {"comment_id": comment_id, "repo_name": templateContext.repo_name, "pull_request_id": pullRequestId})
709 } else if (commitId) {
709 } else if (commitId) {
710 var url = pyroutes.url('repo_commit_comment_delete', {"comment_id": comment_id, "repo_name": templateContext.repo_name, "commit_id": commitId})
710 var url = pyroutes.url('repo_commit_comment_delete', {"comment_id": comment_id, "repo_name": templateContext.repo_name, "commit_id": commitId})
711 }
711 }
712
712
713 var postData = {
713 var postData = {
714 'csrf_token': CSRF_TOKEN
714 'csrf_token': CSRF_TOKEN
715 };
715 };
716
716
717 $comment.addClass('comment-deleting');
717 $comment.addClass('comment-deleting');
718 $comment.hide('fast');
718 $comment.hide('fast');
719
719
720 var success = function(response) {
720 var success = function(response) {
721 $comment.remove();
721 $comment.remove();
722
722
723 if (window.updateSticky !== undefined) {
723 if (window.updateSticky !== undefined) {
724 // potentially our comments change the active window size, so we
724 // potentially our comments change the active window size, so we
725 // notify sticky elements
725 // notify sticky elements
726 updateSticky()
726 updateSticky()
727 }
727 }
728
728
729 if (window.refreshAllComments !== undefined && !isDraft) {
729 if (window.refreshAllComments !== undefined && !isDraft) {
730 // if we have this handler, run it, and refresh all comments boxes
730 // if we have this handler, run it, and refresh all comments boxes
731 refreshAllComments()
731 refreshAllComments()
732 }
732 }
733 else if (window.refreshDraftComments !== undefined && isDraft) {
733 else if (window.refreshDraftComments !== undefined && isDraft) {
734 // if we have this handler, run it, and refresh all comments boxes
734 // if we have this handler, run it, and refresh all comments boxes
735 refreshDraftComments();
735 refreshDraftComments();
736 }
736 }
737 return false;
737 return false;
738 };
738 };
739
739
740 var failure = function(jqXHR, textStatus, errorThrown) {
740 var failure = function(jqXHR, textStatus, errorThrown) {
741 var prefix = "Error while deleting this comment.\n"
741 var prefix = "Error while deleting this comment.\n"
742 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
742 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
743 ajaxErrorSwal(message);
743 ajaxErrorSwal(message);
744
744
745 $comment.show('fast');
745 $comment.show('fast');
746 $comment.removeClass('comment-deleting');
746 $comment.removeClass('comment-deleting');
747 return false;
747 return false;
748 };
748 };
749 ajaxPOST(url, postData, success, failure);
749 ajaxPOST(url, postData, success, failure);
750
750
751 }
751 }
752
752
753 this.deleteComment = function(node) {
753 this.deleteComment = function(node) {
754 var $comment = $(node).closest('.comment');
754 var $comment = $(node).closest('.comment');
755 var comment_id = $comment.attr('data-comment-id');
755 var comment_id = $comment.attr('data-comment-id');
756
756
757 SwalNoAnimation.fire({
757 SwalNoAnimation.fire({
758 title: 'Delete this comment?',
758 title: 'Delete this comment?',
759 icon: 'warning',
759 icon: 'warning',
760 showCancelButton: true,
760 showCancelButton: true,
761 confirmButtonText: _gettext('Yes, delete comment #{0}!').format(comment_id),
761 confirmButtonText: _gettext('Yes, delete comment #{0}!').format(comment_id),
762
762
763 }).then(function(result) {
763 }).then(function(result) {
764 if (result.value) {
764 if (result.value) {
765 self._deleteComment(node);
765 self._deleteComment(node);
766 }
766 }
767 })
767 })
768 };
768 };
769
769
770 this._finalizeDrafts = function(commentIds) {
770 this._finalizeDrafts = function(commentIds) {
771
771
772 var pullRequestId = templateContext.pull_request_data.pull_request_id;
772 var pullRequestId = templateContext.pull_request_data.pull_request_id;
773 var commitId = templateContext.commit_data.commit_id;
773 var commitId = templateContext.commit_data.commit_id;
774
774
775 if (pullRequestId) {
775 if (pullRequestId) {
776 var url = pyroutes.url('pullrequest_draft_comments_submit', {"repo_name": templateContext.repo_name, "pull_request_id": pullRequestId})
776 var url = pyroutes.url('pullrequest_draft_comments_submit', {"repo_name": templateContext.repo_name, "pull_request_id": pullRequestId})
777 } else if (commitId) {
777 } else if (commitId) {
778 var url = pyroutes.url('commit_draft_comments_submit', {"repo_name": templateContext.repo_name, "commit_id": commitId})
778 var url = pyroutes.url('commit_draft_comments_submit', {"repo_name": templateContext.repo_name, "commit_id": commitId})
779 }
779 }
780
780
781 // remove the drafts so we can lock them before submit.
781 // remove the drafts so we can lock them before submit.
782 $.each(commentIds, function(idx, val){
782 $.each(commentIds, function(idx, val){
783 $('#comment-{0}'.format(val)).remove();
783 $('#comment-{0}'.format(val)).remove();
784 })
784 })
785
785
786 var postData = {'comments': commentIds, 'csrf_token': CSRF_TOKEN};
786 var postData = {'comments': commentIds, 'csrf_token': CSRF_TOKEN};
787
787
788 var submitSuccessCallback = function(json_data) {
788 var submitSuccessCallback = function(json_data) {
789 self.attachInlineComment(json_data);
789 self.attachInlineComment(json_data);
790
790
791 if (window.refreshDraftComments !== undefined) {
791 if (window.refreshDraftComments !== undefined) {
792 // if we have this handler, run it, and refresh all comments boxes
792 // if we have this handler, run it, and refresh all comments boxes
793 refreshDraftComments()
793 refreshDraftComments()
794 }
794 }
795
795
796 return false;
796 return false;
797 };
797 };
798
798
799 ajaxPOST(url, postData, submitSuccessCallback)
799 ajaxPOST(url, postData, submitSuccessCallback)
800
800
801 }
801 }
802
802
803 this.finalizeDrafts = function(commentIds, callback) {
803 this.finalizeDrafts = function(commentIds, callback) {
804
804
805 SwalNoAnimation.fire({
805 SwalNoAnimation.fire({
806 title: _ngettext('Submit {0} draft comment.', 'Submit {0} draft comments.', commentIds.length).format(commentIds.length),
806 title: _ngettext('Submit {0} draft comment.', 'Submit {0} draft comments.', commentIds.length).format(commentIds.length),
807 icon: 'warning',
807 icon: 'warning',
808 showCancelButton: true,
808 showCancelButton: true,
809 confirmButtonText: _gettext('Yes'),
809 confirmButtonText: _gettext('Yes'),
810
810
811 }).then(function(result) {
811 }).then(function(result) {
812 if (result.value) {
812 if (result.value) {
813 if (callback !== undefined) {
813 if (callback !== undefined) {
814 callback(result)
814 callback(result)
815 }
815 }
816 self._finalizeDrafts(commentIds);
816 self._finalizeDrafts(commentIds);
817 }
817 }
818 })
818 })
819 };
819 };
820
820
821 this.toggleWideMode = function (node) {
821 this.toggleWideMode = function (node) {
822
822
823 if ($('#content').hasClass('wrapper')) {
823 if ($('#content').hasClass('wrapper')) {
824 $('#content').removeClass("wrapper");
824 $('#content').removeClass("wrapper");
825 $('#content').addClass("wide-mode-wrapper");
825 $('#content').addClass("wide-mode-wrapper");
826 $(node).addClass('btn-success');
826 $(node).addClass('btn-success');
827 return true
827 return true
828 } else {
828 } else {
829 $('#content').removeClass("wide-mode-wrapper");
829 $('#content').removeClass("wide-mode-wrapper");
830 $('#content').addClass("wrapper");
830 $('#content').addClass("wrapper");
831 $(node).removeClass('btn-success');
831 $(node).removeClass('btn-success');
832 return false
832 return false
833 }
833 }
834
834
835 };
835 };
836
836
837 /**
837 /**
838 * Turn off/on all comments in file diff
838 * Turn off/on all comments in file diff
839 */
839 */
840 this.toggleDiffComments = function(node) {
840 this.toggleDiffComments = function(node) {
841 // Find closes filediff container
841 // Find closes filediff container
842 var $filediff = $(node).closest('.filediff');
842 var $filediff = $(node).closest('.filediff');
843 if ($(node).hasClass('toggle-on')) {
843 if ($(node).hasClass('toggle-on')) {
844 var show = false;
844 var show = false;
845 } else if ($(node).hasClass('toggle-off')) {
845 } else if ($(node).hasClass('toggle-off')) {
846 var show = true;
846 var show = true;
847 }
847 }
848
848
849 // Toggle each individual comment block, so we can un-toggle single ones
849 // Toggle each individual comment block, so we can un-toggle single ones
850 $.each($filediff.find('.toggle-comment-action'), function(idx, val) {
850 $.each($filediff.find('.toggle-comment-action'), function(idx, val) {
851 self.toggleLineComments($(val), show)
851 self.toggleLineComments($(val), show)
852 })
852 })
853
853
854 // since we change the height of the diff container that has anchor points for upper
854 // since we change the height of the diff container that has anchor points for upper
855 // sticky header, we need to tell it to re-calculate those
855 // sticky header, we need to tell it to re-calculate those
856 if (window.updateSticky !== undefined) {
856 if (window.updateSticky !== undefined) {
857 // potentially our comments change the active window size, so we
857 // potentially our comments change the active window size, so we
858 // notify sticky elements
858 // notify sticky elements
859 updateSticky()
859 updateSticky()
860 }
860 }
861
861
862 return false;
862 return false;
863 }
863 }
864
864
865 this.toggleLineComments = function(node, show) {
865 this.toggleLineComments = function(node, show) {
866
866
867 var trElem = $(node).closest('tr')
867 var trElem = $(node).closest('tr')
868
868
869 if (show === true) {
869 if (show === true) {
870 // mark outdated comments as visible before the toggle;
870 // mark outdated comments as visible before the toggle;
871 $(trElem).find('.comment-outdated').show();
871 $(trElem).find('.comment-outdated').show();
872 $(trElem).removeClass('hide-line-comments');
872 $(trElem).removeClass('hide-line-comments');
873 } else if (show === false) {
873 } else if (show === false) {
874 $(trElem).find('.comment-outdated').hide();
874 $(trElem).find('.comment-outdated').hide();
875 $(trElem).addClass('hide-line-comments');
875 $(trElem).addClass('hide-line-comments');
876 } else {
876 } else {
877 // mark outdated comments as visible before the toggle;
877 // mark outdated comments as visible before the toggle;
878 $(trElem).find('.comment-outdated').show();
878 $(trElem).find('.comment-outdated').show();
879 $(trElem).toggleClass('hide-line-comments');
879 $(trElem).toggleClass('hide-line-comments');
880 }
880 }
881
881
882 // since we change the height of the diff container that has anchor points for upper
882 // since we change the height of the diff container that has anchor points for upper
883 // sticky header, we need to tell it to re-calculate those
883 // sticky header, we need to tell it to re-calculate those
884 if (window.updateSticky !== undefined) {
884 if (window.updateSticky !== undefined) {
885 // potentially our comments change the active window size, so we
885 // potentially our comments change the active window size, so we
886 // notify sticky elements
886 // notify sticky elements
887 updateSticky()
887 updateSticky()
888 }
888 }
889
889
890 };
890 };
891
891
892 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId, edit, comment_id){
892 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId, edit, comment_id){
893 var pullRequestId = templateContext.pull_request_data.pull_request_id;
893 var pullRequestId = templateContext.pull_request_data.pull_request_id;
894 var commitId = templateContext.commit_data.commit_id;
894 var commitId = templateContext.commit_data.commit_id;
895
895
896 var commentForm = new CommentForm(
896 var commentForm = new CommentForm(
897 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId, edit, comment_id);
897 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId, edit, comment_id);
898 var cm = commentForm.getCmInstance();
898 var cm = commentForm.getCmInstance();
899
899
900 if (resolvesCommentId){
900 if (resolvesCommentId){
901 placeholderText = _gettext('Leave a resolution comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
901 placeholderText = _gettext('Leave a resolution comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
902 }
902 }
903
903
904 setTimeout(function() {
904 setTimeout(function() {
905 // callbacks
905 // callbacks
906 if (cm !== undefined) {
906 if (cm !== undefined) {
907 commentForm.setPlaceholder(placeholderText);
907 commentForm.setPlaceholder(placeholderText);
908 if (commentForm.isInline()) {
908 if (commentForm.isInline()) {
909 cm.focus();
909 cm.focus();
910 cm.refresh();
910 cm.refresh();
911 }
911 }
912 }
912 }
913 }, 10);
913 }, 10);
914
914
915 // trigger scrolldown to the resolve comment, since it might be away
915 // trigger scrolldown to the resolve comment, since it might be away
916 // from the clicked
916 // from the clicked
917 if (resolvesCommentId){
917 if (resolvesCommentId){
918 var actionNode = $(commentForm.resolvesActionId).offset();
918 var actionNode = $(commentForm.resolvesActionId).offset();
919
919
920 setTimeout(function() {
920 setTimeout(function() {
921 if (actionNode) {
921 if (actionNode) {
922 $('body, html').animate({scrollTop: actionNode.top}, 10);
922 $('body, html').animate({scrollTop: actionNode.top}, 10);
923 }
923 }
924 }, 100);
924 }, 100);
925 }
925 }
926
926
927 // add dropzone support
927 // add dropzone support
928 var insertAttachmentText = function (cm, attachmentName, attachmentStoreUrl, isRendered) {
928 var insertAttachmentText = function (cm, attachmentName, attachmentStoreUrl, isRendered) {
929 var renderer = templateContext.visual.default_renderer;
929 var renderer = templateContext.visual.default_renderer;
930 if (renderer == 'rst') {
930 if (renderer == 'rst') {
931 var attachmentUrl = '`#{0} <{1}>`_'.format(attachmentName, attachmentStoreUrl);
931 var attachmentUrl = '`#{0} <{1}>`_'.format(attachmentName, attachmentStoreUrl);
932 if (isRendered){
932 if (isRendered){
933 attachmentUrl = '\n.. image:: {0}'.format(attachmentStoreUrl);
933 attachmentUrl = '\n.. image:: {0}'.format(attachmentStoreUrl);
934 }
934 }
935 } else if (renderer == 'markdown') {
935 } else if (renderer == 'markdown') {
936 var attachmentUrl = '[{0}]({1})'.format(attachmentName, attachmentStoreUrl);
936 var attachmentUrl = '[{0}]({1})'.format(attachmentName, attachmentStoreUrl);
937 if (isRendered){
937 if (isRendered){
938 attachmentUrl = '!' + attachmentUrl;
938 attachmentUrl = '!' + attachmentUrl;
939 }
939 }
940 } else {
940 } else {
941 var attachmentUrl = '{}'.format(attachmentStoreUrl);
941 var attachmentUrl = '{}'.format(attachmentStoreUrl);
942 }
942 }
943 cm.replaceRange(attachmentUrl+'\n', CodeMirror.Pos(cm.lastLine()));
943 cm.replaceRange(attachmentUrl+'\n', CodeMirror.Pos(cm.lastLine()));
944
944
945 return false;
945 return false;
946 };
946 };
947
947
948 //see: https://www.dropzonejs.com/#configuration
948 //see: https://www.dropzonejs.com/#configuration
949 var storeUrl = pyroutes.url('repo_commit_comment_attachment_upload',
949 var storeUrl = pyroutes.url('repo_commit_comment_attachment_upload',
950 {'repo_name': templateContext.repo_name,
950 {'repo_name': templateContext.repo_name,
951 'commit_id': templateContext.commit_data.commit_id})
951 'commit_id': templateContext.commit_data.commit_id})
952
952
953 var previewTmpl = $(formElement).find('.comment-attachment-uploader-template').get(0);
953 var previewTmpl = $(formElement).find('.comment-attachment-uploader-template').get(0);
954 if (previewTmpl !== undefined){
954 if (previewTmpl !== undefined){
955 var selectLink = $(formElement).find('.pick-attachment').get(0);
955 var selectLink = $(formElement).find('.pick-attachment').get(0);
956 $(formElement).find('.comment-attachment-uploader').dropzone({
956 $(formElement).find('.comment-attachment-uploader').dropzone({
957 url: storeUrl,
957 url: storeUrl,
958 headers: {"X-CSRF-Token": CSRF_TOKEN},
958 headers: {"X-CSRF-Token": CSRF_TOKEN},
959 paramName: function () {
959 paramName: function () {
960 return "attachment"
960 return "attachment"
961 }, // The name that will be used to transfer the file
961 }, // The name that will be used to transfer the file
962 clickable: selectLink,
962 clickable: selectLink,
963 parallelUploads: 1,
963 parallelUploads: 1,
964 maxFiles: 10,
964 maxFiles: 10,
965 maxFilesize: templateContext.attachment_store.max_file_size_mb,
965 maxFilesize: templateContext.attachment_store.max_file_size_mb,
966 uploadMultiple: false,
966 uploadMultiple: false,
967 autoProcessQueue: true, // if false queue will not be processed automatically.
967 autoProcessQueue: true, // if false queue will not be processed automatically.
968 createImageThumbnails: false,
968 createImageThumbnails: false,
969 previewTemplate: previewTmpl.innerHTML,
969 previewTemplate: previewTmpl.innerHTML,
970
970
971 accept: function (file, done) {
971 accept: function (file, done) {
972 done();
972 done();
973 },
973 },
974 init: function () {
974 init: function () {
975
975
976 this.on("sending", function (file, xhr, formData) {
976 this.on("sending", function (file, xhr, formData) {
977 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').hide();
977 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').hide();
978 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').show();
978 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').show();
979 });
979 });
980
980
981 this.on("success", function (file, response) {
981 this.on("success", function (file, response) {
982 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').show();
982 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').show();
983 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
983 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
984
984
985 var isRendered = false;
985 var isRendered = false;
986 var ext = file.name.split('.').pop();
986 var ext = file.name.split('.').pop();
987 var imageExts = templateContext.attachment_store.image_ext;
987 var imageExts = templateContext.attachment_store.image_ext;
988 if (imageExts.indexOf(ext) !== -1){
988 if (imageExts.indexOf(ext) !== -1){
989 isRendered = true;
989 isRendered = true;
990 }
990 }
991
991
992 insertAttachmentText(cm, file.name, response.repo_fqn_access_path, isRendered)
992 insertAttachmentText(cm, file.name, response.repo_fqn_access_path, isRendered)
993 });
993 });
994
994
995 this.on("error", function (file, errorMessage, xhr) {
995 this.on("error", function (file, errorMessage, xhr) {
996 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
996 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
997
997
998 var error = null;
998 var error = null;
999
999
1000 if (xhr !== undefined){
1000 if (xhr !== undefined){
1001 var httpStatus = xhr.status + " " + xhr.statusText;
1001 var httpStatus = xhr.status + " " + xhr.statusText;
1002 if (xhr !== undefined && xhr.status >= 500) {
1002 if (xhr !== undefined && xhr.status >= 500) {
1003 error = httpStatus;
1003 error = httpStatus;
1004 }
1004 }
1005 }
1005 }
1006
1006
1007 if (error === null) {
1007 if (error === null) {
1008 error = errorMessage.error || errorMessage || httpStatus;
1008 error = errorMessage.error || errorMessage || httpStatus;
1009 }
1009 }
1010 $(file.previewElement).find('.dz-error-message').html('ERROR: {0}'.format(error));
1010 $(file.previewElement).find('.dz-error-message').html('ERROR: {0}'.format(error));
1011
1011
1012 });
1012 });
1013 }
1013 }
1014 });
1014 });
1015 }
1015 }
1016 return commentForm;
1016 return commentForm;
1017 };
1017 };
1018
1018
1019 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
1019 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
1020
1020
1021 var tmpl = $('#cb-comment-general-form-template').html();
1021 var tmpl = $('#cb-comment-general-form-template').html();
1022 tmpl = tmpl.format(null, 'general');
1022 tmpl = tmpl.format(null, 'general');
1023 var $form = $(tmpl);
1023 var $form = $(tmpl);
1024
1024
1025 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
1025 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
1026 var curForm = $formPlaceholder.find('form');
1026 var curForm = $formPlaceholder.find('form');
1027 if (curForm){
1027 if (curForm){
1028 curForm.remove();
1028 curForm.remove();
1029 }
1029 }
1030 $formPlaceholder.append($form);
1030 $formPlaceholder.append($form);
1031
1031
1032 var _form = $($form[0]);
1032 var _form = $($form[0]);
1033 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
1033 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
1034 var edit = false;
1034 var edit = false;
1035 var comment_id = null;
1035 var comment_id = null;
1036 var commentForm = this.createCommentForm(
1036 var commentForm = this.createCommentForm(
1037 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId, edit, comment_id);
1037 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId, edit, comment_id);
1038 commentForm.initStatusChangeSelector();
1038 commentForm.initStatusChangeSelector();
1039
1039
1040 return commentForm;
1040 return commentForm;
1041 };
1041 };
1042
1042
1043 this.editComment = function(node, line_no, f_path) {
1043 this.editComment = function(node, line_no, f_path) {
1044 self.edit = true;
1044 self.edit = true;
1045 var $node = $(node);
1045 var $node = $(node);
1046 var $td = $node.closest('td');
1046 var $td = $node.closest('td');
1047
1047
1048 var $comment = $(node).closest('.comment');
1048 var $comment = $(node).closest('.comment');
1049 var comment_id = $($comment).data('commentId');
1049 var comment_id = $($comment).data('commentId');
1050 var isDraft = $($comment).data('commentDraft');
1050 var isDraft = $($comment).data('commentDraft');
1051 var $editForm = null
1051 var $editForm = null
1052
1052
1053 var $comments = $node.closest('div.inline-comments');
1053 var $comments = $node.closest('div.inline-comments');
1054 var $general_comments = null;
1054 var $general_comments = null;
1055
1055
1056 if($comments.length){
1056 if($comments.length){
1057 // inline comments setup
1057 // inline comments setup
1058 $editForm = $comments.find('.comment-inline-form');
1058 $editForm = $comments.find('.comment-inline-form');
1059 line_no = self.getLineNumber(node)
1059 line_no = self.getLineNumber(node)
1060 }
1060 }
1061 else{
1061 else{
1062 // general comments setup
1062 // general comments setup
1063 $comments = $('#comments');
1063 $comments = $('#comments');
1064 $editForm = $comments.find('.comment-inline-form');
1064 $editForm = $comments.find('.comment-inline-form');
1065 line_no = $comment[0].id
1065 line_no = $comment[0].id
1066 $('#cb-comment-general-form-placeholder').hide();
1066 $('#cb-comment-general-form-placeholder').hide();
1067 }
1067 }
1068
1068
1069 if ($editForm.length === 0) {
1069 if ($editForm.length === 0) {
1070
1070
1071 // unhide all comments if they are hidden for a proper REPLY mode
1071 // unhide all comments if they are hidden for a proper REPLY mode
1072 var $filediff = $node.closest('.filediff');
1072 var $filediff = $node.closest('.filediff');
1073 $filediff.removeClass('hide-comments');
1073 $filediff.removeClass('hide-comments');
1074
1074
1075 $editForm = self.createNewFormWrapper(f_path, line_no);
1075 $editForm = self.createNewFormWrapper(f_path, line_no);
1076 if(f_path && line_no) {
1076 if(f_path && line_no) {
1077 $editForm.addClass('comment-inline-form-edit')
1077 $editForm.addClass('comment-inline-form-edit')
1078 }
1078 }
1079
1079
1080 $comment.after($editForm)
1080 $comment.after($editForm)
1081
1081
1082 var _form = $($editForm[0]).find('form');
1082 var _form = $($editForm[0]).find('form');
1083 var autocompleteActions = ['as_note',];
1083 var autocompleteActions = ['as_note',];
1084 var commentForm = this.createCommentForm(
1084 var commentForm = this.createCommentForm(
1085 _form, line_no, '', autocompleteActions, resolvesCommentId,
1085 _form, line_no, '', autocompleteActions, resolvesCommentId,
1086 this.edit, comment_id);
1086 this.edit, comment_id);
1087 var old_comment_text_binary = $comment.attr('data-comment-text');
1087 var old_comment_text_binary = $comment.attr('data-comment-text');
1088 var old_comment_text = b64DecodeUnicode(old_comment_text_binary);
1088 var old_comment_text = b64DecodeUnicode(old_comment_text_binary);
1089 commentForm.cm.setValue(old_comment_text);
1089 commentForm.cm.setValue(old_comment_text);
1090 $comment.hide();
1090 $comment.hide();
1091 tooltipActivate();
1091 tooltipActivate();
1092
1092
1093 // set a CUSTOM submit handler for inline comment edit action.
1093 // set a CUSTOM submit handler for inline comment edit action.
1094 commentForm.setHandleFormSubmit(function(o) {
1094 commentForm.setHandleFormSubmit(function(o) {
1095 var text = commentForm.cm.getValue();
1095 var text = commentForm.cm.getValue();
1096 var commentType = commentForm.getCommentType();
1096 var commentType = commentForm.getCommentType();
1097
1097
1098 if (text === "") {
1098 if (text === "") {
1099 return;
1099 return;
1100 }
1100 }
1101
1101
1102 if (old_comment_text == text) {
1102 if (old_comment_text == text) {
1103 SwalNoAnimation.fire({
1103 SwalNoAnimation.fire({
1104 title: 'Unable to edit comment',
1104 title: 'Unable to edit comment',
1105 html: _gettext('Comment body was not changed.'),
1105 html: _gettext('Comment body was not changed.'),
1106 });
1106 });
1107 return;
1107 return;
1108 }
1108 }
1109 var excludeCancelBtn = false;
1109 var excludeCancelBtn = false;
1110 var submitEvent = true;
1110 var submitEvent = true;
1111 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
1111 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
1112 commentForm.cm.setOption("readOnly", true);
1112 commentForm.cm.setOption("readOnly", true);
1113
1113
1114 // Read last version known
1114 // Read last version known
1115 var versionSelector = $('#comment_versions_{0}'.format(comment_id));
1115 var versionSelector = $('#comment_versions_{0}'.format(comment_id));
1116 var version = versionSelector.data('lastVersion');
1116 var version = versionSelector.data('lastVersion');
1117
1117
1118 if (!version) {
1118 if (!version) {
1119 version = 0;
1119 version = 0;
1120 }
1120 }
1121
1121
1122 var postData = {
1122 var postData = {
1123 'text': text,
1123 'text': text,
1124 'f_path': f_path,
1124 'f_path': f_path,
1125 'line': line_no,
1125 'line': line_no,
1126 'comment_type': commentType,
1126 'comment_type': commentType,
1127 'draft': isDraft,
1127 'draft': isDraft,
1128 'version': version,
1128 'version': version,
1129 'csrf_token': CSRF_TOKEN
1129 'csrf_token': CSRF_TOKEN
1130 };
1130 };
1131
1131
1132 var submitSuccessCallback = function(json_data) {
1132 var submitSuccessCallback = function(json_data) {
1133 $editForm.remove();
1133 $editForm.remove();
1134 $comment.show();
1134 $comment.show();
1135 var postData = {
1135 var postData = {
1136 'text': text,
1136 'text': text,
1137 'renderer': $comment.attr('data-comment-renderer'),
1137 'renderer': $comment.attr('data-comment-renderer'),
1138 'csrf_token': CSRF_TOKEN
1138 'csrf_token': CSRF_TOKEN
1139 };
1139 };
1140
1140
1141 /* Inject new edited version selector */
1141 /* Inject new edited version selector */
1142 var updateCommentVersionDropDown = function () {
1142 var updateCommentVersionDropDown = function () {
1143 var versionSelectId = '#comment_versions_'+comment_id;
1143 var versionSelectId = '#comment_versions_'+comment_id;
1144 var preLoadVersionData = [
1144 var preLoadVersionData = [
1145 {
1145 {
1146 id: json_data['comment_version'],
1146 id: json_data['comment_version'],
1147 text: "v{0}".format(json_data['comment_version']),
1147 text: "v{0}".format(json_data['comment_version']),
1148 action: function () {
1148 action: function () {
1149 Rhodecode.comments.showVersion(
1149 Rhodecode.comments.showVersion(
1150 json_data['comment_id'],
1150 json_data['comment_id'],
1151 json_data['comment_history_id']
1151 json_data['comment_history_id']
1152 )
1152 )
1153 },
1153 },
1154 comment_version: json_data['comment_version'],
1154 comment_version: json_data['comment_version'],
1155 comment_author_username: json_data['comment_author_username'],
1155 comment_author_username: json_data['comment_author_username'],
1156 comment_author_gravatar: json_data['comment_author_gravatar'],
1156 comment_author_gravatar: json_data['comment_author_gravatar'],
1157 comment_created_on: json_data['comment_created_on'],
1157 comment_created_on: json_data['comment_created_on'],
1158 },
1158 },
1159 ]
1159 ]
1160
1160
1161
1161
1162 if ($(versionSelectId).data('select2')) {
1162 if ($(versionSelectId).data('select2')) {
1163 var oldData = $(versionSelectId).data('select2').opts.data.results;
1163 var oldData = $(versionSelectId).data('select2').opts.data.results;
1164 $(versionSelectId).select2("destroy");
1164 $(versionSelectId).select2("destroy");
1165 preLoadVersionData = oldData.concat(preLoadVersionData)
1165 preLoadVersionData = oldData.concat(preLoadVersionData)
1166 }
1166 }
1167
1167
1168 initVersionSelector(versionSelectId, {results: preLoadVersionData});
1168 initVersionSelector(versionSelectId, {results: preLoadVersionData});
1169
1169
1170 $comment.attr('data-comment-text', utf8ToB64(text));
1170 $comment.attr('data-comment-text', utf8ToB64(text));
1171
1171
1172 var versionSelector = $('#comment_versions_'+comment_id);
1172 var versionSelector = $('#comment_versions_'+comment_id);
1173
1173
1174 // set lastVersion so we know our last edit version
1174 // set lastVersion so we know our last edit version
1175 versionSelector.data('lastVersion', json_data['comment_version'])
1175 versionSelector.data('lastVersion', json_data['comment_version'])
1176 versionSelector.parent().show();
1176 versionSelector.parent().show();
1177 }
1177 }
1178 updateCommentVersionDropDown();
1178 updateCommentVersionDropDown();
1179
1179
1180 // by default we reset state of comment preserving the text
1180 // by default we reset state of comment preserving the text
1181 var failRenderCommit = function(jqXHR, textStatus, errorThrown) {
1181 var failRenderCommit = function(jqXHR, textStatus, errorThrown) {
1182 var prefix = "Error while editing this comment.\n"
1182 var prefix = "Error while editing this comment.\n"
1183 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1183 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1184 ajaxErrorSwal(message);
1184 ajaxErrorSwal(message);
1185 };
1185 };
1186
1186
1187 var successRenderCommit = function(o){
1187 var successRenderCommit = function(o){
1188 $comment.show();
1188 $comment.show();
1189 $comment[0].lastElementChild.innerHTML = o;
1189 $comment[0].lastElementChild.innerHTML = o;
1190 };
1190 };
1191
1191
1192 var previewUrl = pyroutes.url(
1192 var previewUrl = pyroutes.url(
1193 'repo_commit_comment_preview',
1193 'repo_commit_comment_preview',
1194 {'repo_name': templateContext.repo_name,
1194 {'repo_name': templateContext.repo_name,
1195 'commit_id': templateContext.commit_data.commit_id});
1195 'commit_id': templateContext.commit_data.commit_id});
1196
1196
1197 _submitAjaxPOST(
1197 _submitAjaxPOST(
1198 previewUrl, postData, successRenderCommit, failRenderCommit
1198 previewUrl, postData, successRenderCommit, failRenderCommit
1199 );
1199 );
1200
1200
1201 try {
1201 try {
1202 var html = json_data.rendered_text;
1202 var html = json_data.rendered_text;
1203 var lineno = json_data.line_no;
1203 var lineno = json_data.line_no;
1204 var target_id = json_data.target_id;
1204 var target_id = json_data.target_id;
1205
1205
1206 $comments.find('.cb-comment-add-button').before(html);
1206 $comments.find('.cb-comment-add-button').before(html);
1207
1207
1208 // run global callback on submit
1208 // run global callback on submit
1209 commentForm.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id});
1209 commentForm.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id});
1210
1210
1211 } catch (e) {
1211 } catch (e) {
1212 console.error(e);
1212 console.error(e);
1213 }
1213 }
1214
1214
1215 // re trigger the linkification of next/prev navigation
1215 // re trigger the linkification of next/prev navigation
1216 linkifyComments($('.inline-comment-injected'));
1216 linkifyComments($('.inline-comment-injected'));
1217 timeagoActivate();
1217 timeagoActivate();
1218 tooltipActivate();
1218 tooltipActivate();
1219
1219
1220 if (window.updateSticky !== undefined) {
1220 if (window.updateSticky !== undefined) {
1221 // potentially our comments change the active window size, so we
1221 // potentially our comments change the active window size, so we
1222 // notify sticky elements
1222 // notify sticky elements
1223 updateSticky()
1223 updateSticky()
1224 }
1224 }
1225
1225
1226 if (window.refreshAllComments !== undefined && !isDraft) {
1226 if (window.refreshAllComments !== undefined && !isDraft) {
1227 // if we have this handler, run it, and refresh all comments boxes
1227 // if we have this handler, run it, and refresh all comments boxes
1228 refreshAllComments()
1228 refreshAllComments()
1229 }
1229 }
1230 else if (window.refreshDraftComments !== undefined && isDraft) {
1230 else if (window.refreshDraftComments !== undefined && isDraft) {
1231 // if we have this handler, run it, and refresh all comments boxes
1231 // if we have this handler, run it, and refresh all comments boxes
1232 refreshDraftComments();
1232 refreshDraftComments();
1233 }
1233 }
1234
1234
1235 commentForm.setActionButtonsDisabled(false);
1235 commentForm.setActionButtonsDisabled(false);
1236
1236
1237 };
1237 };
1238
1238
1239 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
1239 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
1240 var prefix = "Error while editing comment.\n"
1240 var prefix = "Error while editing comment.\n"
1241 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1241 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1242 if (jqXHR.status == 409){
1242 if (jqXHR.status == 409){
1243 message = 'This comment was probably changed somewhere else. Please reload the content of this comment.'
1243 message = 'This comment was probably changed somewhere else. Please reload the content of this comment.'
1244 ajaxErrorSwal(message, 'Comment version mismatch.');
1244 ajaxErrorSwal(message, 'Comment version mismatch.');
1245 } else {
1245 } else {
1246 ajaxErrorSwal(message);
1246 ajaxErrorSwal(message);
1247 }
1247 }
1248
1248
1249 commentForm.resetCommentFormState(text)
1249 commentForm.resetCommentFormState(text)
1250 };
1250 };
1251 commentForm.submitAjaxPOST(
1251 commentForm.submitAjaxPOST(
1252 commentForm.submitUrl, postData,
1252 commentForm.submitUrl, postData,
1253 submitSuccessCallback,
1253 submitSuccessCallback,
1254 submitFailCallback);
1254 submitFailCallback);
1255 });
1255 });
1256 }
1256 }
1257
1257
1258 $editForm.addClass('comment-inline-form-open');
1258 $editForm.addClass('comment-inline-form-open');
1259 };
1259 };
1260
1260
1261 this.attachComment = function(json_data) {
1261 this.attachComment = function(json_data) {
1262 var self = this;
1262 var self = this;
1263 $.each(json_data, function(idx, val) {
1263 $.each(json_data, function(idx, val) {
1264 var json_data_elem = [val]
1264 var json_data_elem = [val]
1265 var isInline = val.comment_f_path && val.comment_lineno
1265 var isInline = val.comment_f_path && val.comment_lineno
1266
1266
1267 if (isInline) {
1267 if (isInline) {
1268 self.attachInlineComment(json_data_elem)
1268 self.attachInlineComment(json_data_elem)
1269 } else {
1269 } else {
1270 self.attachGeneralComment(json_data_elem)
1270 self.attachGeneralComment(json_data_elem)
1271 }
1271 }
1272 })
1272 })
1273
1273
1274 }
1274 }
1275
1275
1276 this.attachGeneralComment = function(json_data) {
1276 this.attachGeneralComment = function(json_data) {
1277 $.each(json_data, function(idx, val) {
1277 $.each(json_data, function(idx, val) {
1278 $('#injected_page_comments').append(val.rendered_text);
1278 $('#injected_page_comments').append(val.rendered_text);
1279 })
1279 })
1280 }
1280 }
1281
1281
1282 this.attachInlineComment = function(json_data) {
1282 this.attachInlineComment = function(json_data) {
1283
1283
1284 $.each(json_data, function (idx, val) {
1284 $.each(json_data, function (idx, val) {
1285 var line_qry = '*[data-line-no="{0}"]'.format(val.line_no);
1285 var line_qry = '*[data-line-no="{0}"]'.format(val.line_no);
1286 var html = val.rendered_text;
1286 var html = val.rendered_text;
1287 var $inlineComments = $('#' + val.target_id)
1287 var $inlineComments = $('#' + val.target_id)
1288 .find(line_qry)
1288 .find(line_qry)
1289 .find('.inline-comments');
1289 .find('.inline-comments');
1290
1290
1291 var lastComment = $inlineComments.find('.comment-inline').last();
1291 var lastComment = $inlineComments.find('.comment-inline').last();
1292
1292
1293 if (lastComment.length === 0) {
1293 if (lastComment.length === 0) {
1294 // first comment, we append simply
1294 // first comment, we append simply
1295 $inlineComments.find('.reply-thread-container-wrapper').before(html);
1295 $inlineComments.find('.reply-thread-container-wrapper').before(html);
1296 } else {
1296 } else {
1297 $(lastComment).after(html)
1297 $(lastComment).after(html)
1298 }
1298 }
1299
1299
1300 })
1300 })
1301
1301
1302 };
1302 };
1303
1303
1304 this.createNewFormWrapper = function(f_path, line_no) {
1304 this.createNewFormWrapper = function(f_path, line_no) {
1305 // create a new reply HTML form from template
1305 // create a new reply HTML form from template
1306 var tmpl = $('#cb-comment-inline-form-template').html();
1306 var tmpl = $('#cb-comment-inline-form-template').html();
1307 tmpl = tmpl.format(escapeHtml(f_path), line_no);
1307 tmpl = tmpl.format(escapeHtml(f_path), line_no);
1308 return $(tmpl);
1308 return $(tmpl);
1309 }
1309 }
1310
1310
1311 this.createComment = function(node, f_path, line_no, resolutionComment) {
1311 this.createComment = function(node, f_path, line_no, resolutionComment) {
1312 self.edit = false;
1312 self.edit = false;
1313 var $node = $(node);
1313 var $node = $(node);
1314 var $td = $node.closest('td');
1314 var $td = $node.closest('td');
1315 var resolvesCommentId = resolutionComment || null;
1315 var resolvesCommentId = resolutionComment || null;
1316
1316
1317 var $replyForm = $td.find('.comment-inline-form');
1317 var $replyForm = $td.find('.comment-inline-form');
1318
1318
1319 // if form isn't existing, we're generating a new one and injecting it.
1319 // if form isn't existing, we're generating a new one and injecting it.
1320 if ($replyForm.length === 0) {
1320 if ($replyForm.length === 0) {
1321
1321
1322 // unhide/expand all comments if they are hidden for a proper REPLY mode
1322 // unhide/expand all comments if they are hidden for a proper REPLY mode
1323 self.toggleLineComments($node, true);
1323 self.toggleLineComments($node, true);
1324
1324
1325 $replyForm = self.createNewFormWrapper(f_path, line_no);
1325 $replyForm = self.createNewFormWrapper(f_path, line_no);
1326
1326
1327 var $comments = $td.find('.inline-comments');
1327 var $comments = $td.find('.inline-comments');
1328
1328
1329 // There aren't any comments, we init the `.inline-comments` with `reply-thread-container` first
1329 // There aren't any comments, we init the `.inline-comments` with `reply-thread-container` first
1330 if ($comments.length===0) {
1330 if ($comments.length===0) {
1331 var replBtn = '<button class="cb-comment-add-button" onclick="return Rhodecode.comments.createComment(this, \'{0}\', \'{1}\', null)">Reply...</button>'.format(f_path, line_no)
1331 var replBtn = '<button class="cb-comment-add-button" onclick="return Rhodecode.comments.createComment(this, \'{0}\', \'{1}\', null)">Reply...</button>'.format(f_path, line_no)
1332 var $reply_container = $('#cb-comments-inline-container-template')
1332 var $reply_container = $('#cb-comments-inline-container-template')
1333 $reply_container.find('button.cb-comment-add-button').replaceWith(replBtn);
1333 $reply_container.find('button.cb-comment-add-button').replaceWith(replBtn);
1334 $td.append($($reply_container).html());
1334 $td.append($($reply_container).html());
1335 }
1335 }
1336
1336
1337 // default comment button exists, so we prepend the form for leaving initial comment
1337 // default comment button exists, so we prepend the form for leaving initial comment
1338 $td.find('.cb-comment-add-button').before($replyForm);
1338 $td.find('.cb-comment-add-button').before($replyForm);
1339 // set marker, that we have a open form
1339 // set marker, that we have a open form
1340 var $replyWrapper = $td.find('.reply-thread-container-wrapper')
1340 var $replyWrapper = $td.find('.reply-thread-container-wrapper')
1341 $replyWrapper.addClass('comment-form-active');
1341 $replyWrapper.addClass('comment-form-active');
1342
1342
1343 var lastComment = $comments.find('.comment-inline').last();
1343 var lastComment = $comments.find('.comment-inline').last();
1344 if ($(lastComment).hasClass('comment-outdated')) {
1344 if ($(lastComment).hasClass('comment-outdated')) {
1345 $replyWrapper.show();
1345 $replyWrapper.show();
1346 }
1346 }
1347
1347
1348 var _form = $($replyForm[0]).find('form');
1348 var _form = $($replyForm[0]).find('form');
1349 var autocompleteActions = ['as_note', 'as_todo'];
1349 var autocompleteActions = ['as_note', 'as_todo'];
1350 var comment_id=null;
1350 var comment_id=null;
1351 var placeholderText = _gettext('Leave a comment on file {0} line {1}.').format(f_path, line_no);
1351 var placeholderText = _gettext('Leave a comment on file {0} line {1}.').format(f_path, line_no);
1352 var commentForm = self.createCommentForm(
1352 var commentForm = self.createCommentForm(
1353 _form, line_no, placeholderText, autocompleteActions, resolvesCommentId,
1353 _form, line_no, placeholderText, autocompleteActions, resolvesCommentId,
1354 self.edit, comment_id);
1354 self.edit, comment_id);
1355
1355
1356 // set a CUSTOM submit handler for inline comments.
1356 // set a CUSTOM submit handler for inline comments.
1357 commentForm.setHandleFormSubmit(function(o) {
1357 commentForm.setHandleFormSubmit(function(o) {
1358 var text = commentForm.cm.getValue();
1358 var text = commentForm.cm.getValue();
1359 var commentType = commentForm.getCommentType();
1359 var commentType = commentForm.getCommentType();
1360 var resolvesCommentId = commentForm.getResolvesId();
1360 var resolvesCommentId = commentForm.getResolvesId();
1361 var isDraft = commentForm.getDraftState();
1361 var isDraft = commentForm.getDraftState();
1362
1362
1363 if (text === "") {
1363 if (text === "") {
1364 return;
1364 return;
1365 }
1365 }
1366
1366
1367 if (line_no === undefined) {
1367 if (line_no === undefined) {
1368 alert('Error: unable to fetch line number for this inline comment !');
1368 alert('Error: unable to fetch line number for this inline comment !');
1369 return;
1369 return;
1370 }
1370 }
1371
1371
1372 if (f_path === undefined) {
1372 if (f_path === undefined) {
1373 alert('Error: unable to fetch file path for this inline comment !');
1373 alert('Error: unable to fetch file path for this inline comment !');
1374 return;
1374 return;
1375 }
1375 }
1376
1376
1377 var excludeCancelBtn = false;
1377 var excludeCancelBtn = false;
1378 var submitEvent = true;
1378 var submitEvent = true;
1379 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
1379 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
1380 commentForm.cm.setOption("readOnly", true);
1380 commentForm.cm.setOption("readOnly", true);
1381 var postData = {
1381 var postData = {
1382 'text': text,
1382 'text': text,
1383 'f_path': f_path,
1383 'f_path': f_path,
1384 'line': line_no,
1384 'line': line_no,
1385 'comment_type': commentType,
1385 'comment_type': commentType,
1386 'draft': isDraft,
1386 'draft': isDraft,
1387 'csrf_token': CSRF_TOKEN
1387 'csrf_token': CSRF_TOKEN
1388 };
1388 };
1389 if (resolvesCommentId){
1389 if (resolvesCommentId){
1390 postData['resolves_comment_id'] = resolvesCommentId;
1390 postData['resolves_comment_id'] = resolvesCommentId;
1391 }
1391 }
1392
1392
1393 // submitSuccess for inline commits
1393 // submitSuccess for inline commits
1394 var submitSuccessCallback = function(json_data) {
1394 var submitSuccessCallback = function(json_data) {
1395
1395
1396 $replyForm.remove();
1396 $replyForm.remove();
1397 $td.find('.reply-thread-container-wrapper').removeClass('comment-form-active');
1397 $td.find('.reply-thread-container-wrapper').removeClass('comment-form-active');
1398
1398
1399 try {
1399 try {
1400
1400
1401 // inject newly created comments, json_data is {<comment_id>: {}}
1401 // inject newly created comments, json_data is {<comment_id>: {}}
1402 self.attachInlineComment(json_data)
1402 self.attachInlineComment(json_data)
1403
1403
1404 //mark visually which comment was resolved
1404 //mark visually which comment was resolved
1405 if (resolvesCommentId) {
1405 if (resolvesCommentId) {
1406 commentForm.markCommentResolved(resolvesCommentId);
1406 commentForm.markCommentResolved(resolvesCommentId);
1407 }
1407 }
1408
1408
1409 // run global callback on submit
1409 // run global callback on submit
1410 commentForm.globalSubmitSuccessCallback({
1410 commentForm.globalSubmitSuccessCallback({
1411 draft: isDraft,
1411 draft: isDraft,
1412 comment_id: comment_id
1412 comment_id: comment_id
1413 });
1413 });
1414
1414
1415 } catch (e) {
1415 } catch (e) {
1416 console.error(e);
1416 console.error(e);
1417 }
1417 }
1418
1418
1419 if (window.updateSticky !== undefined) {
1419 if (window.updateSticky !== undefined) {
1420 // potentially our comments change the active window size, so we
1420 // potentially our comments change the active window size, so we
1421 // notify sticky elements
1421 // notify sticky elements
1422 updateSticky()
1422 updateSticky()
1423 }
1423 }
1424
1424
1425 if (window.refreshAllComments !== undefined && !isDraft) {
1425 if (window.refreshAllComments !== undefined && !isDraft) {
1426 // if we have this handler, run it, and refresh all comments boxes
1426 // if we have this handler, run it, and refresh all comments boxes
1427 refreshAllComments()
1427 refreshAllComments()
1428 }
1428 }
1429 else if (window.refreshDraftComments !== undefined && isDraft) {
1429 else if (window.refreshDraftComments !== undefined && isDraft) {
1430 // if we have this handler, run it, and refresh all comments boxes
1430 // if we have this handler, run it, and refresh all comments boxes
1431 refreshDraftComments();
1431 refreshDraftComments();
1432 }
1432 }
1433
1433
1434 commentForm.setActionButtonsDisabled(false);
1434 commentForm.setActionButtonsDisabled(false);
1435
1435
1436 // re trigger the linkification of next/prev navigation
1436 // re trigger the linkification of next/prev navigation
1437 linkifyComments($('.inline-comment-injected'));
1437 linkifyComments($('.inline-comment-injected'));
1438 timeagoActivate();
1438 timeagoActivate();
1439 tooltipActivate();
1439 tooltipActivate();
1440 };
1440 };
1441
1441
1442 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
1442 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
1443 var prefix = "Error while submitting comment.\n"
1443 var prefix = "Error while submitting comment.\n"
1444 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1444 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1445 ajaxErrorSwal(message);
1445 ajaxErrorSwal(message);
1446 commentForm.resetCommentFormState(text)
1446 commentForm.resetCommentFormState(text)
1447 };
1447 };
1448
1448
1449 commentForm.submitAjaxPOST(
1449 commentForm.submitAjaxPOST(
1450 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
1450 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
1451 });
1451 });
1452 }
1452 }
1453
1453
1454 // Finally "open" our reply form, since we know there are comments and we have the "attached" old form
1454 // Finally "open" our reply form, since we know there are comments and we have the "attached" old form
1455 $replyForm.addClass('comment-inline-form-open');
1455 $replyForm.addClass('comment-inline-form-open');
1456 tooltipActivate();
1456 tooltipActivate();
1457 };
1457 };
1458
1458
1459 this.createResolutionComment = function(commentId){
1459 this.createResolutionComment = function(commentId){
1460 // hide the trigger text
1460 // hide the trigger text
1461 $('#resolve-comment-{0}'.format(commentId)).hide();
1461 $('#resolve-comment-{0}'.format(commentId)).hide();
1462
1462
1463 var comment = $('#comment-'+commentId);
1463 var comment = $('#comment-'+commentId);
1464 var commentData = comment.data();
1464 var commentData = comment.data();
1465 console.log(commentData);
1465 console.log(commentData);
1466
1466
1467 if (commentData.commentInline) {
1467 if (commentData.commentInline) {
1468 var f_path = commentData.commentFPath;
1468 var f_path = commentData.commentFPath;
1469 var line_no = commentData.commentLineNo;
1469 var line_no = commentData.commentLineNo;
1470 this.createComment(comment, f_path, line_no, commentId)
1470 this.createComment(comment, f_path, line_no, commentId)
1471 } else {
1471 } else {
1472 this.createGeneralComment('general', "$placeholder", commentId)
1472 this.createGeneralComment('general', "$placeholder", commentId)
1473 }
1473 }
1474
1474
1475 return false;
1475 return false;
1476 };
1476 };
1477
1477
1478 this.submitResolution = function(commentId){
1478 this.submitResolution = function(commentId){
1479 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
1479 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
1480 var commentForm = form.get(0).CommentForm;
1480 var commentForm = form.get(0).CommentForm;
1481
1481
1482 var cm = commentForm.getCmInstance();
1482 var cm = commentForm.getCmInstance();
1483 var renderer = templateContext.visual.default_renderer;
1483 var renderer = templateContext.visual.default_renderer;
1484 if (renderer == 'rst'){
1484 if (renderer == 'rst'){
1485 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
1485 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
1486 } else if (renderer == 'markdown') {
1486 } else if (renderer == 'markdown') {
1487 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
1487 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
1488 } else {
1488 } else {
1489 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
1489 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
1490 }
1490 }
1491
1491
1492 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
1492 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
1493 form.submit();
1493 form.submit();
1494 return false;
1494 return false;
1495 };
1495 };
1496
1496
1497 };
1497 };
1498
1498
1499 window.commentHelp = function(renderer) {
1499 window.commentHelp = function(renderer) {
1500 var funcData = {'renderer': renderer}
1500 var funcData = {'renderer': renderer}
1501 return renderTemplate('commentHelpHovercard', funcData)
1501 return renderTemplate('commentHelpHovercard', funcData)
1502 } No newline at end of file
1502 }
General Comments 0
You need to be logged in to leave comments. Login now