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

Auto status change to "Under Review"

You need to be logged in to leave comments. Login now