##// 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>
@@ -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
@@ -243,7 +243,7 b''
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
@@ -348,16 +348,19 b''
348 }
348 }
349
349
350 var useGeneratedDescription = (
350 var useGeneratedDescription = (
351 !codeMirrorInstance._userDefinedDesc ||
351 !codeMirrorInstance._userDefinedValue ||
352 codeMirrorInstance.getValue() === "");
352 codeMirrorInstance.getValue() === "");
353
353
354 if (proposedDescription && useGeneratedDescription) {
354 if (proposedDescription && useGeneratedDescription) {
355 // set proposed content, if we haven't defined our own,
355 // set proposed content, if we haven't defined our own,
356 // or we don't have description written
356 // or we don't have description written
357 codeMirrorInstance._userDefinedDesc = false; // reset state
357 codeMirrorInstance._userDefinedValue = false; // reset state
358 codeMirrorInstance.setValue(proposedDescription);
358 codeMirrorInstance.setValue(proposedDescription);
359 }
359 }
360
360
361 // refresh our codeMirror so events kicks in and it's change aware
362 codeMirrorInstance.refresh();
363
361 var msg = '';
364 var msg = '';
362 if (commitElements.length === 1) {
365 if (commitElements.length === 1) {
363 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 1)}";
366 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 1)}";
@@ -512,6 +515,8 b''
512 });
515 });
513
516
514 $pullRequestForm.on('submit', function(e){
517 $pullRequestForm.on('submit', function(e){
518 // Flush changes into textarea
519 codeMirrorInstance.save();
515 prButtonLock(true, null, 'all');
520 prButtonLock(true, null, 'all');
516 });
521 });
517
522
@@ -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