##// END OF EJS Templates
pull-requests: allow markup rendered description.
marcink -
r2816:b1852ba4 default
parent child Browse files
Show More
@@ -42,6 +42,10 b' def includeme(config):'
42 name='goto_switcher_data',
42 name='goto_switcher_data',
43 pattern='/_goto_data')
43 pattern='/_goto_data')
44
44
45 config.add_route(
46 name='markup_preview',
47 pattern='/_markup_preview')
48
45 # register our static links via redirection mechanism
49 # register our static links via redirection mechanism
46 routing_links.connect_redirection_links(config)
50 routing_links.connect_redirection_links(config)
47
51
@@ -27,7 +27,8 b' from pyramid.view import view_config'
27 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.lib import helpers as h
28 from rhodecode.lib import helpers as h
29 from rhodecode.lib.auth import (
29 from rhodecode.lib.auth import (
30 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator)
30 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator,
31 CSRFRequired)
31 from rhodecode.lib.index import searcher_from_config
32 from rhodecode.lib.index import searcher_from_config
32 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
33 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
33 from rhodecode.lib.ext_json import json
34 from rhodecode.lib.ext_json import json
@@ -425,3 +426,21 b' class HomeView(BaseAppView):'
425 c.repo_groups_data = json.dumps(repo_group_data)
426 c.repo_groups_data = json.dumps(repo_group_data)
426
427
427 return self._get_template_context(c)
428 return self._get_template_context(c)
429
430 @LoginRequired()
431 @CSRFRequired()
432 @view_config(
433 route_name='markup_preview', request_method='POST',
434 renderer='string', xhr=True)
435 def markup_preview(self):
436 # Technically a CSRF token is not needed as no state changes with this
437 # call. However, as this is a POST is better to have it, so automated
438 # tools don't flag it as potential CSRF.
439 # Post is required because the payload could be bigger than the maximum
440 # allowed by GET.
441
442 text = self.request.POST.get('text')
443 renderer = self.request.POST.get('renderer') or 'rst'
444 if text:
445 return h.render(text, renderer=renderer, mentions=True)
446 return ''
@@ -1418,9 +1418,6 b' table.integrations {'
1418 margin-top: @textmargin;
1418 margin-top: @textmargin;
1419 margin-bottom: @textmargin;
1419 margin-bottom: @textmargin;
1420 }
1420 }
1421 .pr-description {
1422 white-space:pre-wrap;
1423 }
1424
1421
1425 .pr-reviewer-rules {
1422 .pr-reviewer-rules {
1426 padding: 10px 0px 20px 0px;
1423 padding: 10px 0px 20px 0px;
@@ -2405,3 +2402,65 b' input[type=radio] {'
2405 height: 16px;
2402 height: 16px;
2406 width: 16px;
2403 width: 16px;
2407 }
2404 }
2405
2406
2407 .markup-form .clearfix {
2408 .border-radius(@border-radius);
2409 margin: 0px;
2410 }
2411
2412 .markup-form-area {
2413 padding: 8px 12px;
2414 border: 1px solid @grey4;
2415 .border-radius(@border-radius);
2416 }
2417
2418 .markup-form-area-header .nav-links {
2419 display: flex;
2420 flex-flow: row wrap;
2421 -webkit-flex-flow: row wrap;
2422 width: 100%;
2423 }
2424
2425 .markup-form-area-footer {
2426 display: flex;
2427 }
2428
2429 .markup-form-area-footer .toolbar {
2430
2431 }
2432
2433 // markup Form
2434 div.markup-form {
2435 margin-top: 20px;
2436 }
2437
2438 .markup-form strong {
2439 display: block;
2440 margin-bottom: 15px;
2441 }
2442
2443 .markup-form textarea {
2444 width: 100%;
2445 height: 100px;
2446 font-family: 'Monaco', 'Courier', 'Courier New', monospace;
2447 }
2448
2449 form.markup-form {
2450 margin-top: 10px;
2451 margin-left: 10px;
2452 }
2453
2454 .markup-form .comment-block-ta,
2455 .markup-form .preview-box {
2456 .border-radius(@border-radius);
2457 .box-sizing(border-box);
2458 background-color: white;
2459 }
2460
2461 .markup-form .preview-box.unloaded {
2462 height: 50px;
2463 text-align: center;
2464 padding: 20px;
2465 background-color: white;
2466 }
@@ -76,6 +76,7 b' function registerRCRoutes() {'
76 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
76 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
77 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
77 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
78 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
78 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
79 pyroutes.register('admin_settings_automation', '/_admin/_admin/settings/automation', []);
79 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
80 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
80 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
81 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
81 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
82 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
@@ -141,6 +142,7 b' function registerRCRoutes() {'
141 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
142 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
142 pyroutes.register('repo_list_data', '/_repos', []);
143 pyroutes.register('repo_list_data', '/_repos', []);
143 pyroutes.register('goto_switcher_data', '/_goto_data', []);
144 pyroutes.register('goto_switcher_data', '/_goto_data', []);
145 pyroutes.register('markup_preview', '/_markup_preview', []);
144 pyroutes.register('journal', '/_admin/journal', []);
146 pyroutes.register('journal', '/_admin/journal', []);
145 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
147 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
146 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
148 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
@@ -222,6 +224,7 b' function registerRCRoutes() {'
222 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
224 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
223 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
225 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
224 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
226 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
227 pyroutes.register('edit_repo_advanced_hooks', '/%(repo_name)s/settings/advanced/hooks', ['repo_name']);
225 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
228 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
226 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
229 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
227 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
230 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
@@ -275,6 +278,7 b' function registerRCRoutes() {'
275 pyroutes.register('search', '/_admin/search', []);
278 pyroutes.register('search', '/_admin/search', []);
276 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
279 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
277 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
280 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
281 pyroutes.register('user_group_profile', '/_profile_user_group/%(user_group_name)s', ['user_group_name']);
278 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
282 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
279 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
283 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
280 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
284 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
@@ -231,6 +231,68 b' var initCodeMirror = function(textAreadI'
231 return myCodeMirror;
231 return myCodeMirror;
232 };
232 };
233
233
234
235 var initMarkupCodeMirror = function(textAreadId, focus, options) {
236 var initialHeight = 100;
237
238 var ta = $(textAreadId).get(0);
239 if (focus === undefined) {
240 focus = true;
241 }
242
243 // default options
244 var codeMirrorOptions = {
245 lineNumbers: false,
246 indentUnit: 4,
247 viewportMargin: 30,
248 // this is a trick to trigger some logic behind codemirror placeholder
249 // it influences styling and behaviour.
250 placeholder: " ",
251 lineWrapping: true,
252 autofocus: focus
253 };
254
255 if (options !== undefined) {
256 // extend with custom options
257 codeMirrorOptions = $.extend(true, codeMirrorOptions, options);
258 }
259
260 var cm = CodeMirror.fromTextArea(ta, codeMirrorOptions);
261 cm.setSize(null, initialHeight);
262 cm.setOption("mode", DEFAULT_RENDERER);
263 CodeMirror.autoLoadMode(cm, DEFAULT_RENDERER); // load rst or markdown mode
264 cmLog.debug('Loading codemirror mode', DEFAULT_RENDERER);
265
266 // start listening on changes to make auto-expanded editor
267 cm.on("change", function(instance, changeObj) {
268 var height = initialHeight;
269 var lines = instance.lineCount();
270 if ( lines > 6 && lines < 20) {
271 height = "auto";
272 }
273 else if (lines >= 20){
274 zheight = 20*15;
275 }
276 instance.setSize(null, height);
277
278 // detect if the change was trigger by auto desc, or user input
279 var changeOrigin = changeObj.origin;
280
281 if (changeOrigin === "setValue") {
282 cmLog.debug('Change triggered by setValue');
283 }
284 else {
285 cmLog.debug('user triggered change !');
286 // set special marker to indicate user has created an input.
287 instance._userDefinedValue = true;
288 }
289
290 });
291
292 return cm;
293 };
294
295
234 var initCommentBoxCodeMirror = function(CommentForm, textAreaId, triggerActions){
296 var initCommentBoxCodeMirror = function(CommentForm, textAreaId, triggerActions){
235 var initialHeight = 100;
297 var initialHeight = 100;
236
298
@@ -593,3 +655,196 b' var CodeMirrorPreviewEnable = function(e'
593 }
655 }
594 }
656 }
595 };
657 };
658
659
660 /* markup form */
661 (function(mod) {
662
663 if (typeof exports == "object" && typeof module == "object") {
664 // CommonJS
665 module.exports = mod();
666 }
667 else {
668 // Plain browser env
669 (this || window).MarkupForm = mod();
670 }
671
672 })(function() {
673 "use strict";
674
675 function MarkupForm(textareaId) {
676 if (!(this instanceof MarkupForm)) {
677 return new MarkupForm(textareaId);
678 }
679
680 // bind the element instance to our Form
681 $('#' + textareaId).get(0).MarkupForm = this;
682
683 this.withSelectorId = function(selector) {
684 var selectorId = textareaId;
685 return selector + '_' + selectorId;
686 };
687
688 this.previewButton = this.withSelectorId('#preview-btn');
689 this.previewContainer = this.withSelectorId('#preview-container');
690
691 this.previewBoxSelector = this.withSelectorId('#preview-box');
692
693 this.editButton = this.withSelectorId('#edit-btn');
694 this.editContainer = this.withSelectorId('#edit-container');
695
696 this.cmBox = textareaId;
697 this.cm = initMarkupCodeMirror('#' + textareaId);
698
699 this.previewUrl = pyroutes.url('markup_preview');
700
701 // FUNCTIONS and helpers
702 var self = this;
703
704 this.getCmInstance = function(){
705 return this.cm
706 };
707
708 this.setPlaceholder = function(placeholder) {
709 var cm = this.getCmInstance();
710 if (cm){
711 cm.setOption('placeholder', placeholder);
712 }
713 };
714
715 this.initStatusChangeSelector = function(){
716 var formatChangeStatus = function(state, escapeMarkup) {
717 var originalOption = state.element;
718 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
719 '<span>' + escapeMarkup(state.text) + '</span>';
720 };
721 var formatResult = function(result, container, query, escapeMarkup) {
722 return formatChangeStatus(result, escapeMarkup);
723 };
724
725 var formatSelection = function(data, container, escapeMarkup) {
726 return formatChangeStatus(data, escapeMarkup);
727 };
728
729 $(this.submitForm).find(this.statusChange).select2({
730 placeholder: _gettext('Status Review'),
731 formatResult: formatResult,
732 formatSelection: formatSelection,
733 containerCssClass: "drop-menu status_box_menu",
734 dropdownCssClass: "drop-menu-dropdown",
735 dropdownAutoWidth: true,
736 minimumResultsForSearch: -1
737 });
738 $(this.submitForm).find(this.statusChange).on('change', function() {
739 var status = self.getCommentStatus();
740
741 if (status && !self.isInline()) {
742 $(self.submitButton).prop('disabled', false);
743 }
744
745 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
746 self.setPlaceholder(placeholderText)
747 })
748 };
749
750 // reset the text area into it's original state
751 this.resetMarkupFormState = function(content) {
752 content = content || '';
753
754 $(this.editContainer).show();
755 $(this.editButton).parent().addClass('active');
756
757 $(this.previewContainer).hide();
758 $(this.previewButton).parent().removeClass('active');
759
760 this.setActionButtonsDisabled(true);
761 self.cm.setValue(content);
762 self.cm.setOption("readOnly", false);
763 };
764
765 this.previewSuccessCallback = function(o) {
766 $(self.previewBoxSelector).html(o);
767 $(self.previewBoxSelector).removeClass('unloaded');
768
769 // swap buttons, making preview active
770 $(self.previewButton).parent().addClass('active');
771 $(self.editButton).parent().removeClass('active');
772
773 // unlock buttons
774 self.setActionButtonsDisabled(false);
775 };
776
777 this.setActionButtonsDisabled = function(state) {
778 $(this.editButton).prop('disabled', state);
779 $(this.previewButton).prop('disabled', state);
780 };
781
782 // lock preview/edit/submit buttons on load, but exclude cancel button
783 var excludeCancelBtn = true;
784 this.setActionButtonsDisabled(true);
785
786 // anonymous users don't have access to initialized CM instance
787 if (this.cm !== undefined){
788 this.cm.on('change', function(cMirror) {
789 if (cMirror.getValue() === "") {
790 self.setActionButtonsDisabled(true)
791 } else {
792 self.setActionButtonsDisabled(false)
793 }
794 });
795 }
796
797 $(this.editButton).on('click', function(e) {
798 e.preventDefault();
799
800 $(self.previewButton).parent().removeClass('active');
801 $(self.previewContainer).hide();
802
803 $(self.editButton).parent().addClass('active');
804 $(self.editContainer).show();
805
806 });
807
808 $(this.previewButton).on('click', function(e) {
809 e.preventDefault();
810 var text = self.cm.getValue();
811
812 if (text === "") {
813 return;
814 }
815
816 var postData = {
817 'text': text,
818 'renderer': templateContext.visual.default_renderer,
819 'csrf_token': CSRF_TOKEN
820 };
821
822 // lock ALL buttons on preview
823 self.setActionButtonsDisabled(true);
824
825 $(self.previewBoxSelector).addClass('unloaded');
826 $(self.previewBoxSelector).html(_gettext('Loading ...'));
827
828 $(self.editContainer).hide();
829 $(self.previewContainer).show();
830
831 // by default we reset state of comment preserving the text
832 var previewFailCallback = function(data){
833 alert(
834 "Error while submitting preview.\n" +
835 "Error code {0} ({1}).".format(data.status, data.statusText)
836 );
837 self.resetMarkupFormState(text)
838 };
839 _submitAjaxPOST(
840 self.previewUrl, postData, self.previewSuccessCallback,
841 previewFailCallback);
842
843 $(self.previewButton).parent().addClass('active');
844 $(self.editButton).parent().removeClass('active');
845 });
846
847 }
848
849 return MarkupForm;
850 });
@@ -42,6 +42,29 b' var bindToggleButtons = function() {'
42 });
42 });
43 };
43 };
44
44
45
46
47 var _submitAjaxPOST = function(url, postData, successHandler, failHandler) {
48 failHandler = failHandler || function() {};
49 postData = toQueryString(postData);
50 var request = $.ajax({
51 url: url,
52 type: 'POST',
53 data: postData,
54 headers: {'X-PARTIAL-XHR': true}
55 })
56 .done(function (data) {
57 successHandler(data);
58 })
59 .fail(function (data, textStatus, errorThrown) {
60 failHandler(data, textStatus, errorThrown)
61 });
62 return request;
63 };
64
65
66
67
45 /* Comment form for main and inline comments */
68 /* Comment form for main and inline comments */
46 (function(mod) {
69 (function(mod) {
47
70
@@ -259,24 +282,7 b' var bindToggleButtons = function() {'
259 };
282 };
260
283
261 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
284 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
262 failHandler = failHandler || function() {};
285 return _submitAjaxPOST(url, postData, successHandler, failHandler);
263 var postData = toQueryString(postData);
264 var request = $.ajax({
265 url: url,
266 type: 'POST',
267 data: postData,
268 headers: {'X-PARTIAL-XHR': true}
269 })
270 .done(function(data) {
271 successHandler(data);
272 })
273 .fail(function(data, textStatus, errorThrown){
274 alert(
275 "Error while submitting comment.\n" +
276 "Error code {0} ({1}).".format(data.status, data.statusText));
277 failHandler()
278 });
279 return request;
280 };
286 };
281
287
282 // overwrite a submitHandler, we need to do it for inline comments
288 // overwrite a submitHandler, we need to do it for inline comments
@@ -340,7 +346,11 b' var bindToggleButtons = function() {'
340 self.globalSubmitSuccessCallback();
346 self.globalSubmitSuccessCallback();
341
347
342 };
348 };
343 var submitFailCallback = function(){
349 var submitFailCallback = function(data) {
350 alert(
351 "Error while submitting comment.\n" +
352 "Error code {0} ({1}).".format(data.status, data.statusText)
353 );
344 self.resetCommentFormState(text);
354 self.resetCommentFormState(text);
345 };
355 };
346 self.submitAjaxPOST(
356 self.submitAjaxPOST(
@@ -436,7 +446,11 b' var bindToggleButtons = function() {'
436 $(self.previewContainer).show();
446 $(self.previewContainer).show();
437
447
438 // by default we reset state of comment preserving the text
448 // by default we reset state of comment preserving the text
439 var previewFailCallback = function(){
449 var previewFailCallback = function(data){
450 alert(
451 "Error while preview of comment.\n" +
452 "Error code {0} ({1}).".format(data.status, data.statusText)
453 );
440 self.resetCommentFormState(text)
454 self.resetCommentFormState(text)
441 };
455 };
442 self.submitAjaxPOST(
456 self.submitAjaxPOST(
@@ -763,7 +777,11 b' var CommentsController = function() {'
763 commentForm.setActionButtonsDisabled(false);
777 commentForm.setActionButtonsDisabled(false);
764
778
765 };
779 };
766 var submitFailCallback = function(){
780 var submitFailCallback = function(data){
781 alert(
782 "Error while submitting comment.\n" +
783 "Error code {0} ({1}).".format(data.status, data.statusText)
784 );
767 commentForm.resetCommentFormState(text)
785 commentForm.resetCommentFormState(text)
768 };
786 };
769 commentForm.submitAjaxPOST(
787 commentForm.submitAjaxPOST(
@@ -378,49 +378,6 b' var editPullRequest = function(repo_name'
378 ajaxPOST(url, postData, success);
378 ajaxPOST(url, postData, success);
379 };
379 };
380
380
381 var initPullRequestsCodeMirror = function (textAreaId) {
382 var ta = $(textAreaId).get(0);
383 var initialHeight = '100px';
384
385 // default options
386 var codeMirrorOptions = {
387 mode: "text",
388 lineNumbers: false,
389 indentUnit: 4,
390 theme: 'rc-input'
391 };
392
393 var codeMirrorInstance = CodeMirror.fromTextArea(ta, codeMirrorOptions);
394 // marker for manually set description
395 codeMirrorInstance._userDefinedDesc = false;
396 codeMirrorInstance.setSize(null, initialHeight);
397 codeMirrorInstance.on("change", function(instance, changeObj) {
398 var height = initialHeight;
399 var lines = instance.lineCount();
400 if (lines > 6 && lines < 20) {
401 height = "auto"
402 }
403 else if (lines >= 20) {
404 height = 20 * 15;
405 }
406 instance.setSize(null, height);
407
408 // detect if the change was trigger by auto desc, or user input
409 changeOrigin = changeObj.origin;
410
411 if (changeOrigin === "setValue") {
412 cmLog.debug('Change triggered by setValue');
413 }
414 else {
415 cmLog.debug('user triggered change !');
416 // set special marker to indicate user has created an input.
417 instance._userDefinedDesc = true;
418 }
419
420 });
421
422 return codeMirrorInstance
423 };
424
381
425 /**
382 /**
426 * Reviewer autocomplete
383 * Reviewer autocomplete
@@ -350,8 +350,7 b''
350 </%def>
350 </%def>
351
351
352 <%def name="pullrequest_title(title, description)">
352 <%def name="pullrequest_title(title, description)">
353 ${title} <br/>
353 ${title}
354 ${h.shorter(description, 40)}
355 </%def>
354 </%def>
356
355
357 <%def name="pullrequest_comments(comments_nr)">
356 <%def name="pullrequest_comments(comments_nr)">
@@ -375,3 +374,52 b''
375 <%def name="pullrequest_author(full_contact)">
374 <%def name="pullrequest_author(full_contact)">
376 ${base.gravatar_with_user(full_contact, 16)}
375 ${base.gravatar_with_user(full_contact, 16)}
377 </%def>
376 </%def>
377
378
379 <%def name="markup_form(form_id, form_text='', help_text=None)">
380
381 <div class="markup-form">
382 <div class="markup-form-area">
383 <div class="markup-form-area-header">
384 <ul class="nav-links clearfix">
385 <li class="active">
386 <a href="#edit-text" tabindex="-1" id="edit-btn_${form_id}">${_('Write')}</a>
387 </li>
388 <li class="">
389 <a href="#preview-text" tabindex="-1" id="preview-btn_${form_id}">${_('Preview')}</a>
390 </li>
391 </ul>
392 </div>
393
394 <div class="markup-form-area-write" style="display: block;">
395 <div id="edit-container_${form_id}">
396 <textarea id="${form_id}" name="${form_id}" class="comment-block-ta ac-input">${form_text if form_text else ''}</textarea>
397 </div>
398 <div id="preview-container_${form_id}" class="clearfix" style="display: none;">
399 <div id="preview-box_${form_id}" class="preview-box"></div>
400 </div>
401 </div>
402
403 <div class="markup-form-area-footer">
404 <div class="toolbar">
405 <div class="toolbar-text">
406 ${(_('Parsed using %s syntax') % (
407 ('<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
408 )
409 )|n}
410 </div>
411 </div>
412 </div>
413 </div>
414
415 <div class="markup-form-footer">
416 % if help_text:
417 <span class="help-block">${help_text}</span>
418 % endif
419 </div>
420 </div>
421 <script type="text/javascript">
422 new MarkupForm('${form_id}');
423 </script>
424
425 </%def>
This diff has been collapsed as it changes many lines, (633 lines changed) Show them Hide them
@@ -1,4 +1,5 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
2
3
3 <%def name="title()">
4 <%def name="title()">
4 ${c.repo_name} ${_('New pull request')}
5 ${c.repo_name} ${_('New pull request')}
@@ -54,14 +55,13 b''
54 <label for="pullrequest_desc">${_('Description')}:</label>
55 <label for="pullrequest_desc">${_('Description')}:</label>
55 </div>
56 </div>
56 <div class="textarea text-area editor">
57 <div class="textarea text-area editor">
57 ${h.textarea('pullrequest_desc',size=30, )}
58 ${dt.markup_form('pullrequest_desc')}
58 <span class="help-block">${_('Write a short description on this pull request')}</span>
59 </div>
59 </div>
60 </div>
60 </div>
61
61
62 <div class="field">
62 <div class="field">
63 <div class="label label-textarea">
63 <div class="label label-textarea">
64 <label for="pullrequest_desc">${_('Commit flow')}:</label>
64 <label for="commit_flow">${_('Commit flow')}:</label>
65 </div>
65 </div>
66
66
67 ## TODO: johbo: Abusing the "content" class here to get the
67 ## TODO: johbo: Abusing the "content" class here to get the
@@ -164,379 +164,384 b''
164 </div>
164 </div>
165
165
166 <script type="text/javascript">
166 <script type="text/javascript">
167 $(function(){
167 $(function(){
168 var defaultSourceRepo = '${c.default_repo_data['source_repo_name']}';
168 var defaultSourceRepo = '${c.default_repo_data['source_repo_name']}';
169 var defaultSourceRepoData = ${c.default_repo_data['source_refs_json']|n};
169 var defaultSourceRepoData = ${c.default_repo_data['source_refs_json']|n};
170 var defaultTargetRepo = '${c.default_repo_data['target_repo_name']}';
170 var defaultTargetRepo = '${c.default_repo_data['target_repo_name']}';
171 var defaultTargetRepoData = ${c.default_repo_data['target_refs_json']|n};
171 var defaultTargetRepoData = ${c.default_repo_data['target_refs_json']|n};
172
172
173 var $pullRequestForm = $('#pull_request_form');
173 var $pullRequestForm = $('#pull_request_form');
174 var $pullRequestSubmit = $('#pr_submit', $pullRequestForm);
174 var $pullRequestSubmit = $('#pr_submit', $pullRequestForm);
175 var $sourceRepo = $('#source_repo', $pullRequestForm);
175 var $sourceRepo = $('#source_repo', $pullRequestForm);
176 var $targetRepo = $('#target_repo', $pullRequestForm);
176 var $targetRepo = $('#target_repo', $pullRequestForm);
177 var $sourceRef = $('#source_ref', $pullRequestForm);
177 var $sourceRef = $('#source_ref', $pullRequestForm);
178 var $targetRef = $('#target_ref', $pullRequestForm);
178 var $targetRef = $('#target_ref', $pullRequestForm);
179
179
180 var sourceRepo = function() { return $sourceRepo.eq(0).val() };
180 var sourceRepo = function() { return $sourceRepo.eq(0).val() };
181 var sourceRef = function() { return $sourceRef.eq(0).val().split(':') };
181 var sourceRef = function() { return $sourceRef.eq(0).val().split(':') };
182
182
183 var targetRepo = function() { return $targetRepo.eq(0).val() };
183 var targetRepo = function() { return $targetRepo.eq(0).val() };
184 var targetRef = function() { return $targetRef.eq(0).val().split(':') };
184 var targetRef = function() { return $targetRef.eq(0).val().split(':') };
185
185
186 var calculateContainerWidth = function() {
186 var calculateContainerWidth = function() {
187 var maxWidth = 0;
187 var maxWidth = 0;
188 var repoSelect2Containers = ['#source_repo', '#target_repo'];
188 var repoSelect2Containers = ['#source_repo', '#target_repo'];
189 $.each(repoSelect2Containers, function(idx, value) {
189 $.each(repoSelect2Containers, function(idx, value) {
190 $(value).select2('container').width('auto');
190 $(value).select2('container').width('auto');
191 var curWidth = $(value).select2('container').width();
191 var curWidth = $(value).select2('container').width();
192 if (maxWidth <= curWidth) {
192 if (maxWidth <= curWidth) {
193 maxWidth = curWidth;
193 maxWidth = curWidth;
194 }
194 }
195 $.each(repoSelect2Containers, function(idx, value) {
195 $.each(repoSelect2Containers, function(idx, value) {
196 $(value).select2('container').width(maxWidth + 10);
196 $(value).select2('container').width(maxWidth + 10);
197 });
197 });
198 });
198 });
199 };
199 };
200
200
201 var initRefSelection = function(selectedRef) {
201 var initRefSelection = function(selectedRef) {
202 return function(element, callback) {
202 return function(element, callback) {
203 // translate our select2 id into a text, it's a mapping to show
203 // translate our select2 id into a text, it's a mapping to show
204 // simple label when selecting by internal ID.
204 // simple label when selecting by internal ID.
205 var id, refData;
205 var id, refData;
206 if (selectedRef === undefined || selectedRef === null) {
206 if (selectedRef === undefined || selectedRef === null) {
207 id = element.val();
207 id = element.val();
208 refData = element.val().split(':');
208 refData = element.val().split(':');
209
209
210 if (refData.length !== 3){
210 if (refData.length !== 3){
211 refData = ["", "", ""]
211 refData = ["", "", ""]
212 }
212 }
213 } else {
213 } else {
214 id = selectedRef;
214 id = selectedRef;
215 refData = selectedRef.split(':');
215 refData = selectedRef.split(':');
216 }
216 }
217
217
218 var text = refData[1];
218 var text = refData[1];
219 if (refData[0] === 'rev') {
219 if (refData[0] === 'rev') {
220 text = text.substring(0, 12);
220 text = text.substring(0, 12);
221 }
221 }
222
222
223 var data = {id: id, text: text};
223 var data = {id: id, text: text};
224 callback(data);
224 callback(data);
225 };
225 };
226 };
226 };
227
227
228 var formatRefSelection = function(item) {
228 var formatRefSelection = function(item) {
229 var prefix = '';
229 var prefix = '';
230 var refData = item.id.split(':');
230 var refData = item.id.split(':');
231 if (refData[0] === 'branch') {
231 if (refData[0] === 'branch') {
232 prefix = '<i class="icon-branch"></i>';
232 prefix = '<i class="icon-branch"></i>';
233 }
233 }
234 else if (refData[0] === 'book') {
234 else if (refData[0] === 'book') {
235 prefix = '<i class="icon-bookmark"></i>';
235 prefix = '<i class="icon-bookmark"></i>';
236 }
236 }
237 else if (refData[0] === 'tag') {
237 else if (refData[0] === 'tag') {
238 prefix = '<i class="icon-tag"></i>';
238 prefix = '<i class="icon-tag"></i>';
239 }
239 }
240
240
241 var originalOption = item.element;
241 var originalOption = item.element;
242 return prefix + item.text;
242 return prefix + item.text;
243 };
243 };
244
244
245 // custom code mirror
245 // custom code mirror
246 var codeMirrorInstance = initPullRequestsCodeMirror('#pullrequest_desc');
246 var codeMirrorInstance = $('#pullrequest_desc').get(0).MarkupForm.cm;
247
247
248 reviewersController = new ReviewersController();
248 reviewersController = new ReviewersController();
249
249
250 var queryTargetRepo = function(self, query) {
250 var queryTargetRepo = function(self, query) {
251 // cache ALL results if query is empty
251 // cache ALL results if query is empty
252 var cacheKey = query.term || '__';
252 var cacheKey = query.term || '__';
253 var cachedData = self.cachedDataSource[cacheKey];
253 var cachedData = self.cachedDataSource[cacheKey];
254
254
255 if (cachedData) {
255 if (cachedData) {
256 query.callback({results: cachedData.results});
256 query.callback({results: cachedData.results});
257 } else {
257 } else {
258 $.ajax({
258 $.ajax({
259 url: pyroutes.url('pullrequest_repo_destinations', {'repo_name': templateContext.repo_name}),
259 url: pyroutes.url('pullrequest_repo_destinations', {'repo_name': templateContext.repo_name}),
260 data: {query: query.term},
260 data: {query: query.term},
261 dataType: 'json',
261 dataType: 'json',
262 type: 'GET',
262 type: 'GET',
263 success: function(data) {
263 success: function(data) {
264 self.cachedDataSource[cacheKey] = data;
264 self.cachedDataSource[cacheKey] = data;
265 query.callback({results: data.results});
265 query.callback({results: data.results});
266 },
266 },
267 error: function(data, textStatus, errorThrown) {
267 error: function(data, textStatus, errorThrown) {
268 alert(
268 alert(
269 "Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
269 "Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
270 }
270 }
271 });
271 });
272 }
272 }
273 };
273 };
274
274
275 var queryTargetRefs = function(initialData, query) {
275 var queryTargetRefs = function(initialData, query) {
276 var data = {results: []};
276 var data = {results: []};
277 // filter initialData
277 // filter initialData
278 $.each(initialData, function() {
278 $.each(initialData, function() {
279 var section = this.text;
279 var section = this.text;
280 var children = [];
280 var children = [];
281 $.each(this.children, function() {
281 $.each(this.children, function() {
282 if (query.term.length === 0 ||
282 if (query.term.length === 0 ||
283 this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ) {
283 this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ) {
284 children.push({'id': this.id, 'text': this.text})
284 children.push({'id': this.id, 'text': this.text})
285 }
285 }
286 });
286 });
287 data.results.push({'text': section, 'children': children})
287 data.results.push({'text': section, 'children': children})
288 });
288 });
289 query.callback({results: data.results});
289 query.callback({results: data.results});
290 };
290 };
291
291
292 var loadRepoRefDiffPreview = function() {
292 var loadRepoRefDiffPreview = function() {
293
293
294 var url_data = {
294 var url_data = {
295 'repo_name': targetRepo(),
295 'repo_name': targetRepo(),
296 'target_repo': sourceRepo(),
296 'target_repo': sourceRepo(),
297 'source_ref': targetRef()[2],
297 'source_ref': targetRef()[2],
298 'source_ref_type': 'rev',
298 'source_ref_type': 'rev',
299 'target_ref': sourceRef()[2],
299 'target_ref': sourceRef()[2],
300 'target_ref_type': 'rev',
300 'target_ref_type': 'rev',
301 'merge': true,
301 'merge': true,
302 '_': Date.now() // bypass browser caching
302 '_': Date.now() // bypass browser caching
303 }; // gather the source/target ref and repo here
303 }; // gather the source/target ref and repo here
304
304
305 if (sourceRef().length !== 3 || targetRef().length !== 3) {
305 if (sourceRef().length !== 3 || targetRef().length !== 3) {
306 prButtonLock(true, "${_('Please select source and target')}");
306 prButtonLock(true, "${_('Please select source and target')}");
307 return;
307 return;
308 }
308 }
309 var url = pyroutes.url('repo_compare', url_data);
309 var url = pyroutes.url('repo_compare', url_data);
310
310
311 // lock PR button, so we cannot send PR before it's calculated
311 // lock PR button, so we cannot send PR before it's calculated
312 prButtonLock(true, "${_('Loading compare ...')}", 'compare');
312 prButtonLock(true, "${_('Loading compare ...')}", 'compare');
313
314 if (loadRepoRefDiffPreview._currentRequest) {
315 loadRepoRefDiffPreview._currentRequest.abort();
316 }
313
317
314 if (loadRepoRefDiffPreview._currentRequest) {
318 loadRepoRefDiffPreview._currentRequest = $.get(url)
315 loadRepoRefDiffPreview._currentRequest.abort();
319 .error(function(data, textStatus, errorThrown) {
316 }
320 if (textStatus !== 'abort') {
321 alert(
322 "Error while processing request.\nError code {0} ({1}).".format(
323 data.status, data.statusText));
324 }
317
325
318 loadRepoRefDiffPreview._currentRequest = $.get(url)
326 })
319 .error(function(data, textStatus, errorThrown) {
327 .done(function(data) {
320 if (textStatus !== 'abort') {
328 loadRepoRefDiffPreview._currentRequest = null;
321 alert(
329 $('#pull_request_overview').html(data);
322 "Error while processing request.\nError code {0} ({1}).".format(
330
323 data.status, data.statusText));
331 var commitElements = $(data).find('tr[commit_id]');
324 }
325
332
326 })
333 var prTitleAndDesc = getTitleAndDescription(
327 .done(function(data) {
334 sourceRef()[1], commitElements, 5);
328 loadRepoRefDiffPreview._currentRequest = null;
329 $('#pull_request_overview').html(data);
330
335
331 var commitElements = $(data).find('tr[commit_id]');
336 var title = prTitleAndDesc[0];
337 var proposedDescription = prTitleAndDesc[1];
332
338
333 var prTitleAndDesc = getTitleAndDescription(
339 var useGeneratedTitle = (
334 sourceRef()[1], commitElements, 5);
340 $('#pullrequest_title').hasClass('autogenerated-title') ||
341 $('#pullrequest_title').val() === "");
335
342
336 var title = prTitleAndDesc[0];
343 if (title && useGeneratedTitle) {
337 var proposedDescription = prTitleAndDesc[1];
344 // use generated title if we haven't specified our own
338
345 $('#pullrequest_title').val(title);
339 var useGeneratedTitle = (
346 $('#pullrequest_title').addClass('autogenerated-title');
340 $('#pullrequest_title').hasClass('autogenerated-title') ||
341 $('#pullrequest_title').val() === "");
342
347
343 if (title && useGeneratedTitle) {
348 }
344 // use generated title if we haven't specified our own
349
345 $('#pullrequest_title').val(title);
350 var useGeneratedDescription = (
346 $('#pullrequest_title').addClass('autogenerated-title');
351 !codeMirrorInstance._userDefinedValue ||
352 codeMirrorInstance.getValue() === "");
347
353
348 }
354 if (proposedDescription && useGeneratedDescription) {
355 // set proposed content, if we haven't defined our own,
356 // or we don't have description written
357 codeMirrorInstance._userDefinedValue = false; // reset state
358 codeMirrorInstance.setValue(proposedDescription);
359 }
349
360
350 var useGeneratedDescription = (
361 // refresh our codeMirror so events kicks in and it's change aware
351 !codeMirrorInstance._userDefinedDesc ||
362 codeMirrorInstance.refresh();
352 codeMirrorInstance.getValue() === "");
353
363
354 if (proposedDescription && useGeneratedDescription) {
364 var msg = '';
355 // set proposed content, if we haven't defined our own,
365 if (commitElements.length === 1) {
356 // or we don't have description written
366 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 1)}";
357 codeMirrorInstance._userDefinedDesc = false; // reset state
367 } else {
358 codeMirrorInstance.setValue(proposedDescription);
368 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 2)}";
359 }
369 }
360
370
361 var msg = '';
371 msg += ' <a id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
362 if (commitElements.length === 1) {
363 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 1)}";
364 } else {
365 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 2)}";
366 }
367
372
368 msg += ' <a id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
373 if (commitElements.length) {
369
374 var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length);
370 if (commitElements.length) {
375 prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare');
371 var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length);
376 }
372 prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare');
377 else {
373 }
378 prButtonLock(true, "${_('There are no commits to merge.')}", 'compare');
374 else {
379 }
375 prButtonLock(true, "${_('There are no commits to merge.')}", 'compare');
376 }
377
380
378
381
379 });
382 });
380 };
383 };
381
384
382 var Select2Box = function(element, overrides) {
385 var Select2Box = function(element, overrides) {
383 var globalDefaults = {
386 var globalDefaults = {
384 dropdownAutoWidth: true,
387 dropdownAutoWidth: true,
385 containerCssClass: "drop-menu",
388 containerCssClass: "drop-menu",
386 dropdownCssClass: "drop-menu-dropdown"
389 dropdownCssClass: "drop-menu-dropdown"
387 };
390 };
388
391
389 var initSelect2 = function(defaultOptions) {
392 var initSelect2 = function(defaultOptions) {
390 var options = jQuery.extend(globalDefaults, defaultOptions, overrides);
393 var options = jQuery.extend(globalDefaults, defaultOptions, overrides);
391 element.select2(options);
394 element.select2(options);
392 };
395 };
393
396
394 return {
397 return {
395 initRef: function() {
398 initRef: function() {
396 var defaultOptions = {
399 var defaultOptions = {
397 minimumResultsForSearch: 5,
400 minimumResultsForSearch: 5,
398 formatSelection: formatRefSelection
401 formatSelection: formatRefSelection
399 };
402 };
400
403
401 initSelect2(defaultOptions);
404 initSelect2(defaultOptions);
402 },
405 },
403
406
404 initRepo: function(defaultValue, readOnly) {
407 initRepo: function(defaultValue, readOnly) {
405 var defaultOptions = {
408 var defaultOptions = {
406 initSelection : function (element, callback) {
409 initSelection : function (element, callback) {
407 var data = {id: defaultValue, text: defaultValue};
410 var data = {id: defaultValue, text: defaultValue};
408 callback(data);
411 callback(data);
409 }
412 }
410 };
413 };
411
414
412 initSelect2(defaultOptions);
415 initSelect2(defaultOptions);
413
416
414 element.select2('val', defaultSourceRepo);
417 element.select2('val', defaultSourceRepo);
415 if (readOnly === true) {
418 if (readOnly === true) {
416 element.select2('readonly', true);
419 element.select2('readonly', true);
417 }
420 }
418 }
421 }
419 };
422 };
420 };
423 };
421
424
422 var initTargetRefs = function(refsData, selectedRef) {
425 var initTargetRefs = function(refsData, selectedRef) {
423
426
424 Select2Box($targetRef, {
427 Select2Box($targetRef, {
425 placeholder: "${_('Select commit reference')}",
428 placeholder: "${_('Select commit reference')}",
426 query: function(query) {
429 query: function(query) {
427 queryTargetRefs(refsData, query);
430 queryTargetRefs(refsData, query);
428 },
431 },
429 initSelection : initRefSelection(selectedRef)
432 initSelection : initRefSelection(selectedRef)
430 }).initRef();
433 }).initRef();
431
434
432 if (!(selectedRef === undefined)) {
435 if (!(selectedRef === undefined)) {
433 $targetRef.select2('val', selectedRef);
436 $targetRef.select2('val', selectedRef);
434 }
437 }
435 };
438 };
436
439
437 var targetRepoChanged = function(repoData) {
440 var targetRepoChanged = function(repoData) {
438 // generate new DESC of target repo displayed next to select
441 // generate new DESC of target repo displayed next to select
439 var prLink = pyroutes.url('pullrequest_new', {'repo_name': repoData['name']});
442 var prLink = pyroutes.url('pullrequest_new', {'repo_name': repoData['name']});
440 $('#target_repo_desc').html(
443 $('#target_repo_desc').html(
441 "<strong>${_('Target repository')}</strong>: {0}. <a href=\"{1}\">Switch base, and use as source.</a>".format(repoData['description'], prLink)
444 "<strong>${_('Target repository')}</strong>: {0}. <a href=\"{1}\">Switch base, and use as source.</a>".format(repoData['description'], prLink)
442 );
445 );
443
446
444 // generate dynamic select2 for refs.
447 // generate dynamic select2 for refs.
445 initTargetRefs(repoData['refs']['select2_refs'],
448 initTargetRefs(repoData['refs']['select2_refs'],
446 repoData['refs']['selected_ref']);
449 repoData['refs']['selected_ref']);
447
450
448 };
451 };
449
452
450 var sourceRefSelect2 = Select2Box($sourceRef, {
453 var sourceRefSelect2 = Select2Box($sourceRef, {
451 placeholder: "${_('Select commit reference')}",
454 placeholder: "${_('Select commit reference')}",
452 query: function(query) {
455 query: function(query) {
453 var initialData = defaultSourceRepoData['refs']['select2_refs'];
456 var initialData = defaultSourceRepoData['refs']['select2_refs'];
454 queryTargetRefs(initialData, query)
457 queryTargetRefs(initialData, query)
455 },
458 },
456 initSelection: initRefSelection()
459 initSelection: initRefSelection()
457 }
460 }
458 );
461 );
459
462
460 var sourceRepoSelect2 = Select2Box($sourceRepo, {
463 var sourceRepoSelect2 = Select2Box($sourceRepo, {
461 query: function(query) {}
464 query: function(query) {}
462 });
465 });
463
466
464 var targetRepoSelect2 = Select2Box($targetRepo, {
467 var targetRepoSelect2 = Select2Box($targetRepo, {
465 cachedDataSource: {},
468 cachedDataSource: {},
466 query: $.debounce(250, function(query) {
469 query: $.debounce(250, function(query) {
467 queryTargetRepo(this, query);
470 queryTargetRepo(this, query);
468 }),
471 }),
469 formatResult: formatRepoResult
472 formatResult: formatRepoResult
470 });
473 });
471
474
472 sourceRefSelect2.initRef();
475 sourceRefSelect2.initRef();
473
476
474 sourceRepoSelect2.initRepo(defaultSourceRepo, true);
477 sourceRepoSelect2.initRepo(defaultSourceRepo, true);
475
478
476 targetRepoSelect2.initRepo(defaultTargetRepo, false);
479 targetRepoSelect2.initRepo(defaultTargetRepo, false);
477
480
478 $sourceRef.on('change', function(e){
481 $sourceRef.on('change', function(e){
479 loadRepoRefDiffPreview();
482 loadRepoRefDiffPreview();
480 reviewersController.loadDefaultReviewers(
483 reviewersController.loadDefaultReviewers(
481 sourceRepo(), sourceRef(), targetRepo(), targetRef());
484 sourceRepo(), sourceRef(), targetRepo(), targetRef());
482 });
485 });
483
486
484 $targetRef.on('change', function(e){
487 $targetRef.on('change', function(e){
485 loadRepoRefDiffPreview();
488 loadRepoRefDiffPreview();
486 reviewersController.loadDefaultReviewers(
489 reviewersController.loadDefaultReviewers(
487 sourceRepo(), sourceRef(), targetRepo(), targetRef());
490 sourceRepo(), sourceRef(), targetRepo(), targetRef());
488 });
491 });
489
492
490 $targetRepo.on('change', function(e){
493 $targetRepo.on('change', function(e){
491 var repoName = $(this).val();
494 var repoName = $(this).val();
492 calculateContainerWidth();
495 calculateContainerWidth();
493 $targetRef.select2('destroy');
496 $targetRef.select2('destroy');
494 $('#target_ref_loading').show();
497 $('#target_ref_loading').show();
495
498
496 $.ajax({
499 $.ajax({
497 url: pyroutes.url('pullrequest_repo_refs',
500 url: pyroutes.url('pullrequest_repo_refs',
498 {'repo_name': templateContext.repo_name, 'target_repo_name':repoName}),
501 {'repo_name': templateContext.repo_name, 'target_repo_name':repoName}),
499 data: {},
502 data: {},
500 dataType: 'json',
503 dataType: 'json',
501 type: 'GET',
504 type: 'GET',
502 success: function(data) {
505 success: function(data) {
503 $('#target_ref_loading').hide();
506 $('#target_ref_loading').hide();
504 targetRepoChanged(data);
507 targetRepoChanged(data);
505 loadRepoRefDiffPreview();
508 loadRepoRefDiffPreview();
506 },
509 },
507 error: function(data, textStatus, errorThrown) {
510 error: function(data, textStatus, errorThrown) {
508 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
511 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
509 }
512 }
510 })
513 })
511
514
512 });
515 });
513
514 $pullRequestForm.on('submit', function(e){
515 prButtonLock(true, null, 'all');
516 });
517
516
518 prButtonLock(true, "${_('Please select source and target')}", 'all');
517 $pullRequestForm.on('submit', function(e){
518 // Flush changes into textarea
519 codeMirrorInstance.save();
520 prButtonLock(true, null, 'all');
521 });
519
522
520 // auto-load on init, the target refs select2
523 prButtonLock(true, "${_('Please select source and target')}", 'all');
521 calculateContainerWidth();
522 targetRepoChanged(defaultTargetRepoData);
523
524
524 $('#pullrequest_title').on('keyup', function(e){
525 // auto-load on init, the target refs select2
525 $(this).removeClass('autogenerated-title');
526 calculateContainerWidth();
526 });
527 targetRepoChanged(defaultTargetRepoData);
527
528
528 % if c.default_source_ref:
529 $('#pullrequest_title').on('keyup', function(e){
529 // in case we have a pre-selected value, use it now
530 $(this).removeClass('autogenerated-title');
530 $sourceRef.select2('val', '${c.default_source_ref}');
531 });
531 // diff preview load
532 loadRepoRefDiffPreview();
533 // default reviewers
534 reviewersController.loadDefaultReviewers(
535 sourceRepo(), sourceRef(), targetRepo(), targetRef());
536 % endif
537
532
538 ReviewerAutoComplete('#user');
533 % if c.default_source_ref:
539 });
534 // in case we have a pre-selected value, use it now
535 $sourceRef.select2('val', '${c.default_source_ref}');
536 // diff preview load
537 loadRepoRefDiffPreview();
538 // default reviewers
539 reviewersController.loadDefaultReviewers(
540 sourceRepo(), sourceRef(), targetRepo(), targetRef());
541 % endif
542
543 ReviewerAutoComplete('#user');
544 });
540 </script>
545 </script>
541
546
542 </%def>
547 </%def>
@@ -1,5 +1,6 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2 <%namespace name="base" file="/base/base.mako"/>
2 <%namespace name="base" file="/base/base.mako"/>
3 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
3
4
4 <%def name="title()">
5 <%def name="title()">
5 ${_('%s Pull Request #%s') % (c.repo_name, c.pull_request.pull_request_id)}
6 ${_('%s Pull Request #%s') % (c.repo_name, c.pull_request.pull_request_id)}
@@ -169,10 +170,10 b''
169 <label>${_('Description')}:</label>
170 <label>${_('Description')}:</label>
170 </div>
171 </div>
171 <div id="pr-desc" class="input">
172 <div id="pr-desc" class="input">
172 <div class="pr-description">${h.urlify_commit_message(c.pull_request.description, c.repo_name)}</div>
173 <div class="pr-description">${h.render(c.pull_request.description, renderer=c.visual.default_renderer)}</div>
173 </div>
174 </div>
174 <div id="pr-desc-edit" class="input textarea editor" style="display: none;">
175 <div id="pr-desc-edit" class="input textarea editor" style="display: none;">
175 <textarea id="pr-description-input" size="30">${c.pull_request.description}</textarea>
176 ${dt.markup_form('pr-description-input', form_text=c.pull_request.description)}
176 </div>
177 </div>
177 </div>
178 </div>
178
179
@@ -643,7 +644,7 b''
643 $(function(){
644 $(function(){
644
645
645 // custom code mirror
646 // custom code mirror
646 var codeMirrorInstance = initPullRequestsCodeMirror('#pr-description-input');
647 var codeMirrorInstance = $('#pr-description-input').get(0).MarkupForm.cm;
647
648
648 var PRDetails = {
649 var PRDetails = {
649 editButton: $('#open_edit_pullrequest'),
650 editButton: $('#open_edit_pullrequest'),
General Comments 0
You need to be logged in to leave comments. Login now