##// END OF EJS Templates
comments: escape file-paths on commenting to prevent html breakage
ergo -
r2182:2a2643df default
parent child Browse files
Show More
@@ -1,830 +1,830 b''
1 // # Copyright (C) 2010-2017 RhodeCode GmbH
1 // # Copyright (C) 2010-2017 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 /* Comment form for main and inline comments */
45 /* Comment form for main and inline comments */
46 (function(mod) {
46 (function(mod) {
47
47
48 if (typeof exports == "object" && typeof module == "object") {
48 if (typeof exports == "object" && typeof module == "object") {
49 // CommonJS
49 // CommonJS
50 module.exports = mod();
50 module.exports = mod();
51 }
51 }
52 else {
52 else {
53 // Plain browser env
53 // Plain browser env
54 (this || window).CommentForm = mod();
54 (this || window).CommentForm = mod();
55 }
55 }
56
56
57 })(function() {
57 })(function() {
58 "use strict";
58 "use strict";
59
59
60 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) {
60 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) {
61 if (!(this instanceof CommentForm)) {
61 if (!(this instanceof CommentForm)) {
62 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId);
62 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId);
63 }
63 }
64
64
65 // bind the element instance to our Form
65 // bind the element instance to our Form
66 $(formElement).get(0).CommentForm = this;
66 $(formElement).get(0).CommentForm = this;
67
67
68 this.withLineNo = function(selector) {
68 this.withLineNo = function(selector) {
69 var lineNo = this.lineNo;
69 var lineNo = this.lineNo;
70 if (lineNo === undefined) {
70 if (lineNo === undefined) {
71 return selector
71 return selector
72 } else {
72 } else {
73 return selector + '_' + lineNo;
73 return selector + '_' + lineNo;
74 }
74 }
75 };
75 };
76
76
77 this.commitId = commitId;
77 this.commitId = commitId;
78 this.pullRequestId = pullRequestId;
78 this.pullRequestId = pullRequestId;
79 this.lineNo = lineNo;
79 this.lineNo = lineNo;
80 this.initAutocompleteActions = initAutocompleteActions;
80 this.initAutocompleteActions = initAutocompleteActions;
81
81
82 this.previewButton = this.withLineNo('#preview-btn');
82 this.previewButton = this.withLineNo('#preview-btn');
83 this.previewContainer = this.withLineNo('#preview-container');
83 this.previewContainer = this.withLineNo('#preview-container');
84
84
85 this.previewBoxSelector = this.withLineNo('#preview-box');
85 this.previewBoxSelector = this.withLineNo('#preview-box');
86
86
87 this.editButton = this.withLineNo('#edit-btn');
87 this.editButton = this.withLineNo('#edit-btn');
88 this.editContainer = this.withLineNo('#edit-container');
88 this.editContainer = this.withLineNo('#edit-container');
89 this.cancelButton = this.withLineNo('#cancel-btn');
89 this.cancelButton = this.withLineNo('#cancel-btn');
90 this.commentType = this.withLineNo('#comment_type');
90 this.commentType = this.withLineNo('#comment_type');
91
91
92 this.resolvesId = null;
92 this.resolvesId = null;
93 this.resolvesActionId = null;
93 this.resolvesActionId = null;
94
94
95 this.closesPr = '#close_pull_request';
95 this.closesPr = '#close_pull_request';
96
96
97 this.cmBox = this.withLineNo('#text');
97 this.cmBox = this.withLineNo('#text');
98 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
98 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
99
99
100 this.statusChange = this.withLineNo('#change_status');
100 this.statusChange = this.withLineNo('#change_status');
101
101
102 this.submitForm = formElement;
102 this.submitForm = formElement;
103 this.submitButton = $(this.submitForm).find('input[type="submit"]');
103 this.submitButton = $(this.submitForm).find('input[type="submit"]');
104 this.submitButtonText = this.submitButton.val();
104 this.submitButtonText = this.submitButton.val();
105
105
106 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
106 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
107 {'repo_name': templateContext.repo_name,
107 {'repo_name': templateContext.repo_name,
108 'commit_id': templateContext.commit_data.commit_id});
108 'commit_id': templateContext.commit_data.commit_id});
109
109
110 if (resolvesCommentId){
110 if (resolvesCommentId){
111 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
111 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
112 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
112 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
113 $(this.commentType).prop('disabled', true);
113 $(this.commentType).prop('disabled', true);
114 $(this.commentType).addClass('disabled');
114 $(this.commentType).addClass('disabled');
115
115
116 // disable select
116 // disable select
117 setTimeout(function() {
117 setTimeout(function() {
118 $(self.statusChange).select2('readonly', true);
118 $(self.statusChange).select2('readonly', true);
119 }, 10);
119 }, 10);
120
120
121 var resolvedInfo = (
121 var resolvedInfo = (
122 '<li class="resolve-action">' +
122 '<li class="resolve-action">' +
123 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
123 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
124 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
124 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
125 '</li>'
125 '</li>'
126 ).format(resolvesCommentId, _gettext('resolve comment'));
126 ).format(resolvesCommentId, _gettext('resolve comment'));
127 $(resolvedInfo).insertAfter($(this.commentType).parent());
127 $(resolvedInfo).insertAfter($(this.commentType).parent());
128 }
128 }
129
129
130 // based on commitId, or pullRequestId decide where do we submit
130 // based on commitId, or pullRequestId decide where do we submit
131 // out data
131 // out data
132 if (this.commitId){
132 if (this.commitId){
133 this.submitUrl = pyroutes.url('repo_commit_comment_create',
133 this.submitUrl = pyroutes.url('repo_commit_comment_create',
134 {'repo_name': templateContext.repo_name,
134 {'repo_name': templateContext.repo_name,
135 'commit_id': this.commitId});
135 'commit_id': this.commitId});
136 this.selfUrl = pyroutes.url('repo_commit',
136 this.selfUrl = pyroutes.url('repo_commit',
137 {'repo_name': templateContext.repo_name,
137 {'repo_name': templateContext.repo_name,
138 'commit_id': this.commitId});
138 'commit_id': this.commitId});
139
139
140 } else if (this.pullRequestId) {
140 } else if (this.pullRequestId) {
141 this.submitUrl = pyroutes.url('pullrequest_comment_create',
141 this.submitUrl = pyroutes.url('pullrequest_comment_create',
142 {'repo_name': templateContext.repo_name,
142 {'repo_name': templateContext.repo_name,
143 'pull_request_id': this.pullRequestId});
143 'pull_request_id': this.pullRequestId});
144 this.selfUrl = pyroutes.url('pullrequest_show',
144 this.selfUrl = pyroutes.url('pullrequest_show',
145 {'repo_name': templateContext.repo_name,
145 {'repo_name': templateContext.repo_name,
146 'pull_request_id': this.pullRequestId});
146 'pull_request_id': this.pullRequestId});
147
147
148 } else {
148 } else {
149 throw new Error(
149 throw new Error(
150 'CommentForm requires pullRequestId, or commitId to be specified.')
150 'CommentForm requires pullRequestId, or commitId to be specified.')
151 }
151 }
152
152
153 // FUNCTIONS and helpers
153 // FUNCTIONS and helpers
154 var self = this;
154 var self = this;
155
155
156 this.isInline = function(){
156 this.isInline = function(){
157 return this.lineNo && this.lineNo != 'general';
157 return this.lineNo && this.lineNo != 'general';
158 };
158 };
159
159
160 this.getCmInstance = function(){
160 this.getCmInstance = function(){
161 return this.cm
161 return this.cm
162 };
162 };
163
163
164 this.setPlaceholder = function(placeholder) {
164 this.setPlaceholder = function(placeholder) {
165 var cm = this.getCmInstance();
165 var cm = this.getCmInstance();
166 if (cm){
166 if (cm){
167 cm.setOption('placeholder', placeholder);
167 cm.setOption('placeholder', placeholder);
168 }
168 }
169 };
169 };
170
170
171 this.getCommentStatus = function() {
171 this.getCommentStatus = function() {
172 return $(this.submitForm).find(this.statusChange).val();
172 return $(this.submitForm).find(this.statusChange).val();
173 };
173 };
174 this.getCommentType = function() {
174 this.getCommentType = function() {
175 return $(this.submitForm).find(this.commentType).val();
175 return $(this.submitForm).find(this.commentType).val();
176 };
176 };
177
177
178 this.getResolvesId = function() {
178 this.getResolvesId = function() {
179 return $(this.submitForm).find(this.resolvesId).val() || null;
179 return $(this.submitForm).find(this.resolvesId).val() || null;
180 };
180 };
181
181
182 this.getClosePr = function() {
182 this.getClosePr = function() {
183 return $(this.submitForm).find(this.closesPr).val() || null;
183 return $(this.submitForm).find(this.closesPr).val() || null;
184 };
184 };
185
185
186 this.markCommentResolved = function(resolvedCommentId){
186 this.markCommentResolved = function(resolvedCommentId){
187 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
187 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
188 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
188 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
189 };
189 };
190
190
191 this.isAllowedToSubmit = function() {
191 this.isAllowedToSubmit = function() {
192 return !$(this.submitButton).prop('disabled');
192 return !$(this.submitButton).prop('disabled');
193 };
193 };
194
194
195 this.initStatusChangeSelector = function(){
195 this.initStatusChangeSelector = function(){
196 var formatChangeStatus = function(state, escapeMarkup) {
196 var formatChangeStatus = function(state, escapeMarkup) {
197 var originalOption = state.element;
197 var originalOption = state.element;
198 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
198 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
199 '<span>' + escapeMarkup(state.text) + '</span>';
199 '<span>' + escapeMarkup(state.text) + '</span>';
200 };
200 };
201 var formatResult = function(result, container, query, escapeMarkup) {
201 var formatResult = function(result, container, query, escapeMarkup) {
202 return formatChangeStatus(result, escapeMarkup);
202 return formatChangeStatus(result, escapeMarkup);
203 };
203 };
204
204
205 var formatSelection = function(data, container, escapeMarkup) {
205 var formatSelection = function(data, container, escapeMarkup) {
206 return formatChangeStatus(data, escapeMarkup);
206 return formatChangeStatus(data, escapeMarkup);
207 };
207 };
208
208
209 $(this.submitForm).find(this.statusChange).select2({
209 $(this.submitForm).find(this.statusChange).select2({
210 placeholder: _gettext('Status Review'),
210 placeholder: _gettext('Status Review'),
211 formatResult: formatResult,
211 formatResult: formatResult,
212 formatSelection: formatSelection,
212 formatSelection: formatSelection,
213 containerCssClass: "drop-menu status_box_menu",
213 containerCssClass: "drop-menu status_box_menu",
214 dropdownCssClass: "drop-menu-dropdown",
214 dropdownCssClass: "drop-menu-dropdown",
215 dropdownAutoWidth: true,
215 dropdownAutoWidth: true,
216 minimumResultsForSearch: -1
216 minimumResultsForSearch: -1
217 });
217 });
218 $(this.submitForm).find(this.statusChange).on('change', function() {
218 $(this.submitForm).find(this.statusChange).on('change', function() {
219 var status = self.getCommentStatus();
219 var status = self.getCommentStatus();
220
220
221 if (status && !self.isInline()) {
221 if (status && !self.isInline()) {
222 $(self.submitButton).prop('disabled', false);
222 $(self.submitButton).prop('disabled', false);
223 }
223 }
224
224
225 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
225 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
226 self.setPlaceholder(placeholderText)
226 self.setPlaceholder(placeholderText)
227 })
227 })
228 };
228 };
229
229
230 // reset the comment form into it's original state
230 // reset the comment form into it's original state
231 this.resetCommentFormState = function(content) {
231 this.resetCommentFormState = function(content) {
232 content = content || '';
232 content = content || '';
233
233
234 $(this.editContainer).show();
234 $(this.editContainer).show();
235 $(this.editButton).parent().addClass('active');
235 $(this.editButton).parent().addClass('active');
236
236
237 $(this.previewContainer).hide();
237 $(this.previewContainer).hide();
238 $(this.previewButton).parent().removeClass('active');
238 $(this.previewButton).parent().removeClass('active');
239
239
240 this.setActionButtonsDisabled(true);
240 this.setActionButtonsDisabled(true);
241 self.cm.setValue(content);
241 self.cm.setValue(content);
242 self.cm.setOption("readOnly", false);
242 self.cm.setOption("readOnly", false);
243
243
244 if (this.resolvesId) {
244 if (this.resolvesId) {
245 // destroy the resolve action
245 // destroy the resolve action
246 $(this.resolvesId).parent().remove();
246 $(this.resolvesId).parent().remove();
247 }
247 }
248 // reset closingPR flag
248 // reset closingPR flag
249 $('.close-pr-input').remove();
249 $('.close-pr-input').remove();
250
250
251 $(this.statusChange).select2('readonly', false);
251 $(this.statusChange).select2('readonly', false);
252 };
252 };
253
253
254 this.globalSubmitSuccessCallback = function(){
254 this.globalSubmitSuccessCallback = function(){
255 // default behaviour is to call GLOBAL hook, if it's registered.
255 // default behaviour is to call GLOBAL hook, if it's registered.
256 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
256 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
257 commentFormGlobalSubmitSuccessCallback()
257 commentFormGlobalSubmitSuccessCallback()
258 }
258 }
259 };
259 };
260
260
261 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
261 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
262 failHandler = failHandler || function() {};
262 failHandler = failHandler || function() {};
263 var postData = toQueryString(postData);
263 var postData = toQueryString(postData);
264 var request = $.ajax({
264 var request = $.ajax({
265 url: url,
265 url: url,
266 type: 'POST',
266 type: 'POST',
267 data: postData,
267 data: postData,
268 headers: {'X-PARTIAL-XHR': true}
268 headers: {'X-PARTIAL-XHR': true}
269 })
269 })
270 .done(function(data) {
270 .done(function(data) {
271 successHandler(data);
271 successHandler(data);
272 })
272 })
273 .fail(function(data, textStatus, errorThrown){
273 .fail(function(data, textStatus, errorThrown){
274 alert(
274 alert(
275 "Error while submitting comment.\n" +
275 "Error while submitting comment.\n" +
276 "Error code {0} ({1}).".format(data.status, data.statusText));
276 "Error code {0} ({1}).".format(data.status, data.statusText));
277 failHandler()
277 failHandler()
278 });
278 });
279 return request;
279 return request;
280 };
280 };
281
281
282 // overwrite a submitHandler, we need to do it for inline comments
282 // overwrite a submitHandler, we need to do it for inline comments
283 this.setHandleFormSubmit = function(callback) {
283 this.setHandleFormSubmit = function(callback) {
284 this.handleFormSubmit = callback;
284 this.handleFormSubmit = callback;
285 };
285 };
286
286
287 // overwrite a submitSuccessHandler
287 // overwrite a submitSuccessHandler
288 this.setGlobalSubmitSuccessCallback = function(callback) {
288 this.setGlobalSubmitSuccessCallback = function(callback) {
289 this.globalSubmitSuccessCallback = callback;
289 this.globalSubmitSuccessCallback = callback;
290 };
290 };
291
291
292 // default handler for for submit for main comments
292 // default handler for for submit for main comments
293 this.handleFormSubmit = function() {
293 this.handleFormSubmit = function() {
294 var text = self.cm.getValue();
294 var text = self.cm.getValue();
295 var status = self.getCommentStatus();
295 var status = self.getCommentStatus();
296 var commentType = self.getCommentType();
296 var commentType = self.getCommentType();
297 var resolvesCommentId = self.getResolvesId();
297 var resolvesCommentId = self.getResolvesId();
298 var closePullRequest = self.getClosePr();
298 var closePullRequest = self.getClosePr();
299
299
300 if (text === "" && !status) {
300 if (text === "" && !status) {
301 return;
301 return;
302 }
302 }
303
303
304 var excludeCancelBtn = false;
304 var excludeCancelBtn = false;
305 var submitEvent = true;
305 var submitEvent = true;
306 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
306 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
307 self.cm.setOption("readOnly", true);
307 self.cm.setOption("readOnly", true);
308
308
309 var postData = {
309 var postData = {
310 'text': text,
310 'text': text,
311 'changeset_status': status,
311 'changeset_status': status,
312 'comment_type': commentType,
312 'comment_type': commentType,
313 'csrf_token': CSRF_TOKEN
313 'csrf_token': CSRF_TOKEN
314 };
314 };
315
315
316 if (resolvesCommentId) {
316 if (resolvesCommentId) {
317 postData['resolves_comment_id'] = resolvesCommentId;
317 postData['resolves_comment_id'] = resolvesCommentId;
318 }
318 }
319
319
320 if (closePullRequest) {
320 if (closePullRequest) {
321 postData['close_pull_request'] = true;
321 postData['close_pull_request'] = true;
322 }
322 }
323
323
324 var submitSuccessCallback = function(o) {
324 var submitSuccessCallback = function(o) {
325 // reload page if we change status for single commit.
325 // reload page if we change status for single commit.
326 if (status && self.commitId) {
326 if (status && self.commitId) {
327 location.reload(true);
327 location.reload(true);
328 } else {
328 } else {
329 $('#injected_page_comments').append(o.rendered_text);
329 $('#injected_page_comments').append(o.rendered_text);
330 self.resetCommentFormState();
330 self.resetCommentFormState();
331 timeagoActivate();
331 timeagoActivate();
332
332
333 // mark visually which comment was resolved
333 // mark visually which comment was resolved
334 if (resolvesCommentId) {
334 if (resolvesCommentId) {
335 self.markCommentResolved(resolvesCommentId);
335 self.markCommentResolved(resolvesCommentId);
336 }
336 }
337 }
337 }
338
338
339 // run global callback on submit
339 // run global callback on submit
340 self.globalSubmitSuccessCallback();
340 self.globalSubmitSuccessCallback();
341
341
342 };
342 };
343 var submitFailCallback = function(){
343 var submitFailCallback = function(){
344 self.resetCommentFormState(text);
344 self.resetCommentFormState(text);
345 };
345 };
346 self.submitAjaxPOST(
346 self.submitAjaxPOST(
347 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
347 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
348 };
348 };
349
349
350 this.previewSuccessCallback = function(o) {
350 this.previewSuccessCallback = function(o) {
351 $(self.previewBoxSelector).html(o);
351 $(self.previewBoxSelector).html(o);
352 $(self.previewBoxSelector).removeClass('unloaded');
352 $(self.previewBoxSelector).removeClass('unloaded');
353
353
354 // swap buttons, making preview active
354 // swap buttons, making preview active
355 $(self.previewButton).parent().addClass('active');
355 $(self.previewButton).parent().addClass('active');
356 $(self.editButton).parent().removeClass('active');
356 $(self.editButton).parent().removeClass('active');
357
357
358 // unlock buttons
358 // unlock buttons
359 self.setActionButtonsDisabled(false);
359 self.setActionButtonsDisabled(false);
360 };
360 };
361
361
362 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
362 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
363 excludeCancelBtn = excludeCancelBtn || false;
363 excludeCancelBtn = excludeCancelBtn || false;
364 submitEvent = submitEvent || false;
364 submitEvent = submitEvent || false;
365
365
366 $(this.editButton).prop('disabled', state);
366 $(this.editButton).prop('disabled', state);
367 $(this.previewButton).prop('disabled', state);
367 $(this.previewButton).prop('disabled', state);
368
368
369 if (!excludeCancelBtn) {
369 if (!excludeCancelBtn) {
370 $(this.cancelButton).prop('disabled', state);
370 $(this.cancelButton).prop('disabled', state);
371 }
371 }
372
372
373 var submitState = state;
373 var submitState = state;
374 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
374 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
375 // if the value of commit review status is set, we allow
375 // if the value of commit review status is set, we allow
376 // submit button, but only on Main form, isInline means inline
376 // submit button, but only on Main form, isInline means inline
377 submitState = false
377 submitState = false
378 }
378 }
379
379
380 $(this.submitButton).prop('disabled', submitState);
380 $(this.submitButton).prop('disabled', submitState);
381 if (submitEvent) {
381 if (submitEvent) {
382 $(this.submitButton).val(_gettext('Submitting...'));
382 $(this.submitButton).val(_gettext('Submitting...'));
383 } else {
383 } else {
384 $(this.submitButton).val(this.submitButtonText);
384 $(this.submitButton).val(this.submitButtonText);
385 }
385 }
386
386
387 };
387 };
388
388
389 // lock preview/edit/submit buttons on load, but exclude cancel button
389 // lock preview/edit/submit buttons on load, but exclude cancel button
390 var excludeCancelBtn = true;
390 var excludeCancelBtn = true;
391 this.setActionButtonsDisabled(true, excludeCancelBtn);
391 this.setActionButtonsDisabled(true, excludeCancelBtn);
392
392
393 // anonymous users don't have access to initialized CM instance
393 // anonymous users don't have access to initialized CM instance
394 if (this.cm !== undefined){
394 if (this.cm !== undefined){
395 this.cm.on('change', function(cMirror) {
395 this.cm.on('change', function(cMirror) {
396 if (cMirror.getValue() === "") {
396 if (cMirror.getValue() === "") {
397 self.setActionButtonsDisabled(true, excludeCancelBtn)
397 self.setActionButtonsDisabled(true, excludeCancelBtn)
398 } else {
398 } else {
399 self.setActionButtonsDisabled(false, excludeCancelBtn)
399 self.setActionButtonsDisabled(false, excludeCancelBtn)
400 }
400 }
401 });
401 });
402 }
402 }
403
403
404 $(this.editButton).on('click', function(e) {
404 $(this.editButton).on('click', function(e) {
405 e.preventDefault();
405 e.preventDefault();
406
406
407 $(self.previewButton).parent().removeClass('active');
407 $(self.previewButton).parent().removeClass('active');
408 $(self.previewContainer).hide();
408 $(self.previewContainer).hide();
409
409
410 $(self.editButton).parent().addClass('active');
410 $(self.editButton).parent().addClass('active');
411 $(self.editContainer).show();
411 $(self.editContainer).show();
412
412
413 });
413 });
414
414
415 $(this.previewButton).on('click', function(e) {
415 $(this.previewButton).on('click', function(e) {
416 e.preventDefault();
416 e.preventDefault();
417 var text = self.cm.getValue();
417 var text = self.cm.getValue();
418
418
419 if (text === "") {
419 if (text === "") {
420 return;
420 return;
421 }
421 }
422
422
423 var postData = {
423 var postData = {
424 'text': text,
424 'text': text,
425 'renderer': templateContext.visual.default_renderer,
425 'renderer': templateContext.visual.default_renderer,
426 'csrf_token': CSRF_TOKEN
426 'csrf_token': CSRF_TOKEN
427 };
427 };
428
428
429 // lock ALL buttons on preview
429 // lock ALL buttons on preview
430 self.setActionButtonsDisabled(true);
430 self.setActionButtonsDisabled(true);
431
431
432 $(self.previewBoxSelector).addClass('unloaded');
432 $(self.previewBoxSelector).addClass('unloaded');
433 $(self.previewBoxSelector).html(_gettext('Loading ...'));
433 $(self.previewBoxSelector).html(_gettext('Loading ...'));
434
434
435 $(self.editContainer).hide();
435 $(self.editContainer).hide();
436 $(self.previewContainer).show();
436 $(self.previewContainer).show();
437
437
438 // by default we reset state of comment preserving the text
438 // by default we reset state of comment preserving the text
439 var previewFailCallback = function(){
439 var previewFailCallback = function(){
440 self.resetCommentFormState(text)
440 self.resetCommentFormState(text)
441 };
441 };
442 self.submitAjaxPOST(
442 self.submitAjaxPOST(
443 self.previewUrl, postData, self.previewSuccessCallback,
443 self.previewUrl, postData, self.previewSuccessCallback,
444 previewFailCallback);
444 previewFailCallback);
445
445
446 $(self.previewButton).parent().addClass('active');
446 $(self.previewButton).parent().addClass('active');
447 $(self.editButton).parent().removeClass('active');
447 $(self.editButton).parent().removeClass('active');
448 });
448 });
449
449
450 $(this.submitForm).submit(function(e) {
450 $(this.submitForm).submit(function(e) {
451 e.preventDefault();
451 e.preventDefault();
452 var allowedToSubmit = self.isAllowedToSubmit();
452 var allowedToSubmit = self.isAllowedToSubmit();
453 if (!allowedToSubmit){
453 if (!allowedToSubmit){
454 return false;
454 return false;
455 }
455 }
456 self.handleFormSubmit();
456 self.handleFormSubmit();
457 });
457 });
458
458
459 }
459 }
460
460
461 return CommentForm;
461 return CommentForm;
462 });
462 });
463
463
464 /* comments controller */
464 /* comments controller */
465 var CommentsController = function() {
465 var CommentsController = function() {
466 var mainComment = '#text';
466 var mainComment = '#text';
467 var self = this;
467 var self = this;
468
468
469 this.cancelComment = function(node) {
469 this.cancelComment = function(node) {
470 var $node = $(node);
470 var $node = $(node);
471 var $td = $node.closest('td');
471 var $td = $node.closest('td');
472 $node.closest('.comment-inline-form').remove();
472 $node.closest('.comment-inline-form').remove();
473 return false;
473 return false;
474 };
474 };
475
475
476 this.getLineNumber = function(node) {
476 this.getLineNumber = function(node) {
477 var $node = $(node);
477 var $node = $(node);
478 return $node.closest('td').attr('data-line-number');
478 return $node.closest('td').attr('data-line-number');
479 };
479 };
480
480
481 this.scrollToComment = function(node, offset, outdated) {
481 this.scrollToComment = function(node, offset, outdated) {
482 if (offset === undefined) {
482 if (offset === undefined) {
483 offset = 0;
483 offset = 0;
484 }
484 }
485 var outdated = outdated || false;
485 var outdated = outdated || false;
486 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
486 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
487
487
488 if (!node) {
488 if (!node) {
489 node = $('.comment-selected');
489 node = $('.comment-selected');
490 if (!node.length) {
490 if (!node.length) {
491 node = $('comment-current')
491 node = $('comment-current')
492 }
492 }
493 }
493 }
494 $wrapper = $(node).closest('div.comment');
494 $wrapper = $(node).closest('div.comment');
495 $comment = $(node).closest(klass);
495 $comment = $(node).closest(klass);
496 $comments = $(klass);
496 $comments = $(klass);
497
497
498 // show hidden comment when referenced.
498 // show hidden comment when referenced.
499 if (!$wrapper.is(':visible')){
499 if (!$wrapper.is(':visible')){
500 $wrapper.show();
500 $wrapper.show();
501 }
501 }
502
502
503 $('.comment-selected').removeClass('comment-selected');
503 $('.comment-selected').removeClass('comment-selected');
504
504
505 var nextIdx = $(klass).index($comment) + offset;
505 var nextIdx = $(klass).index($comment) + offset;
506 if (nextIdx >= $comments.length) {
506 if (nextIdx >= $comments.length) {
507 nextIdx = 0;
507 nextIdx = 0;
508 }
508 }
509 var $next = $(klass).eq(nextIdx);
509 var $next = $(klass).eq(nextIdx);
510
510
511 var $cb = $next.closest('.cb');
511 var $cb = $next.closest('.cb');
512 $cb.removeClass('cb-collapsed');
512 $cb.removeClass('cb-collapsed');
513
513
514 var $filediffCollapseState = $cb.closest('.filediff').prev();
514 var $filediffCollapseState = $cb.closest('.filediff').prev();
515 $filediffCollapseState.prop('checked', false);
515 $filediffCollapseState.prop('checked', false);
516 $next.addClass('comment-selected');
516 $next.addClass('comment-selected');
517 scrollToElement($next);
517 scrollToElement($next);
518 return false;
518 return false;
519 };
519 };
520
520
521 this.nextComment = function(node) {
521 this.nextComment = function(node) {
522 return self.scrollToComment(node, 1);
522 return self.scrollToComment(node, 1);
523 };
523 };
524
524
525 this.prevComment = function(node) {
525 this.prevComment = function(node) {
526 return self.scrollToComment(node, -1);
526 return self.scrollToComment(node, -1);
527 };
527 };
528
528
529 this.nextOutdatedComment = function(node) {
529 this.nextOutdatedComment = function(node) {
530 return self.scrollToComment(node, 1, true);
530 return self.scrollToComment(node, 1, true);
531 };
531 };
532
532
533 this.prevOutdatedComment = function(node) {
533 this.prevOutdatedComment = function(node) {
534 return self.scrollToComment(node, -1, true);
534 return self.scrollToComment(node, -1, true);
535 };
535 };
536
536
537 this.deleteComment = function(node) {
537 this.deleteComment = function(node) {
538 if (!confirm(_gettext('Delete this comment?'))) {
538 if (!confirm(_gettext('Delete this comment?'))) {
539 return false;
539 return false;
540 }
540 }
541 var $node = $(node);
541 var $node = $(node);
542 var $td = $node.closest('td');
542 var $td = $node.closest('td');
543 var $comment = $node.closest('.comment');
543 var $comment = $node.closest('.comment');
544 var comment_id = $comment.attr('data-comment-id');
544 var comment_id = $comment.attr('data-comment-id');
545 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
545 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
546 var postData = {
546 var postData = {
547 'csrf_token': CSRF_TOKEN
547 'csrf_token': CSRF_TOKEN
548 };
548 };
549
549
550 $comment.addClass('comment-deleting');
550 $comment.addClass('comment-deleting');
551 $comment.hide('fast');
551 $comment.hide('fast');
552
552
553 var success = function(response) {
553 var success = function(response) {
554 $comment.remove();
554 $comment.remove();
555 return false;
555 return false;
556 };
556 };
557 var failure = function(data, textStatus, xhr) {
557 var failure = function(data, textStatus, xhr) {
558 alert("error processing request: " + textStatus);
558 alert("error processing request: " + textStatus);
559 $comment.show('fast');
559 $comment.show('fast');
560 $comment.removeClass('comment-deleting');
560 $comment.removeClass('comment-deleting');
561 return false;
561 return false;
562 };
562 };
563 ajaxPOST(url, postData, success, failure);
563 ajaxPOST(url, postData, success, failure);
564 };
564 };
565
565
566 this.toggleWideMode = function (node) {
566 this.toggleWideMode = function (node) {
567 if ($('#content').hasClass('wrapper')) {
567 if ($('#content').hasClass('wrapper')) {
568 $('#content').removeClass("wrapper");
568 $('#content').removeClass("wrapper");
569 $('#content').addClass("wide-mode-wrapper");
569 $('#content').addClass("wide-mode-wrapper");
570 $(node).addClass('btn-success');
570 $(node).addClass('btn-success');
571 } else {
571 } else {
572 $('#content').removeClass("wide-mode-wrapper");
572 $('#content').removeClass("wide-mode-wrapper");
573 $('#content').addClass("wrapper");
573 $('#content').addClass("wrapper");
574 $(node).removeClass('btn-success');
574 $(node).removeClass('btn-success');
575 }
575 }
576 return false;
576 return false;
577 };
577 };
578
578
579 this.toggleComments = function(node, show) {
579 this.toggleComments = function(node, show) {
580 var $filediff = $(node).closest('.filediff');
580 var $filediff = $(node).closest('.filediff');
581 if (show === true) {
581 if (show === true) {
582 $filediff.removeClass('hide-comments');
582 $filediff.removeClass('hide-comments');
583 } else if (show === false) {
583 } else if (show === false) {
584 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
584 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
585 $filediff.addClass('hide-comments');
585 $filediff.addClass('hide-comments');
586 } else {
586 } else {
587 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
587 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
588 $filediff.toggleClass('hide-comments');
588 $filediff.toggleClass('hide-comments');
589 }
589 }
590 return false;
590 return false;
591 };
591 };
592
592
593 this.toggleLineComments = function(node) {
593 this.toggleLineComments = function(node) {
594 self.toggleComments(node, true);
594 self.toggleComments(node, true);
595 var $node = $(node);
595 var $node = $(node);
596 $node.closest('tr').toggleClass('hide-line-comments');
596 $node.closest('tr').toggleClass('hide-line-comments');
597 };
597 };
598
598
599 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId){
599 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId){
600 var pullRequestId = templateContext.pull_request_data.pull_request_id;
600 var pullRequestId = templateContext.pull_request_data.pull_request_id;
601 var commitId = templateContext.commit_data.commit_id;
601 var commitId = templateContext.commit_data.commit_id;
602
602
603 var commentForm = new CommentForm(
603 var commentForm = new CommentForm(
604 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId);
604 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId);
605 var cm = commentForm.getCmInstance();
605 var cm = commentForm.getCmInstance();
606
606
607 if (resolvesCommentId){
607 if (resolvesCommentId){
608 var placeholderText = _gettext('Leave a comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
608 var placeholderText = _gettext('Leave a comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
609 }
609 }
610
610
611 setTimeout(function() {
611 setTimeout(function() {
612 // callbacks
612 // callbacks
613 if (cm !== undefined) {
613 if (cm !== undefined) {
614 commentForm.setPlaceholder(placeholderText);
614 commentForm.setPlaceholder(placeholderText);
615 if (commentForm.isInline()) {
615 if (commentForm.isInline()) {
616 cm.focus();
616 cm.focus();
617 cm.refresh();
617 cm.refresh();
618 }
618 }
619 }
619 }
620 }, 10);
620 }, 10);
621
621
622 // trigger scrolldown to the resolve comment, since it might be away
622 // trigger scrolldown to the resolve comment, since it might be away
623 // from the clicked
623 // from the clicked
624 if (resolvesCommentId){
624 if (resolvesCommentId){
625 var actionNode = $(commentForm.resolvesActionId).offset();
625 var actionNode = $(commentForm.resolvesActionId).offset();
626
626
627 setTimeout(function() {
627 setTimeout(function() {
628 if (actionNode) {
628 if (actionNode) {
629 $('body, html').animate({scrollTop: actionNode.top}, 10);
629 $('body, html').animate({scrollTop: actionNode.top}, 10);
630 }
630 }
631 }, 100);
631 }, 100);
632 }
632 }
633
633
634 return commentForm;
634 return commentForm;
635 };
635 };
636
636
637 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
637 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
638
638
639 var tmpl = $('#cb-comment-general-form-template').html();
639 var tmpl = $('#cb-comment-general-form-template').html();
640 tmpl = tmpl.format(null, 'general');
640 tmpl = tmpl.format(null, 'general');
641 var $form = $(tmpl);
641 var $form = $(tmpl);
642
642
643 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
643 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
644 var curForm = $formPlaceholder.find('form');
644 var curForm = $formPlaceholder.find('form');
645 if (curForm){
645 if (curForm){
646 curForm.remove();
646 curForm.remove();
647 }
647 }
648 $formPlaceholder.append($form);
648 $formPlaceholder.append($form);
649
649
650 var _form = $($form[0]);
650 var _form = $($form[0]);
651 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
651 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
652 var commentForm = this.createCommentForm(
652 var commentForm = this.createCommentForm(
653 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId);
653 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId);
654 commentForm.initStatusChangeSelector();
654 commentForm.initStatusChangeSelector();
655
655
656 return commentForm;
656 return commentForm;
657 };
657 };
658
658
659 this.createComment = function(node, resolutionComment) {
659 this.createComment = function(node, resolutionComment) {
660 var resolvesCommentId = resolutionComment || null;
660 var resolvesCommentId = resolutionComment || null;
661 var $node = $(node);
661 var $node = $(node);
662 var $td = $node.closest('td');
662 var $td = $node.closest('td');
663 var $form = $td.find('.comment-inline-form');
663 var $form = $td.find('.comment-inline-form');
664
664
665 if (!$form.length) {
665 if (!$form.length) {
666
666
667 var $filediff = $node.closest('.filediff');
667 var $filediff = $node.closest('.filediff');
668 $filediff.removeClass('hide-comments');
668 $filediff.removeClass('hide-comments');
669 var f_path = $filediff.attr('data-f-path');
669 var f_path = $filediff.attr('data-f-path');
670 var lineno = self.getLineNumber(node);
670 var lineno = self.getLineNumber(node);
671 // create a new HTML from template
671 // create a new HTML from template
672 var tmpl = $('#cb-comment-inline-form-template').html();
672 var tmpl = $('#cb-comment-inline-form-template').html();
673 tmpl = tmpl.format(f_path, lineno);
673 tmpl = tmpl.format(escapeHtml(f_path), lineno);
674 $form = $(tmpl);
674 $form = $(tmpl);
675
675
676 var $comments = $td.find('.inline-comments');
676 var $comments = $td.find('.inline-comments');
677 if (!$comments.length) {
677 if (!$comments.length) {
678 $comments = $(
678 $comments = $(
679 $('#cb-comments-inline-container-template').html());
679 $('#cb-comments-inline-container-template').html());
680 $td.append($comments);
680 $td.append($comments);
681 }
681 }
682
682
683 $td.find('.cb-comment-add-button').before($form);
683 $td.find('.cb-comment-add-button').before($form);
684
684
685 var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno);
685 var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno);
686 var _form = $($form[0]).find('form');
686 var _form = $($form[0]).find('form');
687 var autocompleteActions = ['as_note', 'as_todo'];
687 var autocompleteActions = ['as_note', 'as_todo'];
688 var commentForm = this.createCommentForm(
688 var commentForm = this.createCommentForm(
689 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId);
689 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId);
690
690
691 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
691 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
692 form: _form,
692 form: _form,
693 parent: $td[0],
693 parent: $td[0],
694 lineno: lineno,
694 lineno: lineno,
695 f_path: f_path}
695 f_path: f_path}
696 );
696 );
697
697
698 // set a CUSTOM submit handler for inline comments.
698 // set a CUSTOM submit handler for inline comments.
699 commentForm.setHandleFormSubmit(function(o) {
699 commentForm.setHandleFormSubmit(function(o) {
700 var text = commentForm.cm.getValue();
700 var text = commentForm.cm.getValue();
701 var commentType = commentForm.getCommentType();
701 var commentType = commentForm.getCommentType();
702 var resolvesCommentId = commentForm.getResolvesId();
702 var resolvesCommentId = commentForm.getResolvesId();
703
703
704 if (text === "") {
704 if (text === "") {
705 return;
705 return;
706 }
706 }
707
707
708 if (lineno === undefined) {
708 if (lineno === undefined) {
709 alert('missing line !');
709 alert('missing line !');
710 return;
710 return;
711 }
711 }
712 if (f_path === undefined) {
712 if (f_path === undefined) {
713 alert('missing file path !');
713 alert('missing file path !');
714 return;
714 return;
715 }
715 }
716
716
717 var excludeCancelBtn = false;
717 var excludeCancelBtn = false;
718 var submitEvent = true;
718 var submitEvent = true;
719 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
719 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
720 commentForm.cm.setOption("readOnly", true);
720 commentForm.cm.setOption("readOnly", true);
721 var postData = {
721 var postData = {
722 'text': text,
722 'text': text,
723 'f_path': f_path,
723 'f_path': f_path,
724 'line': lineno,
724 'line': lineno,
725 'comment_type': commentType,
725 'comment_type': commentType,
726 'csrf_token': CSRF_TOKEN
726 'csrf_token': CSRF_TOKEN
727 };
727 };
728 if (resolvesCommentId){
728 if (resolvesCommentId){
729 postData['resolves_comment_id'] = resolvesCommentId;
729 postData['resolves_comment_id'] = resolvesCommentId;
730 }
730 }
731
731
732 var submitSuccessCallback = function(json_data) {
732 var submitSuccessCallback = function(json_data) {
733 $form.remove();
733 $form.remove();
734 try {
734 try {
735 var html = json_data.rendered_text;
735 var html = json_data.rendered_text;
736 var lineno = json_data.line_no;
736 var lineno = json_data.line_no;
737 var target_id = json_data.target_id;
737 var target_id = json_data.target_id;
738
738
739 $comments.find('.cb-comment-add-button').before(html);
739 $comments.find('.cb-comment-add-button').before(html);
740
740
741 //mark visually which comment was resolved
741 //mark visually which comment was resolved
742 if (resolvesCommentId) {
742 if (resolvesCommentId) {
743 commentForm.markCommentResolved(resolvesCommentId);
743 commentForm.markCommentResolved(resolvesCommentId);
744 }
744 }
745
745
746 // run global callback on submit
746 // run global callback on submit
747 commentForm.globalSubmitSuccessCallback();
747 commentForm.globalSubmitSuccessCallback();
748
748
749 } catch (e) {
749 } catch (e) {
750 console.error(e);
750 console.error(e);
751 }
751 }
752
752
753 // re trigger the linkification of next/prev navigation
753 // re trigger the linkification of next/prev navigation
754 linkifyComments($('.inline-comment-injected'));
754 linkifyComments($('.inline-comment-injected'));
755 timeagoActivate();
755 timeagoActivate();
756 commentForm.setActionButtonsDisabled(false);
756 commentForm.setActionButtonsDisabled(false);
757
757
758 };
758 };
759 var submitFailCallback = function(){
759 var submitFailCallback = function(){
760 commentForm.resetCommentFormState(text)
760 commentForm.resetCommentFormState(text)
761 };
761 };
762 commentForm.submitAjaxPOST(
762 commentForm.submitAjaxPOST(
763 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
763 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
764 });
764 });
765 }
765 }
766
766
767 $form.addClass('comment-inline-form-open');
767 $form.addClass('comment-inline-form-open');
768 };
768 };
769
769
770 this.createResolutionComment = function(commentId){
770 this.createResolutionComment = function(commentId){
771 // hide the trigger text
771 // hide the trigger text
772 $('#resolve-comment-{0}'.format(commentId)).hide();
772 $('#resolve-comment-{0}'.format(commentId)).hide();
773
773
774 var comment = $('#comment-'+commentId);
774 var comment = $('#comment-'+commentId);
775 var commentData = comment.data();
775 var commentData = comment.data();
776 if (commentData.commentInline) {
776 if (commentData.commentInline) {
777 this.createComment(comment, commentId)
777 this.createComment(comment, commentId)
778 } else {
778 } else {
779 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
779 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
780 }
780 }
781
781
782 return false;
782 return false;
783 };
783 };
784
784
785 this.submitResolution = function(commentId){
785 this.submitResolution = function(commentId){
786 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
786 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
787 var commentForm = form.get(0).CommentForm;
787 var commentForm = form.get(0).CommentForm;
788
788
789 var cm = commentForm.getCmInstance();
789 var cm = commentForm.getCmInstance();
790 var renderer = templateContext.visual.default_renderer;
790 var renderer = templateContext.visual.default_renderer;
791 if (renderer == 'rst'){
791 if (renderer == 'rst'){
792 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
792 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
793 } else if (renderer == 'markdown') {
793 } else if (renderer == 'markdown') {
794 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
794 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
795 } else {
795 } else {
796 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
796 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
797 }
797 }
798
798
799 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
799 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
800 form.submit();
800 form.submit();
801 return false;
801 return false;
802 };
802 };
803
803
804 this.renderInlineComments = function(file_comments) {
804 this.renderInlineComments = function(file_comments) {
805 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
805 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
806
806
807 for (var i = 0; i < file_comments.length; i++) {
807 for (var i = 0; i < file_comments.length; i++) {
808 var box = file_comments[i];
808 var box = file_comments[i];
809
809
810 var target_id = $(box).attr('target_id');
810 var target_id = $(box).attr('target_id');
811
811
812 // actually comments with line numbers
812 // actually comments with line numbers
813 var comments = box.children;
813 var comments = box.children;
814
814
815 for (var j = 0; j < comments.length; j++) {
815 for (var j = 0; j < comments.length; j++) {
816 var data = {
816 var data = {
817 'rendered_text': comments[j].outerHTML,
817 'rendered_text': comments[j].outerHTML,
818 'line_no': $(comments[j]).attr('line'),
818 'line_no': $(comments[j]).attr('line'),
819 'target_id': target_id
819 'target_id': target_id
820 };
820 };
821 }
821 }
822 }
822 }
823
823
824 // since order of injection is random, we're now re-iterating
824 // since order of injection is random, we're now re-iterating
825 // from correct order and filling in links
825 // from correct order and filling in links
826 linkifyComments($('.inline-comment-injected'));
826 linkifyComments($('.inline-comment-injected'));
827 firefoxAnchorFix();
827 firefoxAnchorFix();
828 };
828 };
829
829
830 };
830 };
General Comments 0
You need to be logged in to leave comments. Login now