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