##// END OF EJS Templates
comments: added better alerts for failed commenting operations.
marcink -
r4311:a06710aa default
parent child Browse files
Show More
@@ -1,931 +1,932 b''
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 var firefoxAnchorFix = function() {
19 var firefoxAnchorFix = function() {
20 // hack to make anchor links behave properly on firefox, in our inline
20 // hack to make anchor links behave properly on firefox, in our inline
21 // comments generation when comments are injected firefox is misbehaving
21 // comments generation when comments are injected firefox is misbehaving
22 // when jumping to anchor links
22 // when jumping to anchor links
23 if (location.href.indexOf('#') > -1) {
23 if (location.href.indexOf('#') > -1) {
24 location.href += '';
24 location.href += '';
25 }
25 }
26 };
26 };
27
27
28 var linkifyComments = function(comments) {
28 var linkifyComments = function(comments) {
29 var firstCommentId = null;
29 var firstCommentId = null;
30 if (comments) {
30 if (comments) {
31 firstCommentId = $(comments[0]).data('comment-id');
31 firstCommentId = $(comments[0]).data('comment-id');
32 }
32 }
33
33
34 if (firstCommentId){
34 if (firstCommentId){
35 $('#inline-comments-counter').attr('href', '#comment-' + firstCommentId);
35 $('#inline-comments-counter').attr('href', '#comment-' + firstCommentId);
36 }
36 }
37 };
37 };
38
38
39 var bindToggleButtons = function() {
39 var bindToggleButtons = function() {
40 $('.comment-toggle').on('click', function() {
40 $('.comment-toggle').on('click', function() {
41 $(this).parent().nextUntil('tr.line').toggle('inline-comments');
41 $(this).parent().nextUntil('tr.line').toggle('inline-comments');
42 });
42 });
43 };
43 };
44
44
45
45
46
46
47 var _submitAjaxPOST = function(url, postData, successHandler, failHandler) {
47 var _submitAjaxPOST = function(url, postData, successHandler, failHandler) {
48 failHandler = failHandler || function() {};
48 failHandler = failHandler || function() {};
49 postData = toQueryString(postData);
49 postData = toQueryString(postData);
50 var request = $.ajax({
50 var request = $.ajax({
51 url: url,
51 url: url,
52 type: 'POST',
52 type: 'POST',
53 data: postData,
53 data: postData,
54 headers: {'X-PARTIAL-XHR': true}
54 headers: {'X-PARTIAL-XHR': true}
55 })
55 })
56 .done(function (data) {
56 .done(function (data) {
57 successHandler(data);
57 successHandler(data);
58 })
58 })
59 .fail(function (data, textStatus, errorThrown) {
59 .fail(function (data, textStatus, errorThrown) {
60 failHandler(data, textStatus, errorThrown)
60 failHandler(data, textStatus, errorThrown)
61 });
61 });
62 return request;
62 return request;
63 };
63 };
64
64
65
65
66
66
67
67
68 /* Comment form for main and inline comments */
68 /* Comment form for main and inline comments */
69 (function(mod) {
69 (function(mod) {
70
70
71 if (typeof exports == "object" && typeof module == "object") {
71 if (typeof exports == "object" && typeof module == "object") {
72 // CommonJS
72 // CommonJS
73 module.exports = mod();
73 module.exports = mod();
74 }
74 }
75 else {
75 else {
76 // Plain browser env
76 // Plain browser env
77 (this || window).CommentForm = mod();
77 (this || window).CommentForm = mod();
78 }
78 }
79
79
80 })(function() {
80 })(function() {
81 "use strict";
81 "use strict";
82
82
83 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) {
83 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) {
84 if (!(this instanceof CommentForm)) {
84 if (!(this instanceof CommentForm)) {
85 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId);
85 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId);
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 this.submitButton = $(this.submitForm).find('input[type="submit"]');
126 this.submitButton = $(this.submitForm).find('input[type="submit"]');
127 this.submitButtonText = this.submitButton.val();
127 this.submitButtonText = this.submitButton.val();
128
128
129 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
129 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
130 {'repo_name': templateContext.repo_name,
130 {'repo_name': templateContext.repo_name,
131 'commit_id': templateContext.commit_data.commit_id});
131 'commit_id': templateContext.commit_data.commit_id});
132
132
133 if (resolvesCommentId){
133 if (resolvesCommentId){
134 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
134 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
135 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
135 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
136 $(this.commentType).prop('disabled', true);
136 $(this.commentType).prop('disabled', true);
137 $(this.commentType).addClass('disabled');
137 $(this.commentType).addClass('disabled');
138
138
139 // disable select
139 // disable select
140 setTimeout(function() {
140 setTimeout(function() {
141 $(self.statusChange).select2('readonly', true);
141 $(self.statusChange).select2('readonly', true);
142 }, 10);
142 }, 10);
143
143
144 var resolvedInfo = (
144 var resolvedInfo = (
145 '<li class="resolve-action">' +
145 '<li class="resolve-action">' +
146 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
146 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
147 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
147 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
148 '</li>'
148 '</li>'
149 ).format(resolvesCommentId, _gettext('resolve comment'));
149 ).format(resolvesCommentId, _gettext('resolve comment'));
150 $(resolvedInfo).insertAfter($(this.commentType).parent());
150 $(resolvedInfo).insertAfter($(this.commentType).parent());
151 }
151 }
152
152
153 // based on commitId, or pullRequestId decide where do we submit
153 // based on commitId, or pullRequestId decide where do we submit
154 // out data
154 // out data
155 if (this.commitId){
155 if (this.commitId){
156 this.submitUrl = pyroutes.url('repo_commit_comment_create',
156 this.submitUrl = pyroutes.url('repo_commit_comment_create',
157 {'repo_name': templateContext.repo_name,
157 {'repo_name': templateContext.repo_name,
158 'commit_id': this.commitId});
158 'commit_id': this.commitId});
159 this.selfUrl = pyroutes.url('repo_commit',
159 this.selfUrl = pyroutes.url('repo_commit',
160 {'repo_name': templateContext.repo_name,
160 {'repo_name': templateContext.repo_name,
161 'commit_id': this.commitId});
161 'commit_id': this.commitId});
162
162
163 } else if (this.pullRequestId) {
163 } else if (this.pullRequestId) {
164 this.submitUrl = pyroutes.url('pullrequest_comment_create',
164 this.submitUrl = pyroutes.url('pullrequest_comment_create',
165 {'repo_name': templateContext.repo_name,
165 {'repo_name': templateContext.repo_name,
166 'pull_request_id': this.pullRequestId});
166 'pull_request_id': this.pullRequestId});
167 this.selfUrl = pyroutes.url('pullrequest_show',
167 this.selfUrl = pyroutes.url('pullrequest_show',
168 {'repo_name': templateContext.repo_name,
168 {'repo_name': templateContext.repo_name,
169 'pull_request_id': this.pullRequestId});
169 'pull_request_id': this.pullRequestId});
170
170
171 } else {
171 } else {
172 throw new Error(
172 throw new Error(
173 'CommentForm requires pullRequestId, or commitId to be specified.')
173 'CommentForm requires pullRequestId, or commitId to be specified.')
174 }
174 }
175
175
176 // FUNCTIONS and helpers
176 // FUNCTIONS and helpers
177 var self = this;
177 var self = this;
178
178
179 this.isInline = function(){
179 this.isInline = function(){
180 return this.lineNo && this.lineNo != 'general';
180 return this.lineNo && this.lineNo != 'general';
181 };
181 };
182
182
183 this.getCmInstance = function(){
183 this.getCmInstance = function(){
184 return this.cm
184 return this.cm
185 };
185 };
186
186
187 this.setPlaceholder = function(placeholder) {
187 this.setPlaceholder = function(placeholder) {
188 var cm = this.getCmInstance();
188 var cm = this.getCmInstance();
189 if (cm){
189 if (cm){
190 cm.setOption('placeholder', placeholder);
190 cm.setOption('placeholder', placeholder);
191 }
191 }
192 };
192 };
193
193
194 this.getCommentStatus = function() {
194 this.getCommentStatus = function() {
195 return $(this.submitForm).find(this.statusChange).val();
195 return $(this.submitForm).find(this.statusChange).val();
196 };
196 };
197 this.getCommentType = function() {
197 this.getCommentType = function() {
198 return $(this.submitForm).find(this.commentType).val();
198 return $(this.submitForm).find(this.commentType).val();
199 };
199 };
200
200
201 this.getResolvesId = function() {
201 this.getResolvesId = function() {
202 return $(this.submitForm).find(this.resolvesId).val() || null;
202 return $(this.submitForm).find(this.resolvesId).val() || null;
203 };
203 };
204
204
205 this.getClosePr = function() {
205 this.getClosePr = function() {
206 return $(this.submitForm).find(this.closesPr).val() || null;
206 return $(this.submitForm).find(this.closesPr).val() || null;
207 };
207 };
208
208
209 this.markCommentResolved = function(resolvedCommentId){
209 this.markCommentResolved = function(resolvedCommentId){
210 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
210 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
211 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
211 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
212 };
212 };
213
213
214 this.isAllowedToSubmit = function() {
214 this.isAllowedToSubmit = function() {
215 return !$(this.submitButton).prop('disabled');
215 return !$(this.submitButton).prop('disabled');
216 };
216 };
217
217
218 this.initStatusChangeSelector = function(){
218 this.initStatusChangeSelector = function(){
219 var formatChangeStatus = function(state, escapeMarkup) {
219 var formatChangeStatus = function(state, escapeMarkup) {
220 var originalOption = state.element;
220 var originalOption = state.element;
221 var tmpl = '<i class="icon-circle review-status-{0}"></i><span>{1}</span>'.format($(originalOption).data('status'), escapeMarkup(state.text));
221 var tmpl = '<i class="icon-circle review-status-{0}"></i><span>{1}</span>'.format($(originalOption).data('status'), escapeMarkup(state.text));
222 return tmpl
222 return tmpl
223 };
223 };
224 var formatResult = function(result, container, query, escapeMarkup) {
224 var formatResult = function(result, container, query, escapeMarkup) {
225 return formatChangeStatus(result, escapeMarkup);
225 return formatChangeStatus(result, escapeMarkup);
226 };
226 };
227
227
228 var formatSelection = function(data, container, escapeMarkup) {
228 var formatSelection = function(data, container, escapeMarkup) {
229 return formatChangeStatus(data, escapeMarkup);
229 return formatChangeStatus(data, escapeMarkup);
230 };
230 };
231
231
232 $(this.submitForm).find(this.statusChange).select2({
232 $(this.submitForm).find(this.statusChange).select2({
233 placeholder: _gettext('Status Review'),
233 placeholder: _gettext('Status Review'),
234 formatResult: formatResult,
234 formatResult: formatResult,
235 formatSelection: formatSelection,
235 formatSelection: formatSelection,
236 containerCssClass: "drop-menu status_box_menu",
236 containerCssClass: "drop-menu status_box_menu",
237 dropdownCssClass: "drop-menu-dropdown",
237 dropdownCssClass: "drop-menu-dropdown",
238 dropdownAutoWidth: true,
238 dropdownAutoWidth: true,
239 minimumResultsForSearch: -1
239 minimumResultsForSearch: -1
240 });
240 });
241 $(this.submitForm).find(this.statusChange).on('change', function() {
241 $(this.submitForm).find(this.statusChange).on('change', function() {
242 var status = self.getCommentStatus();
242 var status = self.getCommentStatus();
243
243
244 if (status && !self.isInline()) {
244 if (status && !self.isInline()) {
245 $(self.submitButton).prop('disabled', false);
245 $(self.submitButton).prop('disabled', false);
246 }
246 }
247
247
248 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
248 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
249 self.setPlaceholder(placeholderText)
249 self.setPlaceholder(placeholderText)
250 })
250 })
251 };
251 };
252
252
253 // reset the comment form into it's original state
253 // reset the comment form into it's original state
254 this.resetCommentFormState = function(content) {
254 this.resetCommentFormState = function(content) {
255 content = content || '';
255 content = content || '';
256
256
257 $(this.editContainer).show();
257 $(this.editContainer).show();
258 $(this.editButton).parent().addClass('active');
258 $(this.editButton).parent().addClass('active');
259
259
260 $(this.previewContainer).hide();
260 $(this.previewContainer).hide();
261 $(this.previewButton).parent().removeClass('active');
261 $(this.previewButton).parent().removeClass('active');
262
262
263 this.setActionButtonsDisabled(true);
263 this.setActionButtonsDisabled(true);
264 self.cm.setValue(content);
264 self.cm.setValue(content);
265 self.cm.setOption("readOnly", false);
265 self.cm.setOption("readOnly", false);
266
266
267 if (this.resolvesId) {
267 if (this.resolvesId) {
268 // destroy the resolve action
268 // destroy the resolve action
269 $(this.resolvesId).parent().remove();
269 $(this.resolvesId).parent().remove();
270 }
270 }
271 // reset closingPR flag
271 // reset closingPR flag
272 $('.close-pr-input').remove();
272 $('.close-pr-input').remove();
273
273
274 $(this.statusChange).select2('readonly', false);
274 $(this.statusChange).select2('readonly', false);
275 };
275 };
276
276
277 this.globalSubmitSuccessCallback = function(){
277 this.globalSubmitSuccessCallback = function(){
278 // default behaviour is to call GLOBAL hook, if it's registered.
278 // default behaviour is to call GLOBAL hook, if it's registered.
279 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
279 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
280 commentFormGlobalSubmitSuccessCallback()
280 commentFormGlobalSubmitSuccessCallback()
281 }
281 }
282 };
282 };
283
283
284 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
284 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
285 return _submitAjaxPOST(url, postData, successHandler, failHandler);
285 return _submitAjaxPOST(url, postData, successHandler, failHandler);
286 };
286 };
287
287
288 // overwrite a submitHandler, we need to do it for inline comments
288 // overwrite a submitHandler, we need to do it for inline comments
289 this.setHandleFormSubmit = function(callback) {
289 this.setHandleFormSubmit = function(callback) {
290 this.handleFormSubmit = callback;
290 this.handleFormSubmit = callback;
291 };
291 };
292
292
293 // overwrite a submitSuccessHandler
293 // overwrite a submitSuccessHandler
294 this.setGlobalSubmitSuccessCallback = function(callback) {
294 this.setGlobalSubmitSuccessCallback = function(callback) {
295 this.globalSubmitSuccessCallback = callback;
295 this.globalSubmitSuccessCallback = callback;
296 };
296 };
297
297
298 // default handler for for submit for main comments
298 // default handler for for submit for main comments
299 this.handleFormSubmit = function() {
299 this.handleFormSubmit = function() {
300 var text = self.cm.getValue();
300 var text = self.cm.getValue();
301 var status = self.getCommentStatus();
301 var status = self.getCommentStatus();
302 var commentType = self.getCommentType();
302 var commentType = self.getCommentType();
303 var resolvesCommentId = self.getResolvesId();
303 var resolvesCommentId = self.getResolvesId();
304 var closePullRequest = self.getClosePr();
304 var closePullRequest = self.getClosePr();
305
305
306 if (text === "" && !status) {
306 if (text === "" && !status) {
307 return;
307 return;
308 }
308 }
309
309
310 var excludeCancelBtn = false;
310 var excludeCancelBtn = false;
311 var submitEvent = true;
311 var submitEvent = true;
312 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
312 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
313 self.cm.setOption("readOnly", true);
313 self.cm.setOption("readOnly", true);
314
314
315 var postData = {
315 var postData = {
316 'text': text,
316 'text': text,
317 'changeset_status': status,
317 'changeset_status': status,
318 'comment_type': commentType,
318 'comment_type': commentType,
319 'csrf_token': CSRF_TOKEN
319 'csrf_token': CSRF_TOKEN
320 };
320 };
321
321
322 if (resolvesCommentId) {
322 if (resolvesCommentId) {
323 postData['resolves_comment_id'] = resolvesCommentId;
323 postData['resolves_comment_id'] = resolvesCommentId;
324 }
324 }
325
325
326 if (closePullRequest) {
326 if (closePullRequest) {
327 postData['close_pull_request'] = true;
327 postData['close_pull_request'] = true;
328 }
328 }
329
329
330 var submitSuccessCallback = function(o) {
330 var submitSuccessCallback = function(o) {
331 // reload page if we change status for single commit.
331 // reload page if we change status for single commit.
332 if (status && self.commitId) {
332 if (status && self.commitId) {
333 location.reload(true);
333 location.reload(true);
334 } else {
334 } else {
335 $('#injected_page_comments').append(o.rendered_text);
335 $('#injected_page_comments').append(o.rendered_text);
336 self.resetCommentFormState();
336 self.resetCommentFormState();
337 timeagoActivate();
337 timeagoActivate();
338 tooltipActivate();
338 tooltipActivate();
339
339
340 // mark visually which comment was resolved
340 // mark visually which comment was resolved
341 if (resolvesCommentId) {
341 if (resolvesCommentId) {
342 self.markCommentResolved(resolvesCommentId);
342 self.markCommentResolved(resolvesCommentId);
343 }
343 }
344 }
344 }
345
345
346 // run global callback on submit
346 // run global callback on submit
347 self.globalSubmitSuccessCallback();
347 self.globalSubmitSuccessCallback();
348
348
349 };
349 };
350 var submitFailCallback = function(data) {
350 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
351 alert(
351 var prefix = "Error while submitting comment.\n"
352 "Error while submitting comment.\n" +
352 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
353 "Error code {0} ({1}).".format(data.status, data.statusText)
353 ajaxErrorSwal(message);
354 );
355 self.resetCommentFormState(text);
354 self.resetCommentFormState(text);
356 };
355 };
357 self.submitAjaxPOST(
356 self.submitAjaxPOST(
358 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
357 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
359 };
358 };
360
359
361 this.previewSuccessCallback = function(o) {
360 this.previewSuccessCallback = function(o) {
362 $(self.previewBoxSelector).html(o);
361 $(self.previewBoxSelector).html(o);
363 $(self.previewBoxSelector).removeClass('unloaded');
362 $(self.previewBoxSelector).removeClass('unloaded');
364
363
365 // swap buttons, making preview active
364 // swap buttons, making preview active
366 $(self.previewButton).parent().addClass('active');
365 $(self.previewButton).parent().addClass('active');
367 $(self.editButton).parent().removeClass('active');
366 $(self.editButton).parent().removeClass('active');
368
367
369 // unlock buttons
368 // unlock buttons
370 self.setActionButtonsDisabled(false);
369 self.setActionButtonsDisabled(false);
371 };
370 };
372
371
373 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
372 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
374 excludeCancelBtn = excludeCancelBtn || false;
373 excludeCancelBtn = excludeCancelBtn || false;
375 submitEvent = submitEvent || false;
374 submitEvent = submitEvent || false;
376
375
377 $(this.editButton).prop('disabled', state);
376 $(this.editButton).prop('disabled', state);
378 $(this.previewButton).prop('disabled', state);
377 $(this.previewButton).prop('disabled', state);
379
378
380 if (!excludeCancelBtn) {
379 if (!excludeCancelBtn) {
381 $(this.cancelButton).prop('disabled', state);
380 $(this.cancelButton).prop('disabled', state);
382 }
381 }
383
382
384 var submitState = state;
383 var submitState = state;
385 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
384 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
386 // if the value of commit review status is set, we allow
385 // if the value of commit review status is set, we allow
387 // submit button, but only on Main form, isInline means inline
386 // submit button, but only on Main form, isInline means inline
388 submitState = false
387 submitState = false
389 }
388 }
390
389
391 $(this.submitButton).prop('disabled', submitState);
390 $(this.submitButton).prop('disabled', submitState);
392 if (submitEvent) {
391 if (submitEvent) {
393 $(this.submitButton).val(_gettext('Submitting...'));
392 $(this.submitButton).val(_gettext('Submitting...'));
394 } else {
393 } else {
395 $(this.submitButton).val(this.submitButtonText);
394 $(this.submitButton).val(this.submitButtonText);
396 }
395 }
397
396
398 };
397 };
399
398
400 // lock preview/edit/submit buttons on load, but exclude cancel button
399 // lock preview/edit/submit buttons on load, but exclude cancel button
401 var excludeCancelBtn = true;
400 var excludeCancelBtn = true;
402 this.setActionButtonsDisabled(true, excludeCancelBtn);
401 this.setActionButtonsDisabled(true, excludeCancelBtn);
403
402
404 // anonymous users don't have access to initialized CM instance
403 // anonymous users don't have access to initialized CM instance
405 if (this.cm !== undefined){
404 if (this.cm !== undefined){
406 this.cm.on('change', function(cMirror) {
405 this.cm.on('change', function(cMirror) {
407 if (cMirror.getValue() === "") {
406 if (cMirror.getValue() === "") {
408 self.setActionButtonsDisabled(true, excludeCancelBtn)
407 self.setActionButtonsDisabled(true, excludeCancelBtn)
409 } else {
408 } else {
410 self.setActionButtonsDisabled(false, excludeCancelBtn)
409 self.setActionButtonsDisabled(false, excludeCancelBtn)
411 }
410 }
412 });
411 });
413 }
412 }
414
413
415 $(this.editButton).on('click', function(e) {
414 $(this.editButton).on('click', function(e) {
416 e.preventDefault();
415 e.preventDefault();
417
416
418 $(self.previewButton).parent().removeClass('active');
417 $(self.previewButton).parent().removeClass('active');
419 $(self.previewContainer).hide();
418 $(self.previewContainer).hide();
420
419
421 $(self.editButton).parent().addClass('active');
420 $(self.editButton).parent().addClass('active');
422 $(self.editContainer).show();
421 $(self.editContainer).show();
423
422
424 });
423 });
425
424
426 $(this.previewButton).on('click', function(e) {
425 $(this.previewButton).on('click', function(e) {
427 e.preventDefault();
426 e.preventDefault();
428 var text = self.cm.getValue();
427 var text = self.cm.getValue();
429
428
430 if (text === "") {
429 if (text === "") {
431 return;
430 return;
432 }
431 }
433
432
434 var postData = {
433 var postData = {
435 'text': text,
434 'text': text,
436 'renderer': templateContext.visual.default_renderer,
435 'renderer': templateContext.visual.default_renderer,
437 'csrf_token': CSRF_TOKEN
436 'csrf_token': CSRF_TOKEN
438 };
437 };
439
438
440 // lock ALL buttons on preview
439 // lock ALL buttons on preview
441 self.setActionButtonsDisabled(true);
440 self.setActionButtonsDisabled(true);
442
441
443 $(self.previewBoxSelector).addClass('unloaded');
442 $(self.previewBoxSelector).addClass('unloaded');
444 $(self.previewBoxSelector).html(_gettext('Loading ...'));
443 $(self.previewBoxSelector).html(_gettext('Loading ...'));
445
444
446 $(self.editContainer).hide();
445 $(self.editContainer).hide();
447 $(self.previewContainer).show();
446 $(self.previewContainer).show();
448
447
449 // by default we reset state of comment preserving the text
448 // by default we reset state of comment preserving the text
450 var previewFailCallback = function(data){
449 var previewFailCallback = function(jqXHR, textStatus, errorThrown) {
451 alert(
450 var prefix = "Error while preview of comment.\n"
452 "Error while preview of comment.\n" +
451 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
453 "Error code {0} ({1}).".format(data.status, data.statusText)
452 ajaxErrorSwal(message);
454 );
453
455 self.resetCommentFormState(text)
454 self.resetCommentFormState(text)
456 };
455 };
457 self.submitAjaxPOST(
456 self.submitAjaxPOST(
458 self.previewUrl, postData, self.previewSuccessCallback,
457 self.previewUrl, postData, self.previewSuccessCallback,
459 previewFailCallback);
458 previewFailCallback);
460
459
461 $(self.previewButton).parent().addClass('active');
460 $(self.previewButton).parent().addClass('active');
462 $(self.editButton).parent().removeClass('active');
461 $(self.editButton).parent().removeClass('active');
463 });
462 });
464
463
465 $(this.submitForm).submit(function(e) {
464 $(this.submitForm).submit(function(e) {
466 e.preventDefault();
465 e.preventDefault();
467 var allowedToSubmit = self.isAllowedToSubmit();
466 var allowedToSubmit = self.isAllowedToSubmit();
468 if (!allowedToSubmit){
467 if (!allowedToSubmit){
469 return false;
468 return false;
470 }
469 }
471 self.handleFormSubmit();
470 self.handleFormSubmit();
472 });
471 });
473
472
474 }
473 }
475
474
476 return CommentForm;
475 return CommentForm;
477 });
476 });
478
477
479 /* comments controller */
478 /* comments controller */
480 var CommentsController = function() {
479 var CommentsController = function() {
481 var mainComment = '#text';
480 var mainComment = '#text';
482 var self = this;
481 var self = this;
483
482
484 this.cancelComment = function(node) {
483 this.cancelComment = function(node) {
485 var $node = $(node);
484 var $node = $(node);
486 var $td = $node.closest('td');
485 var $td = $node.closest('td');
487 $node.closest('.comment-inline-form').remove();
486 $node.closest('.comment-inline-form').remove();
488 return false;
487 return false;
489 };
488 };
490
489
491 this.getLineNumber = function(node) {
490 this.getLineNumber = function(node) {
492 var $node = $(node);
491 var $node = $(node);
493 var lineNo = $node.closest('td').attr('data-line-no');
492 var lineNo = $node.closest('td').attr('data-line-no');
494 if (lineNo === undefined && $node.data('commentInline')){
493 if (lineNo === undefined && $node.data('commentInline')){
495 lineNo = $node.data('commentLineNo')
494 lineNo = $node.data('commentLineNo')
496 }
495 }
497
496
498 return lineNo
497 return lineNo
499 };
498 };
500
499
501 this.scrollToComment = function(node, offset, outdated) {
500 this.scrollToComment = function(node, offset, outdated) {
502 if (offset === undefined) {
501 if (offset === undefined) {
503 offset = 0;
502 offset = 0;
504 }
503 }
505 var outdated = outdated || false;
504 var outdated = outdated || false;
506 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
505 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
507
506
508 if (!node) {
507 if (!node) {
509 node = $('.comment-selected');
508 node = $('.comment-selected');
510 if (!node.length) {
509 if (!node.length) {
511 node = $('comment-current')
510 node = $('comment-current')
512 }
511 }
513 }
512 }
514
513
515 $wrapper = $(node).closest('div.comment');
514 $wrapper = $(node).closest('div.comment');
516
515
517 // show hidden comment when referenced.
516 // show hidden comment when referenced.
518 if (!$wrapper.is(':visible')){
517 if (!$wrapper.is(':visible')){
519 $wrapper.show();
518 $wrapper.show();
520 }
519 }
521
520
522 $comment = $(node).closest(klass);
521 $comment = $(node).closest(klass);
523 $comments = $(klass);
522 $comments = $(klass);
524
523
525 $('.comment-selected').removeClass('comment-selected');
524 $('.comment-selected').removeClass('comment-selected');
526
525
527 var nextIdx = $(klass).index($comment) + offset;
526 var nextIdx = $(klass).index($comment) + offset;
528 if (nextIdx >= $comments.length) {
527 if (nextIdx >= $comments.length) {
529 nextIdx = 0;
528 nextIdx = 0;
530 }
529 }
531 var $next = $(klass).eq(nextIdx);
530 var $next = $(klass).eq(nextIdx);
532
531
533 var $cb = $next.closest('.cb');
532 var $cb = $next.closest('.cb');
534 $cb.removeClass('cb-collapsed');
533 $cb.removeClass('cb-collapsed');
535
534
536 var $filediffCollapseState = $cb.closest('.filediff').prev();
535 var $filediffCollapseState = $cb.closest('.filediff').prev();
537 $filediffCollapseState.prop('checked', false);
536 $filediffCollapseState.prop('checked', false);
538 $next.addClass('comment-selected');
537 $next.addClass('comment-selected');
539 scrollToElement($next);
538 scrollToElement($next);
540 return false;
539 return false;
541 };
540 };
542
541
543 this.nextComment = function(node) {
542 this.nextComment = function(node) {
544 return self.scrollToComment(node, 1);
543 return self.scrollToComment(node, 1);
545 };
544 };
546
545
547 this.prevComment = function(node) {
546 this.prevComment = function(node) {
548 return self.scrollToComment(node, -1);
547 return self.scrollToComment(node, -1);
549 };
548 };
550
549
551 this.nextOutdatedComment = function(node) {
550 this.nextOutdatedComment = function(node) {
552 return self.scrollToComment(node, 1, true);
551 return self.scrollToComment(node, 1, true);
553 };
552 };
554
553
555 this.prevOutdatedComment = function(node) {
554 this.prevOutdatedComment = function(node) {
556 return self.scrollToComment(node, -1, true);
555 return self.scrollToComment(node, -1, true);
557 };
556 };
558
557
559 this.deleteComment = function(node) {
558 this.deleteComment = function(node) {
560 if (!confirm(_gettext('Delete this comment?'))) {
559 if (!confirm(_gettext('Delete this comment?'))) {
561 return false;
560 return false;
562 }
561 }
563 var $node = $(node);
562 var $node = $(node);
564 var $td = $node.closest('td');
563 var $td = $node.closest('td');
565 var $comment = $node.closest('.comment');
564 var $comment = $node.closest('.comment');
566 var comment_id = $comment.attr('data-comment-id');
565 var comment_id = $comment.attr('data-comment-id');
567 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
566 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
568 var postData = {
567 var postData = {
569 'csrf_token': CSRF_TOKEN
568 'csrf_token': CSRF_TOKEN
570 };
569 };
571
570
572 $comment.addClass('comment-deleting');
571 $comment.addClass('comment-deleting');
573 $comment.hide('fast');
572 $comment.hide('fast');
574
573
575 var success = function(response) {
574 var success = function(response) {
576 $comment.remove();
575 $comment.remove();
577 return false;
576 return false;
578 };
577 };
579 var failure = function(data, textStatus, xhr) {
578 var failure = function(jqXHR, textStatus, errorThrown) {
580 alert("error processing request: " + textStatus);
579 var prefix = "Error while deleting this comment.\n"
580 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
581 ajaxErrorSwal(message);
582
581 $comment.show('fast');
583 $comment.show('fast');
582 $comment.removeClass('comment-deleting');
584 $comment.removeClass('comment-deleting');
583 return false;
585 return false;
584 };
586 };
585 ajaxPOST(url, postData, success, failure);
587 ajaxPOST(url, postData, success, failure);
586 };
588 };
587
589
588 this.toggleWideMode = function (node) {
590 this.toggleWideMode = function (node) {
589 if ($('#content').hasClass('wrapper')) {
591 if ($('#content').hasClass('wrapper')) {
590 $('#content').removeClass("wrapper");
592 $('#content').removeClass("wrapper");
591 $('#content').addClass("wide-mode-wrapper");
593 $('#content').addClass("wide-mode-wrapper");
592 $(node).addClass('btn-success');
594 $(node).addClass('btn-success');
593 return true
595 return true
594 } else {
596 } else {
595 $('#content').removeClass("wide-mode-wrapper");
597 $('#content').removeClass("wide-mode-wrapper");
596 $('#content').addClass("wrapper");
598 $('#content').addClass("wrapper");
597 $(node).removeClass('btn-success');
599 $(node).removeClass('btn-success');
598 return false
600 return false
599 }
601 }
600
602
601 };
603 };
602
604
603 this.toggleComments = function(node, show) {
605 this.toggleComments = function(node, show) {
604 var $filediff = $(node).closest('.filediff');
606 var $filediff = $(node).closest('.filediff');
605 if (show === true) {
607 if (show === true) {
606 $filediff.removeClass('hide-comments');
608 $filediff.removeClass('hide-comments');
607 } else if (show === false) {
609 } else if (show === false) {
608 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
610 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
609 $filediff.addClass('hide-comments');
611 $filediff.addClass('hide-comments');
610 } else {
612 } else {
611 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
613 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
612 $filediff.toggleClass('hide-comments');
614 $filediff.toggleClass('hide-comments');
613 }
615 }
614 return false;
616 return false;
615 };
617 };
616
618
617 this.toggleLineComments = function(node) {
619 this.toggleLineComments = function(node) {
618 self.toggleComments(node, true);
620 self.toggleComments(node, true);
619 var $node = $(node);
621 var $node = $(node);
620 // mark outdated comments as visible before the toggle;
622 // mark outdated comments as visible before the toggle;
621 $(node.closest('tr')).find('.comment-outdated').show();
623 $(node.closest('tr')).find('.comment-outdated').show();
622 $node.closest('tr').toggleClass('hide-line-comments');
624 $node.closest('tr').toggleClass('hide-line-comments');
623 };
625 };
624
626
625 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId){
627 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId){
626 var pullRequestId = templateContext.pull_request_data.pull_request_id;
628 var pullRequestId = templateContext.pull_request_data.pull_request_id;
627 var commitId = templateContext.commit_data.commit_id;
629 var commitId = templateContext.commit_data.commit_id;
628
630
629 var commentForm = new CommentForm(
631 var commentForm = new CommentForm(
630 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId);
632 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId);
631 var cm = commentForm.getCmInstance();
633 var cm = commentForm.getCmInstance();
632
634
633 if (resolvesCommentId){
635 if (resolvesCommentId){
634 var placeholderText = _gettext('Leave a resolution comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
636 var placeholderText = _gettext('Leave a resolution comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
635 }
637 }
636
638
637 setTimeout(function() {
639 setTimeout(function() {
638 // callbacks
640 // callbacks
639 if (cm !== undefined) {
641 if (cm !== undefined) {
640 commentForm.setPlaceholder(placeholderText);
642 commentForm.setPlaceholder(placeholderText);
641 if (commentForm.isInline()) {
643 if (commentForm.isInline()) {
642 cm.focus();
644 cm.focus();
643 cm.refresh();
645 cm.refresh();
644 }
646 }
645 }
647 }
646 }, 10);
648 }, 10);
647
649
648 // trigger scrolldown to the resolve comment, since it might be away
650 // trigger scrolldown to the resolve comment, since it might be away
649 // from the clicked
651 // from the clicked
650 if (resolvesCommentId){
652 if (resolvesCommentId){
651 var actionNode = $(commentForm.resolvesActionId).offset();
653 var actionNode = $(commentForm.resolvesActionId).offset();
652
654
653 setTimeout(function() {
655 setTimeout(function() {
654 if (actionNode) {
656 if (actionNode) {
655 $('body, html').animate({scrollTop: actionNode.top}, 10);
657 $('body, html').animate({scrollTop: actionNode.top}, 10);
656 }
658 }
657 }, 100);
659 }, 100);
658 }
660 }
659
661
660 // add dropzone support
662 // add dropzone support
661 var insertAttachmentText = function (cm, attachmentName, attachmentStoreUrl, isRendered) {
663 var insertAttachmentText = function (cm, attachmentName, attachmentStoreUrl, isRendered) {
662 var renderer = templateContext.visual.default_renderer;
664 var renderer = templateContext.visual.default_renderer;
663 if (renderer == 'rst') {
665 if (renderer == 'rst') {
664 var attachmentUrl = '`#{0} <{1}>`_'.format(attachmentName, attachmentStoreUrl);
666 var attachmentUrl = '`#{0} <{1}>`_'.format(attachmentName, attachmentStoreUrl);
665 if (isRendered){
667 if (isRendered){
666 attachmentUrl = '\n.. image:: {0}'.format(attachmentStoreUrl);
668 attachmentUrl = '\n.. image:: {0}'.format(attachmentStoreUrl);
667 }
669 }
668 } else if (renderer == 'markdown') {
670 } else if (renderer == 'markdown') {
669 var attachmentUrl = '[{0}]({1})'.format(attachmentName, attachmentStoreUrl);
671 var attachmentUrl = '[{0}]({1})'.format(attachmentName, attachmentStoreUrl);
670 if (isRendered){
672 if (isRendered){
671 attachmentUrl = '!' + attachmentUrl;
673 attachmentUrl = '!' + attachmentUrl;
672 }
674 }
673 } else {
675 } else {
674 var attachmentUrl = '{}'.format(attachmentStoreUrl);
676 var attachmentUrl = '{}'.format(attachmentStoreUrl);
675 }
677 }
676 cm.replaceRange(attachmentUrl+'\n', CodeMirror.Pos(cm.lastLine()));
678 cm.replaceRange(attachmentUrl+'\n', CodeMirror.Pos(cm.lastLine()));
677
679
678 return false;
680 return false;
679 };
681 };
680
682
681 //see: https://www.dropzonejs.com/#configuration
683 //see: https://www.dropzonejs.com/#configuration
682 var storeUrl = pyroutes.url('repo_commit_comment_attachment_upload',
684 var storeUrl = pyroutes.url('repo_commit_comment_attachment_upload',
683 {'repo_name': templateContext.repo_name,
685 {'repo_name': templateContext.repo_name,
684 'commit_id': templateContext.commit_data.commit_id})
686 'commit_id': templateContext.commit_data.commit_id})
685
687
686 var previewTmpl = $(formElement).find('.comment-attachment-uploader-template').get(0);
688 var previewTmpl = $(formElement).find('.comment-attachment-uploader-template').get(0);
687 if (previewTmpl !== undefined){
689 if (previewTmpl !== undefined){
688 var selectLink = $(formElement).find('.pick-attachment').get(0);
690 var selectLink = $(formElement).find('.pick-attachment').get(0);
689 $(formElement).find('.comment-attachment-uploader').dropzone({
691 $(formElement).find('.comment-attachment-uploader').dropzone({
690 url: storeUrl,
692 url: storeUrl,
691 headers: {"X-CSRF-Token": CSRF_TOKEN},
693 headers: {"X-CSRF-Token": CSRF_TOKEN},
692 paramName: function () {
694 paramName: function () {
693 return "attachment"
695 return "attachment"
694 }, // The name that will be used to transfer the file
696 }, // The name that will be used to transfer the file
695 clickable: selectLink,
697 clickable: selectLink,
696 parallelUploads: 1,
698 parallelUploads: 1,
697 maxFiles: 10,
699 maxFiles: 10,
698 maxFilesize: templateContext.attachment_store.max_file_size_mb,
700 maxFilesize: templateContext.attachment_store.max_file_size_mb,
699 uploadMultiple: false,
701 uploadMultiple: false,
700 autoProcessQueue: true, // if false queue will not be processed automatically.
702 autoProcessQueue: true, // if false queue will not be processed automatically.
701 createImageThumbnails: false,
703 createImageThumbnails: false,
702 previewTemplate: previewTmpl.innerHTML,
704 previewTemplate: previewTmpl.innerHTML,
703
705
704 accept: function (file, done) {
706 accept: function (file, done) {
705 done();
707 done();
706 },
708 },
707 init: function () {
709 init: function () {
708
710
709 this.on("sending", function (file, xhr, formData) {
711 this.on("sending", function (file, xhr, formData) {
710 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').hide();
712 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').hide();
711 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').show();
713 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').show();
712 });
714 });
713
715
714 this.on("success", function (file, response) {
716 this.on("success", function (file, response) {
715 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').show();
717 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').show();
716 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
718 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
717
719
718 var isRendered = false;
720 var isRendered = false;
719 var ext = file.name.split('.').pop();
721 var ext = file.name.split('.').pop();
720 var imageExts = templateContext.attachment_store.image_ext;
722 var imageExts = templateContext.attachment_store.image_ext;
721 if (imageExts.indexOf(ext) !== -1){
723 if (imageExts.indexOf(ext) !== -1){
722 isRendered = true;
724 isRendered = true;
723 }
725 }
724
726
725 insertAttachmentText(cm, file.name, response.repo_fqn_access_path, isRendered)
727 insertAttachmentText(cm, file.name, response.repo_fqn_access_path, isRendered)
726 });
728 });
727
729
728 this.on("error", function (file, errorMessage, xhr) {
730 this.on("error", function (file, errorMessage, xhr) {
729 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
731 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
730
732
731 var error = null;
733 var error = null;
732
734
733 if (xhr !== undefined){
735 if (xhr !== undefined){
734 var httpStatus = xhr.status + " " + xhr.statusText;
736 var httpStatus = xhr.status + " " + xhr.statusText;
735 if (xhr !== undefined && xhr.status >= 500) {
737 if (xhr !== undefined && xhr.status >= 500) {
736 error = httpStatus;
738 error = httpStatus;
737 }
739 }
738 }
740 }
739
741
740 if (error === null) {
742 if (error === null) {
741 error = errorMessage.error || errorMessage || httpStatus;
743 error = errorMessage.error || errorMessage || httpStatus;
742 }
744 }
743 $(file.previewElement).find('.dz-error-message').html('ERROR: {0}'.format(error));
745 $(file.previewElement).find('.dz-error-message').html('ERROR: {0}'.format(error));
744
746
745 });
747 });
746 }
748 }
747 });
749 });
748 }
750 }
749 return commentForm;
751 return commentForm;
750 };
752 };
751
753
752 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
754 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
753
755
754 var tmpl = $('#cb-comment-general-form-template').html();
756 var tmpl = $('#cb-comment-general-form-template').html();
755 tmpl = tmpl.format(null, 'general');
757 tmpl = tmpl.format(null, 'general');
756 var $form = $(tmpl);
758 var $form = $(tmpl);
757
759
758 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
760 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
759 var curForm = $formPlaceholder.find('form');
761 var curForm = $formPlaceholder.find('form');
760 if (curForm){
762 if (curForm){
761 curForm.remove();
763 curForm.remove();
762 }
764 }
763 $formPlaceholder.append($form);
765 $formPlaceholder.append($form);
764
766
765 var _form = $($form[0]);
767 var _form = $($form[0]);
766 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
768 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
767 var commentForm = this.createCommentForm(
769 var commentForm = this.createCommentForm(
768 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId);
770 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId);
769 commentForm.initStatusChangeSelector();
771 commentForm.initStatusChangeSelector();
770
772
771 return commentForm;
773 return commentForm;
772 };
774 };
773
775
774 this.createComment = function(node, resolutionComment) {
776 this.createComment = function(node, resolutionComment) {
775 var resolvesCommentId = resolutionComment || null;
777 var resolvesCommentId = resolutionComment || null;
776 var $node = $(node);
778 var $node = $(node);
777 var $td = $node.closest('td');
779 var $td = $node.closest('td');
778 var $form = $td.find('.comment-inline-form');
780 var $form = $td.find('.comment-inline-form');
779
781
780 if (!$form.length) {
782 if (!$form.length) {
781
783
782 var $filediff = $node.closest('.filediff');
784 var $filediff = $node.closest('.filediff');
783 $filediff.removeClass('hide-comments');
785 $filediff.removeClass('hide-comments');
784 var f_path = $filediff.attr('data-f-path');
786 var f_path = $filediff.attr('data-f-path');
785 var lineno = self.getLineNumber(node);
787 var lineno = self.getLineNumber(node);
786 // create a new HTML from template
788 // create a new HTML from template
787 var tmpl = $('#cb-comment-inline-form-template').html();
789 var tmpl = $('#cb-comment-inline-form-template').html();
788 tmpl = tmpl.format(escapeHtml(f_path), lineno);
790 tmpl = tmpl.format(escapeHtml(f_path), lineno);
789 $form = $(tmpl);
791 $form = $(tmpl);
790
792
791 var $comments = $td.find('.inline-comments');
793 var $comments = $td.find('.inline-comments');
792 if (!$comments.length) {
794 if (!$comments.length) {
793 $comments = $(
795 $comments = $(
794 $('#cb-comments-inline-container-template').html());
796 $('#cb-comments-inline-container-template').html());
795 $td.append($comments);
797 $td.append($comments);
796 }
798 }
797
799
798 $td.find('.cb-comment-add-button').before($form);
800 $td.find('.cb-comment-add-button').before($form);
799
801
800 var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno);
802 var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno);
801 var _form = $($form[0]).find('form');
803 var _form = $($form[0]).find('form');
802 var autocompleteActions = ['as_note', 'as_todo'];
804 var autocompleteActions = ['as_note', 'as_todo'];
803 var commentForm = this.createCommentForm(
805 var commentForm = this.createCommentForm(
804 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId);
806 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId);
805
807
806 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
808 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
807 form: _form,
809 form: _form,
808 parent: $td[0],
810 parent: $td[0],
809 lineno: lineno,
811 lineno: lineno,
810 f_path: f_path}
812 f_path: f_path}
811 );
813 );
812
814
813 // set a CUSTOM submit handler for inline comments.
815 // set a CUSTOM submit handler for inline comments.
814 commentForm.setHandleFormSubmit(function(o) {
816 commentForm.setHandleFormSubmit(function(o) {
815 var text = commentForm.cm.getValue();
817 var text = commentForm.cm.getValue();
816 var commentType = commentForm.getCommentType();
818 var commentType = commentForm.getCommentType();
817 var resolvesCommentId = commentForm.getResolvesId();
819 var resolvesCommentId = commentForm.getResolvesId();
818
820
819 if (text === "") {
821 if (text === "") {
820 return;
822 return;
821 }
823 }
822
824
823 if (lineno === undefined) {
825 if (lineno === undefined) {
824 alert('missing line !');
826 alert('missing line !');
825 return;
827 return;
826 }
828 }
827 if (f_path === undefined) {
829 if (f_path === undefined) {
828 alert('missing file path !');
830 alert('missing file path !');
829 return;
831 return;
830 }
832 }
831
833
832 var excludeCancelBtn = false;
834 var excludeCancelBtn = false;
833 var submitEvent = true;
835 var submitEvent = true;
834 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
836 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
835 commentForm.cm.setOption("readOnly", true);
837 commentForm.cm.setOption("readOnly", true);
836 var postData = {
838 var postData = {
837 'text': text,
839 'text': text,
838 'f_path': f_path,
840 'f_path': f_path,
839 'line': lineno,
841 'line': lineno,
840 'comment_type': commentType,
842 'comment_type': commentType,
841 'csrf_token': CSRF_TOKEN
843 'csrf_token': CSRF_TOKEN
842 };
844 };
843 if (resolvesCommentId){
845 if (resolvesCommentId){
844 postData['resolves_comment_id'] = resolvesCommentId;
846 postData['resolves_comment_id'] = resolvesCommentId;
845 }
847 }
846
848
847 var submitSuccessCallback = function(json_data) {
849 var submitSuccessCallback = function(json_data) {
848 $form.remove();
850 $form.remove();
849 try {
851 try {
850 var html = json_data.rendered_text;
852 var html = json_data.rendered_text;
851 var lineno = json_data.line_no;
853 var lineno = json_data.line_no;
852 var target_id = json_data.target_id;
854 var target_id = json_data.target_id;
853
855
854 $comments.find('.cb-comment-add-button').before(html);
856 $comments.find('.cb-comment-add-button').before(html);
855
857
856 //mark visually which comment was resolved
858 //mark visually which comment was resolved
857 if (resolvesCommentId) {
859 if (resolvesCommentId) {
858 commentForm.markCommentResolved(resolvesCommentId);
860 commentForm.markCommentResolved(resolvesCommentId);
859 }
861 }
860
862
861 // run global callback on submit
863 // run global callback on submit
862 commentForm.globalSubmitSuccessCallback();
864 commentForm.globalSubmitSuccessCallback();
863
865
864 } catch (e) {
866 } catch (e) {
865 console.error(e);
867 console.error(e);
866 }
868 }
867
869
868 // re trigger the linkification of next/prev navigation
870 // re trigger the linkification of next/prev navigation
869 linkifyComments($('.inline-comment-injected'));
871 linkifyComments($('.inline-comment-injected'));
870 timeagoActivate();
872 timeagoActivate();
871 tooltipActivate();
873 tooltipActivate();
872
874
873 if (window.updateSticky !== undefined) {
875 if (window.updateSticky !== undefined) {
874 // potentially our comments change the active window size, so we
876 // potentially our comments change the active window size, so we
875 // notify sticky elements
877 // notify sticky elements
876 updateSticky()
878 updateSticky()
877 }
879 }
878
880
879 commentForm.setActionButtonsDisabled(false);
881 commentForm.setActionButtonsDisabled(false);
880
882
881 };
883 };
882 var submitFailCallback = function(data){
884 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
883 alert(
885 var prefix = "Error while submitting comment.\n"
884 "Error while submitting comment.\n" +
886 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
885 "Error code {0} ({1}).".format(data.status, data.statusText)
887 ajaxErrorSwal(message);
886 );
887 commentForm.resetCommentFormState(text)
888 commentForm.resetCommentFormState(text)
888 };
889 };
889 commentForm.submitAjaxPOST(
890 commentForm.submitAjaxPOST(
890 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
891 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
891 });
892 });
892 }
893 }
893
894
894 $form.addClass('comment-inline-form-open');
895 $form.addClass('comment-inline-form-open');
895 };
896 };
896
897
897 this.createResolutionComment = function(commentId){
898 this.createResolutionComment = function(commentId){
898 // hide the trigger text
899 // hide the trigger text
899 $('#resolve-comment-{0}'.format(commentId)).hide();
900 $('#resolve-comment-{0}'.format(commentId)).hide();
900
901
901 var comment = $('#comment-'+commentId);
902 var comment = $('#comment-'+commentId);
902 var commentData = comment.data();
903 var commentData = comment.data();
903 if (commentData.commentInline) {
904 if (commentData.commentInline) {
904 this.createComment(comment, commentId)
905 this.createComment(comment, commentId)
905 } else {
906 } else {
906 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
907 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
907 }
908 }
908
909
909 return false;
910 return false;
910 };
911 };
911
912
912 this.submitResolution = function(commentId){
913 this.submitResolution = function(commentId){
913 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
914 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
914 var commentForm = form.get(0).CommentForm;
915 var commentForm = form.get(0).CommentForm;
915
916
916 var cm = commentForm.getCmInstance();
917 var cm = commentForm.getCmInstance();
917 var renderer = templateContext.visual.default_renderer;
918 var renderer = templateContext.visual.default_renderer;
918 if (renderer == 'rst'){
919 if (renderer == 'rst'){
919 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
920 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
920 } else if (renderer == 'markdown') {
921 } else if (renderer == 'markdown') {
921 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
922 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
922 } else {
923 } else {
923 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
924 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
924 }
925 }
925
926
926 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
927 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
927 form.submit();
928 form.submit();
928 return false;
929 return false;
929 };
930 };
930
931
931 };
932 };
General Comments 0
You need to be logged in to leave comments. Login now