##// END OF EJS Templates
helper-blocks: fix pre formatting only for auth plugin helper texts....
marcink -
r1470:337b0a05 default
parent child Browse files
Show More
@@ -1,383 +1,385 b''
1 1
2 2 // Contains the style definitions used for .main-content
3 3 // elements which are mainly around the admin settings.
4 4
5 5
6 6 // TODO: johbo: Integrate in a better way, this is for "main content" which
7 7 // should not have a limit on the width.
8 8 .main-content-full {
9 9 clear: both;
10 10 }
11 11
12 12
13 13 .main-content {
14 14 max-width: @maincontent-maxwidth;
15 15
16 16 h3,
17 17 // TODO: johbo: Change templates to use h3 instead of h4 here
18 18 h4 {
19 19 line-height: 1em;
20 20 }
21 21
22 22 // TODO: johbo: Check if we can do that on a global level
23 23 table {
24 24 th {
25 25 padding: 0;
26 26 }
27 27 td.field{
28 28 .help-block{
29 29 margin-left: 0;
30 30 }
31 31 }
32 32 }
33 33
34 34 // TODO: johbo: Tweak this into the general styling, for a full width
35 35 // textarea
36 36 .textarea-full {
37 37 // 2x 10px padding and 2x 1px border
38 38 margin-right: 22px;
39 39 }
40 40
41 41 }
42 42
43 43
44 44 // TODO: johbo: duplicated, think about a mixins.less
45 45 .block-left{
46 46 float: left;
47 47 }
48 48
49 49 .form {
50 50 .checkboxes {
51 51 // TODO: johbo: Should be changed in .checkboxes already
52 52 width: auto;
53 53 }
54 54
55 55 // TODO: johbo: some settings pages are broken and don't have the .buttons
56 56 // inside the .fields, tweak those templates and remove this.
57 57 .buttons {
58 58 margin-top: @textmargin;
59 59 }
60 60
61 61 .help-block {
62 62 display: block;
63 63 margin-left: @label-width;
64 white-space: pre;
64 &.pre-formatting {
65 white-space: pre;
66 }
65 67 }
66 68
67 69 .action_button {
68 70 color: @grey4;
69 71 }
70 72 }
71 73
72 74 .main-content-full-width {
73 75 .main-content;
74 76 width: 100%;
75 77 min-width: 100%;
76 78 }
77 79
78 80 .field {
79 81 clear: left;
80 82 margin-bottom: @padding;
81 83
82 84 }
83 85
84 86 .input-monospace {
85 87 font-family: @font-family-monospace;
86 88 }
87 89
88 90 .fields {
89 91 label {
90 92 color: @grey2;
91 93 }
92 94
93 95 .field {
94 96 clear: right;
95 97 margin-bottom: @textmargin;
96 98 width: 100%;
97 99
98 100 .label {
99 101 float: left;
100 102 margin-right: @form-vertical-margin;
101 103 margin-top: 0;
102 104 padding-top: @input-padding-px + @border-thickness-inputs;
103 105 width: @label-width - @form-vertical-margin;
104 106 }
105 107 // used in forms for fields that show just text
106 108 .label-text {
107 109 .label;
108 110 padding-top: 5px;
109 111 }
110 112 // Used to position content on the right side of a .label
111 113 .content,
112 114 .side-by-side-selector {
113 115 padding-top: @input-padding-px + @input-border-thickness;
114 116 }
115 117
116 118 .checkboxes,
117 119 .input,
118 120 .select,
119 121 .textarea,
120 122 .content {
121 123 float: none;
122 124 margin-left: @label-width;
123 125
124 126 .help-block {
125 127 margin-left: 0;
126 128 }
127 129 }
128 130
129 131 .checkboxes,
130 132 .input,
131 133 .select {
132 134 .help-block {
133 135 display: block;
134 136 }
135 137 }
136 138
137 139 .checkboxes,
138 140 .radios {
139 141 // TODO: johbo: We get a 4px margin from the from-bootstrap,
140 142 // compensating here to align well with labels on the left.
141 143 padding-top: @input-padding-px + @input-border-thickness - 3px;
142 144 }
143 145
144 146 .checkbox,
145 147 .radio {
146 148 display: block;
147 149 width: auto;
148 150 }
149 151
150 152 .checkbox + .checkbox {
151 153 display: block;
152 154 }
153 155
154 156 .input,
155 157 .select {
156 158 .help-block,
157 159 .info-block {
158 160 margin-top: @form-vertical-margin / 2;
159 161 }
160 162 }
161 163
162 164 .input {
163 165 .medium {
164 166 width: @fields-input-m;
165 167 }
166 168 .large {
167 169 width: @fields-input-l;
168 170 }
169 171
170 172 .text-as-placeholder {
171 173 padding-top: @input-padding-px + @border-thickness-inputs;
172 174 }
173 175 }
174 176
175 177 // TODO: johbo: Try to find a better integration of this bit.
176 178 // When using a select2 inside of a field, it should not have the
177 179 // top margin.
178 180 .select .drop-menu {
179 181 margin-top: 0;
180 182 }
181 183
182 184 .textarea {
183 185 float: none;
184 186
185 187 textarea {
186 188 // TODO: johbo: From somewhere we get a clear which does
187 189 // more harm than good here.
188 190 clear: none;
189 191 }
190 192
191 193 .CodeMirror {
192 194 // TODO: johbo: Tweak to position the .help-block nicer,
193 195 // figure out how to apply for .text-block instead.
194 196 margin-bottom: 10px;
195 197 }
196 198
197 199 // TODO: johbo: Check if we can remove the grey background on
198 200 // the global level and remove this if possible.
199 201 .help-block {
200 202 background: transparent;
201 203 padding: 0;
202 204 }
203 205 }
204 206
205 207 &.tag_patterns,
206 208 &.branch_patterns {
207 209
208 210 input {
209 211 max-width: 430px;
210 212 }
211 213 }
212 214 }
213 215
214 216 .field-sm {
215 217 .label {
216 218 padding-top: @input-padding-px / 2 + @input-border-thickness;
217 219 }
218 220 .checkboxes,
219 221 .radios {
220 222 // TODO: johbo: We get a 4px margin from the from-bootstrap,
221 223 // compensating here to align well with labels on the left.
222 224 padding-top: @input-padding-px / 2 + @input-border-thickness - 3px;
223 225 }
224 226 }
225 227
226 228 .field.customhooks {
227 229 .label {
228 230 padding-top: 0;
229 231 }
230 232 .input-wrapper {
231 233 padding-right: 25px;
232 234
233 235 input {
234 236 width: 100%;
235 237 }
236 238 }
237 239 .input {
238 240 padding-right: 25px;
239 241 }
240 242 }
241 243
242 244 .buttons {
243 245 // TODO: johbo: define variable for this value.
244 246 // Note that this should be 40px but since most elements add some
245 247 // space in the bottom, we are with 20 closer to 40.
246 248 margin-top: 20px;
247 249 clear: both;
248 250 margin-bottom: @padding;
249 251 }
250 252
251 253 .desc{
252 254 margin-right: @textmargin;
253 255 }
254 256
255 257 input,
256 258 .drop-menu {
257 259 margin-right: @padding/3;
258 260 }
259 261
260 262 }
261 263
262 264 .form-vertical .fields .field {
263 265
264 266 .label {
265 267 float: none;
266 268 width: auto;
267 269 }
268 270
269 271 .checkboxes,
270 272 .input,
271 273 .select,
272 274 .textarea {
273 275 margin-left: 0;
274 276 }
275 277
276 278 // TODO: johbo: had to tweak the width here to make it big enough for
277 279 // the license.
278 280 .textarea.editor {
279 281 max-width: none;
280 282 }
281 283
282 284 .textarea.large textarea {
283 285 min-height: 200px;
284 286 }
285 287
286 288 .help-block {
287 289 margin-left: 0;
288 290 }
289 291 }
290 292
291 293
292 294
293 295
294 296 .main-content {
295 297 .block-left;
296 298
297 299 .section {
298 300 margin-bottom: @space;
299 301 }
300 302
301 303
302 304 // Table aligning same way as forms in admin section, e.g.
303 305 // python packages table
304 306 table.formalign {
305 307 float: left;
306 308 width: auto;
307 309
308 310 .label {
309 311 width: @label-width;
310 312 }
311 313
312 314 }
313 315
314 316
315 317 table.issuetracker {
316 318
317 319 color: @text-color;
318 320
319 321 .issue-tracker-example {
320 322 color: @grey4;
321 323 }
322 324 }
323 325
324 326 .side-by-side-selector{
325 327 .left-group,
326 328 .middle-group,
327 329 .right-group{
328 330 float: left;
329 331 }
330 332
331 333 .left-group,
332 334 .right-group{
333 335 width: 45%;
334 336 text-align: center;
335 337
336 338 label{
337 339 width: 100%;
338 340 text-align: left;
339 341 }
340 342
341 343 select{
342 344 width: 100%;
343 345 background: none;
344 346 border-color: @border-highlight-color;
345 347 color: @text-color;
346 348 font-family: @text-light;
347 349 font-size: @basefontsize;
348 350 color: @grey1;
349 351 padding: @textmargin/2;
350 352 }
351 353
352 354 select:after{
353 355 content: "";
354 356 }
355 357
356 358 }
357 359
358 360 .middle-group{
359 361 width: 10%;
360 362 text-align: center;
361 363 padding-top: 4em;
362 364 i {
363 365 font-size: 18px;
364 366 cursor: pointer;
365 367 line-height: 2em;
366 368 }
367 369 }
368 370
369 371 }
370 372
371 373 .permissions_boxes{
372 374 label, .label{
373 375 margin-right: @textmargin/2;
374 376 }
375 377 }
376 378
377 379 .radios{
378 380 label{
379 381 margin-right: @textmargin;
380 382 }
381 383 }
382 384 }
383 385
@@ -1,118 +1,118 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.mako"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Authentication Settings')}
6 6 %if c.rhodecode_name:
7 7 &middot; ${h.branding(c.rhodecode_name)}}
8 8 %endif
9 9 </%def>
10 10
11 11 <%def name="breadcrumbs_links()">
12 12 ${h.link_to(_('Admin'),h.url('admin_home'))}
13 13 &raquo;
14 14 ${h.link_to(_('Authentication Plugins'),request.resource_path(resource.__parent__, route_name='auth_home'))}
15 15 &raquo;
16 16 ${resource.display_name}
17 17 </%def>
18 18
19 19 <%def name="menu_bar_nav()">
20 20 ${self.menu_items(active='admin')}
21 21 </%def>
22 22
23 23 <%def name="main()">
24 24 <div class="box">
25 25 <div class="title">
26 26 ${self.breadcrumbs()}
27 27 </div>
28 28 <div class='sidebar-col-wrapper'>
29 29
30 30 ## TODO: This is repeated in the auth root template and should be merged
31 31 ## into a single solution.
32 32 <div class="sidebar">
33 33 <ul class="nav nav-pills nav-stacked">
34 34 % for item in resource.get_root().get_nav_list():
35 35 <li ${'class=active' if item == resource else ''}>
36 36 <a href="${request.resource_path(item, route_name='auth_home')}">${item.display_name}</a>
37 37 </li>
38 38 % endfor
39 39 </ul>
40 40 </div>
41 41
42 42 <div class="main-content-full-width">
43 43 <div class="panel panel-default">
44 44 <div class="panel-heading">
45 45 <h3 class="panel-title">${_('Plugin')}: ${resource.display_name}</h3>
46 46 </div>
47 47 <div class="panel-body">
48 48 <div class="plugin_form">
49 49 <div class="fields">
50 50 ${h.secure_form(request.resource_path(resource, route_name='auth_home'))}
51 51 <div class="form">
52 52
53 53 %for node in plugin.get_settings_schema():
54 54 <% label_css_class = ("label-checkbox" if (node.widget == "bool") else "") %>
55 55 <div class="field">
56 56 <div class="label ${label_css_class}"><label for="${node.name}">${node.title}</label></div>
57 57 <div class="input">
58 58 %if node.widget in ["string", "int", "unicode"]:
59 59 ${h.text(node.name, defaults.get(node.name), class_="medium")}
60 60 %elif node.widget == "password":
61 61 ${h.password(node.name, defaults.get(node.name), class_="medium")}
62 62 %elif node.widget == "bool":
63 63 <div class="checkbox">${h.checkbox(node.name, True, checked=defaults.get(node.name))}</div>
64 64 %elif node.widget == "select":
65 65 ${h.select(node.name, defaults.get(node.name), node.validator.choices)}
66 66 %elif node.widget == "readonly":
67 67 ${node.default}
68 68 %else:
69 69 This field is of type ${node.typ}, which cannot be displayed. Must be one of [string|int|bool|select].
70 70 %endif
71 71 %if node.name in errors:
72 72 <span class="error-message">${errors.get(node.name)}</span>
73 73 <br />
74 74 %endif
75 <p class="help-block">${node.description}</p>
75 <p class="help-block pre-formatting">${node.description}</p>
76 76 </div>
77 77 </div>
78 78 %endfor
79 79
80 80 ## Allow derived templates to add something below the form
81 81 ## input fields
82 82 %if hasattr(next, 'below_form_fields'):
83 83 ${next.below_form_fields()}
84 84 %endif
85 85
86 86 <div class="buttons">
87 87 ${h.submit('save',_('Save'),class_="btn")}
88 88 </div>
89 89
90 90 </div>
91 91 ${h.end_form()}
92 92 </div>
93 93 </div>
94 94 </div>
95 95 </div>
96 96 </div>
97 97
98 98 </div>
99 99 </div>
100 100
101 101 ## TODO: Ugly hack to get ldap select elements to work.
102 102 ## Find a solution to integrate this nicely.
103 103 <script>
104 104 $(document).ready(function() {
105 105 var select2Options = {
106 106 containerCssClass: 'drop-menu',
107 107 dropdownCssClass: 'drop-menu-dropdown',
108 108 dropdownAutoWidth: true,
109 109 minimumResultsForSearch: -1
110 110 };
111 111 $("#tls_kind").select2(select2Options);
112 112 $("#tls_reqcert").select2(select2Options);
113 113 $("#search_scope").select2(select2Options);
114 114 $("#group_extraction_type").select2(select2Options);
115 115 $("#admin_groups_sync").select2(select2Options);
116 116 });
117 117 </script>
118 118 </%def>
@@ -1,76 +1,74 b''
1 1 <%namespace name="vcss" file="/base/vcs_settings.mako"/>
2 2
3 3 <div id="repo_vcs_settings" class="${'inherited' if c.inherit_global_settings else ''}">
4 4 ${h.secure_form(url('repo_vcs_settings', repo_name=c.repo_info.repo_name), method='post')}
5 5 <div class="form panel panel-default">
6 6 <div class="fields panel-body">
7 7 <div class="field">
8 8 <div class="label label-checkbox">
9 9 <label for="inherit_global_settings">${_('Inherit from global settings')}:</label>
10 10 </div>
11 11 <div class="checkboxes">
12 12 ${h.checkbox('inherit_global_settings',value=True)}
13 <span class="help-block">
14 ${h.literal(_('Select to inherit global vcs settings.'))}
15 </span>
13 <span class="help-block">${h.literal(_('Select to inherit global vcs settings.'))}</span>
16 14 </div>
17 15 </div>
18 16 </div>
19 17 </div>
20 18
21 19 <div id="inherit_overlay_vcs_default">
22 20 <div>
23 21 ${vcss.vcs_settings_fields(
24 22 suffix='_inherited',
25 23 svn_tag_patterns=c.global_svn_tag_patterns,
26 24 svn_branch_patterns=c.global_svn_branch_patterns,
27 25 repo_type=c.repo_info.repo_type,
28 26 disabled='disabled'
29 27 )}
30 28 </div>
31 29 </div>
32 30
33 31 <div id="inherit_overlay_vcs_custom">
34 32 <div>
35 33 ${vcss.vcs_settings_fields(
36 34 suffix='',
37 35 svn_tag_patterns=c.svn_tag_patterns,
38 36 svn_branch_patterns=c.svn_branch_patterns,
39 37 repo_type=c.repo_info.repo_type
40 38 )}
41 39 </div>
42 40 </div>
43 41
44 42 <div class="buttons">
45 43 ${h.submit('save',_('Save settings'),class_="btn")}
46 44 ${h.reset('reset',_('Reset'),class_="btn")}
47 45 </div>
48 46
49 47 ${h.end_form()}
50 48 </div>
51 49
52 50 <script type="text/javascript">
53 51
54 52 function ajaxDeletePattern(pattern_id, field_id) {
55 53 var sUrl = "${h.url('repo_vcs_settings', repo_name=c.repo_info.repo_name)}";
56 54 var callback = function (o) {
57 55 var elem = $("#"+field_id);
58 56 elem.remove();
59 57 };
60 58 var postData = {
61 59 '_method': 'delete',
62 60 'delete_svn_pattern': pattern_id,
63 61 'csrf_token': CSRF_TOKEN
64 62 };
65 63 var request = $.post(sUrl, postData)
66 64 .done(callback)
67 65 .fail(function (data, textStatus, errorThrown) {
68 66 alert("Error while deleting hooks.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
69 67 });
70 68 }
71 69
72 70 $('#inherit_global_settings').on('change', function(e){
73 71 $('#repo_vcs_settings').toggleClass('inherited', this.checked);
74 72 });
75 73
76 74 </script>
@@ -1,88 +1,87 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="base/root.mako"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Sign In')}
6 6 %if c.rhodecode_name:
7 7 &middot; ${h.branding(c.rhodecode_name)}
8 8 %endif
9 9 </%def>
10 10
11 11 <style>body{background-color:#eeeeee;}</style>
12 12 <div class="loginbox">
13 13 <div class="header">
14 14 <div id="header-inner" class="title">
15 15 <div id="logo">
16 16 <div class="logo-wrapper">
17 17 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
18 18 </div>
19 19 %if c.rhodecode_name:
20 20 <div class="branding"> ${h.branding(c.rhodecode_name)}</div>
21 21 %endif
22 22 </div>
23 23 </div>
24 24 </div>
25 25
26 26 <div class="loginwrapper">
27 27 <div class="left-column">
28 28 <img class="sign-in-image" src="${h.asset('images/sign-in.png')}" alt="RhodeCode"/>
29 29 </div>
30 30 <%block name="above_login_button" />
31 31 <div id="login" class="right-column">
32 32 <!-- login -->
33 33 <div class="sign-in-title">
34 34 <h1>${_('Sign In')}</h1>
35 35 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
36 36 <h4>${h.link_to(_("Go to the registration page to create a new account."), request.route_path('register'))}</h4>
37 37 %endif
38 38 </div>
39 39 <div class="inner form">
40 40 ${h.form(request.route_path('login', _query={'came_from': came_from}), needs_csrf_token=False)}
41 41
42 42 <label for="username">${_('Username')}:</label>
43 43 ${h.text('username', class_='focus', value=defaults.get('username'))}
44 44 %if 'username' in errors:
45 45 <span class="error-message">${errors.get('username')}</span>
46 46 <br />
47 47 %endif
48 48
49 49 <label for="password">${_('Password')}:</label>
50 50 ${h.password('password', class_='focus')}
51 51 %if 'password' in errors:
52 52 <span class="error-message">${errors.get('password')}</span>
53 53 <br />
54 54 %endif
55 55
56 56 ${h.checkbox('remember', value=True, checked=defaults.get('remember'))}
57 57 <label class="checkbox" for="remember">${_('Remember me')}</label>
58 58
59 59 %if h.HasPermissionAny('hg.password_reset.enabled')():
60 60 <p class="links">
61 61 ${h.link_to(_('Forgot your password?'), h.route_path('reset_password'), class_='pwd_reset')}
62 62 </p>
63 63 %elif h.HasPermissionAny('hg.password_reset.hidden')():
64 64 <p class="help-block">
65 65 ${_('Password reset is disabled. Please contact ')}
66 66 % if c.visual.rhodecode_support_url:
67 67 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
68 68 ${_('or')}
69 69 % endif
70 70 ${_('an administrator if you need help.')}
71 71 </p>
72 %endif
73
72 %endif
74 73
75 74 ${h.submit('sign_in', _('Sign In'), class_="btn sign-in")}
76 75
77 76 ${h.end_form()}
78 77 <script type="text/javascript">
79 78 $(document).ready(function(){
80 79 $('#username').focus();
81 80 })
82 81 </script>
83 82 </div>
84 83 <!-- end login -->
85 84 <%block name="below_login_button" />
86 85 </div>
87 86 </div>
88 87 </div>
@@ -1,593 +1,591 b''
1 1 <%inherit file="/base/base.mako"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('New pull request')}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${_('New pull request')}
9 9 </%def>
10 10
11 11 <%def name="menu_bar_nav()">
12 12 ${self.menu_items(active='repositories')}
13 13 </%def>
14 14
15 15 <%def name="menu_bar_subnav()">
16 16 ${self.repo_menu(active='showpullrequest')}
17 17 </%def>
18 18
19 19 <%def name="main()">
20 20 <div class="box">
21 21 <div class="title">
22 22 ${self.repo_page_title(c.rhodecode_db_repo)}
23 23 ${self.breadcrumbs()}
24 24 </div>
25 25
26 26 ${h.secure_form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
27 27 <div class="box pr-summary">
28 28
29 29 <div class="summary-details block-left">
30 30
31 31 <div class="form">
32 32 <!-- fields -->
33 33
34 34 <div class="fields" >
35 35
36 36 <div class="field">
37 37 <div class="label">
38 38 <label for="pullrequest_title">${_('Title')}:</label>
39 39 </div>
40 40 <div class="input">
41 41 ${h.text('pullrequest_title', c.default_title, class_="medium autogenerated-title")}
42 42 </div>
43 43 </div>
44 44
45 45 <div class="field">
46 46 <div class="label label-textarea">
47 47 <label for="pullrequest_desc">${_('Description')}:</label>
48 48 </div>
49 49 <div class="textarea text-area editor">
50 50 ${h.textarea('pullrequest_desc',size=30, )}
51 <span class="help-block">
52 ${_('Write a short description on this pull request')}
53 </span>
51 <span class="help-block">${_('Write a short description on this pull request')}</span>
54 52 </div>
55 53 </div>
56 54
57 55 <div class="field">
58 56 <div class="label label-textarea">
59 57 <label for="pullrequest_desc">${_('Commit flow')}:</label>
60 58 </div>
61 59
62 60 ## TODO: johbo: Abusing the "content" class here to get the
63 61 ## desired effect. Should be replaced by a proper solution.
64 62
65 63 ##ORG
66 64 <div class="content">
67 65 <strong>${_('Origin repository')}:</strong>
68 66 ${c.rhodecode_db_repo.description}
69 67 </div>
70 68 <div class="content">
71 69 ${h.hidden('source_repo')}
72 70 ${h.hidden('source_ref')}
73 71 </div>
74 72
75 73 ##OTHER, most Probably the PARENT OF THIS FORK
76 74 <div class="content">
77 75 ## filled with JS
78 76 <div id="target_repo_desc"></div>
79 77 </div>
80 78
81 79 <div class="content">
82 80 ${h.hidden('target_repo')}
83 81 ${h.hidden('target_ref')}
84 82 <span id="target_ref_loading" style="display: none">
85 83 ${_('Loading refs...')}
86 84 </span>
87 85 </div>
88 86 </div>
89 87
90 88 <div class="field">
91 89 <div class="label label-textarea">
92 90 <label for="pullrequest_submit"></label>
93 91 </div>
94 92 <div class="input">
95 93 <div class="pr-submit-button">
96 94 ${h.submit('save',_('Submit Pull Request'),class_="btn")}
97 95 </div>
98 96 <div id="pr_open_message"></div>
99 97 </div>
100 98 </div>
101 99
102 100 <div class="pr-spacing-container"></div>
103 101 </div>
104 102 </div>
105 103 </div>
106 104 <div>
107 105 <div class="reviewers-title block-right">
108 106 <div class="pr-details-title">
109 107 ${_('Pull request reviewers')}
110 108 <span class="calculate-reviewers"> - ${_('loading...')}</span>
111 109 </div>
112 110 </div>
113 111 <div id="reviewers" class="block-right pr-details-content reviewers">
114 112 ## members goes here, filled via JS based on initial selection !
115 113 <input type="hidden" name="__start__" value="review_members:sequence">
116 114 <ul id="review_members" class="group_members"></ul>
117 115 <input type="hidden" name="__end__" value="review_members:sequence">
118 116 <div id="add_reviewer_input" class='ac'>
119 117 <div class="reviewer_ac">
120 118 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer'))}
121 119 <div id="reviewers_container"></div>
122 120 </div>
123 121 </div>
124 122 </div>
125 123 </div>
126 124 </div>
127 125 <div class="box">
128 126 <div>
129 127 ## overview pulled by ajax
130 128 <div id="pull_request_overview"></div>
131 129 </div>
132 130 </div>
133 131 ${h.end_form()}
134 132 </div>
135 133
136 134 <script type="text/javascript">
137 135 $(function(){
138 136 var defaultSourceRepo = '${c.default_repo_data['source_repo_name']}';
139 137 var defaultSourceRepoData = ${c.default_repo_data['source_refs_json']|n};
140 138 var defaultTargetRepo = '${c.default_repo_data['target_repo_name']}';
141 139 var defaultTargetRepoData = ${c.default_repo_data['target_refs_json']|n};
142 140 var targetRepoName = '${c.repo_name}';
143 141
144 142 var $pullRequestForm = $('#pull_request_form');
145 143 var $sourceRepo = $('#source_repo', $pullRequestForm);
146 144 var $targetRepo = $('#target_repo', $pullRequestForm);
147 145 var $sourceRef = $('#source_ref', $pullRequestForm);
148 146 var $targetRef = $('#target_ref', $pullRequestForm);
149 147
150 148 var calculateContainerWidth = function() {
151 149 var maxWidth = 0;
152 150 var repoSelect2Containers = ['#source_repo', '#target_repo'];
153 151 $.each(repoSelect2Containers, function(idx, value) {
154 152 $(value).select2('container').width('auto');
155 153 var curWidth = $(value).select2('container').width();
156 154 if (maxWidth <= curWidth) {
157 155 maxWidth = curWidth;
158 156 }
159 157 $.each(repoSelect2Containers, function(idx, value) {
160 158 $(value).select2('container').width(maxWidth + 10);
161 159 });
162 160 });
163 161 };
164 162
165 163 var initRefSelection = function(selectedRef) {
166 164 return function(element, callback) {
167 165 // translate our select2 id into a text, it's a mapping to show
168 166 // simple label when selecting by internal ID.
169 167 var id, refData;
170 168 if (selectedRef === undefined) {
171 169 id = element.val();
172 170 refData = element.val().split(':');
173 171 } else {
174 172 id = selectedRef;
175 173 refData = selectedRef.split(':');
176 174 }
177 175
178 176 var text = refData[1];
179 177 if (refData[0] === 'rev') {
180 178 text = text.substring(0, 12);
181 179 }
182 180
183 181 var data = {id: id, text: text};
184 182
185 183 callback(data);
186 184 };
187 185 };
188 186
189 187 var formatRefSelection = function(item) {
190 188 var prefix = '';
191 189 var refData = item.id.split(':');
192 190 if (refData[0] === 'branch') {
193 191 prefix = '<i class="icon-branch"></i>';
194 192 }
195 193 else if (refData[0] === 'book') {
196 194 prefix = '<i class="icon-bookmark"></i>';
197 195 }
198 196 else if (refData[0] === 'tag') {
199 197 prefix = '<i class="icon-tag"></i>';
200 198 }
201 199
202 200 var originalOption = item.element;
203 201 return prefix + item.text;
204 202 };
205 203
206 204 // custom code mirror
207 205 var codeMirrorInstance = initPullRequestsCodeMirror('#pullrequest_desc');
208 206
209 207 var queryTargetRepo = function(self, query) {
210 208 // cache ALL results if query is empty
211 209 var cacheKey = query.term || '__';
212 210 var cachedData = self.cachedDataSource[cacheKey];
213 211
214 212 if (cachedData) {
215 213 query.callback({results: cachedData.results});
216 214 } else {
217 215 $.ajax({
218 216 url: pyroutes.url('pullrequest_repo_destinations', {'repo_name': targetRepoName}),
219 217 data: {query: query.term},
220 218 dataType: 'json',
221 219 type: 'GET',
222 220 success: function(data) {
223 221 self.cachedDataSource[cacheKey] = data;
224 222 query.callback({results: data.results});
225 223 },
226 224 error: function(data, textStatus, errorThrown) {
227 225 alert(
228 226 "Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
229 227 }
230 228 });
231 229 }
232 230 };
233 231
234 232 var queryTargetRefs = function(initialData, query) {
235 233 var data = {results: []};
236 234 // filter initialData
237 235 $.each(initialData, function() {
238 236 var section = this.text;
239 237 var children = [];
240 238 $.each(this.children, function() {
241 239 if (query.term.length === 0 ||
242 240 this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ) {
243 241 children.push({'id': this.id, 'text': this.text})
244 242 }
245 243 });
246 244 data.results.push({'text': section, 'children': children})
247 245 });
248 246 query.callback({results: data.results});
249 247 };
250 248
251 249
252 250 var prButtonLockChecks = {
253 251 'compare': false,
254 252 'reviewers': false
255 253 };
256 254
257 255 var prButtonLock = function(lockEnabled, msg, scope) {
258 256 scope = scope || 'all';
259 257 if (scope == 'all'){
260 258 prButtonLockChecks['compare'] = !lockEnabled;
261 259 prButtonLockChecks['reviewers'] = !lockEnabled;
262 260 } else if (scope == 'compare') {
263 261 prButtonLockChecks['compare'] = !lockEnabled;
264 262 } else if (scope == 'reviewers'){
265 263 prButtonLockChecks['reviewers'] = !lockEnabled;
266 264 }
267 265 var checksMeet = prButtonLockChecks.compare && prButtonLockChecks.reviewers;
268 266 if (lockEnabled) {
269 267 $('#save').attr('disabled', 'disabled');
270 268 }
271 269 else if (checksMeet) {
272 270 $('#save').removeAttr('disabled');
273 271 }
274 272
275 273 if (msg) {
276 274 $('#pr_open_message').html(msg);
277 275 }
278 276 };
279 277
280 278 var loadRepoRefDiffPreview = function() {
281 279 var sourceRepo = $sourceRepo.eq(0).val();
282 280 var sourceRef = $sourceRef.eq(0).val().split(':');
283 281
284 282 var targetRepo = $targetRepo.eq(0).val();
285 283 var targetRef = $targetRef.eq(0).val().split(':');
286 284
287 285 var url_data = {
288 286 'repo_name': targetRepo,
289 287 'target_repo': sourceRepo,
290 288 'source_ref': targetRef[2],
291 289 'source_ref_type': 'rev',
292 290 'target_ref': sourceRef[2],
293 291 'target_ref_type': 'rev',
294 292 'merge': true,
295 293 '_': Date.now() // bypass browser caching
296 294 }; // gather the source/target ref and repo here
297 295
298 296 if (sourceRef.length !== 3 || targetRef.length !== 3) {
299 297 prButtonLock(true, "${_('Please select origin and destination')}");
300 298 return;
301 299 }
302 300 var url = pyroutes.url('compare_url', url_data);
303 301
304 302 // lock PR button, so we cannot send PR before it's calculated
305 303 prButtonLock(true, "${_('Loading compare ...')}", 'compare');
306 304
307 305 if (loadRepoRefDiffPreview._currentRequest) {
308 306 loadRepoRefDiffPreview._currentRequest.abort();
309 307 }
310 308
311 309 loadRepoRefDiffPreview._currentRequest = $.get(url)
312 310 .error(function(data, textStatus, errorThrown) {
313 311 alert(
314 312 "Error while processing request.\nError code {0} ({1}).".format(
315 313 data.status, data.statusText));
316 314 })
317 315 .done(function(data) {
318 316 loadRepoRefDiffPreview._currentRequest = null;
319 317 $('#pull_request_overview').html(data);
320 318 var commitElements = $(data).find('tr[commit_id]');
321 319
322 320 var prTitleAndDesc = getTitleAndDescription(sourceRef[1],
323 321 commitElements, 5);
324 322
325 323 var title = prTitleAndDesc[0];
326 324 var proposedDescription = prTitleAndDesc[1];
327 325
328 326 var useGeneratedTitle = (
329 327 $('#pullrequest_title').hasClass('autogenerated-title') ||
330 328 $('#pullrequest_title').val() === "");
331 329
332 330 if (title && useGeneratedTitle) {
333 331 // use generated title if we haven't specified our own
334 332 $('#pullrequest_title').val(title);
335 333 $('#pullrequest_title').addClass('autogenerated-title');
336 334
337 335 }
338 336
339 337 var useGeneratedDescription = (
340 338 !codeMirrorInstance._userDefinedDesc ||
341 339 codeMirrorInstance.getValue() === "");
342 340
343 341 if (proposedDescription && useGeneratedDescription) {
344 342 // set proposed content, if we haven't defined our own,
345 343 // or we don't have description written
346 344 codeMirrorInstance._userDefinedDesc = false; // reset state
347 345 codeMirrorInstance.setValue(proposedDescription);
348 346 }
349 347
350 348 var msg = '';
351 349 if (commitElements.length === 1) {
352 350 msg = "${ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 1)}";
353 351 } else {
354 352 msg = "${ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 2)}";
355 353 }
356 354
357 355 msg += ' <a id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
358 356
359 357 if (commitElements.length) {
360 358 var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length);
361 359 prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare');
362 360 }
363 361 else {
364 362 prButtonLock(true, "${_('There are no commits to merge.')}", 'compare');
365 363 }
366 364
367 365
368 366 });
369 367 };
370 368
371 369 /**
372 370 Generate Title and Description for a PullRequest.
373 371 In case of 1 commits, the title and description is that one commit
374 372 in case of multiple commits, we iterate on them with max N number of commits,
375 373 and build description in a form
376 374 - commitN
377 375 - commitN+1
378 376 ...
379 377
380 378 Title is then constructed from branch names, or other references,
381 379 replacing '-' and '_' into spaces
382 380
383 381 * @param sourceRef
384 382 * @param elements
385 383 * @param limit
386 384 * @returns {*[]}
387 385 */
388 386 var getTitleAndDescription = function(sourceRef, elements, limit) {
389 387 var title = '';
390 388 var desc = '';
391 389
392 390 $.each($(elements).get().reverse().slice(0, limit), function(idx, value) {
393 391 var rawMessage = $(value).find('td.td-description .message').data('messageRaw');
394 392 desc += '- ' + rawMessage.split('\n')[0].replace(/\n+$/, "") + '\n';
395 393 });
396 394 // only 1 commit, use commit message as title
397 395 if (elements.length == 1) {
398 396 title = $(elements[0]).find('td.td-description .message').data('messageRaw').split('\n')[0];
399 397 }
400 398 else {
401 399 // use reference name
402 400 title = sourceRef.replace(/-/g, ' ').replace(/_/g, ' ').capitalizeFirstLetter();
403 401 }
404 402
405 403 return [title, desc]
406 404 };
407 405
408 406 var Select2Box = function(element, overrides) {
409 407 var globalDefaults = {
410 408 dropdownAutoWidth: true,
411 409 containerCssClass: "drop-menu",
412 410 dropdownCssClass: "drop-menu-dropdown"
413 411 };
414 412
415 413 var initSelect2 = function(defaultOptions) {
416 414 var options = jQuery.extend(globalDefaults, defaultOptions, overrides);
417 415 element.select2(options);
418 416 };
419 417
420 418 return {
421 419 initRef: function() {
422 420 var defaultOptions = {
423 421 minimumResultsForSearch: 5,
424 422 formatSelection: formatRefSelection
425 423 };
426 424
427 425 initSelect2(defaultOptions);
428 426 },
429 427
430 428 initRepo: function(defaultValue, readOnly) {
431 429 var defaultOptions = {
432 430 initSelection : function (element, callback) {
433 431 var data = {id: defaultValue, text: defaultValue};
434 432 callback(data);
435 433 }
436 434 };
437 435
438 436 initSelect2(defaultOptions);
439 437
440 438 element.select2('val', defaultSourceRepo);
441 439 if (readOnly === true) {
442 440 element.select2('readonly', true);
443 441 }
444 442 }
445 443 };
446 444 };
447 445
448 446 var initTargetRefs = function(refsData, selectedRef){
449 447 Select2Box($targetRef, {
450 448 query: function(query) {
451 449 queryTargetRefs(refsData, query);
452 450 },
453 451 initSelection : initRefSelection(selectedRef)
454 452 }).initRef();
455 453
456 454 if (!(selectedRef === undefined)) {
457 455 $targetRef.select2('val', selectedRef);
458 456 }
459 457 };
460 458
461 459 var targetRepoChanged = function(repoData) {
462 460 // generate new DESC of target repo displayed next to select
463 461 $('#target_repo_desc').html(
464 462 "<strong>${_('Destination repository')}</strong>: {0}".format(repoData['description'])
465 463 );
466 464
467 465 // generate dynamic select2 for refs.
468 466 initTargetRefs(repoData['refs']['select2_refs'],
469 467 repoData['refs']['selected_ref']);
470 468
471 469 };
472 470
473 471 var sourceRefSelect2 = Select2Box(
474 472 $sourceRef, {
475 473 placeholder: "${_('Select commit reference')}",
476 474 query: function(query) {
477 475 var initialData = defaultSourceRepoData['refs']['select2_refs'];
478 476 queryTargetRefs(initialData, query)
479 477 },
480 478 initSelection: initRefSelection()
481 479 }
482 480 );
483 481
484 482 var sourceRepoSelect2 = Select2Box($sourceRepo, {
485 483 query: function(query) {}
486 484 });
487 485
488 486 var targetRepoSelect2 = Select2Box($targetRepo, {
489 487 cachedDataSource: {},
490 488 query: $.debounce(250, function(query) {
491 489 queryTargetRepo(this, query);
492 490 }),
493 491 formatResult: formatResult
494 492 });
495 493
496 494 sourceRefSelect2.initRef();
497 495
498 496 sourceRepoSelect2.initRepo(defaultSourceRepo, true);
499 497
500 498 targetRepoSelect2.initRepo(defaultTargetRepo, false);
501 499
502 500 $sourceRef.on('change', function(e){
503 501 loadRepoRefDiffPreview();
504 502 loadDefaultReviewers();
505 503 });
506 504
507 505 $targetRef.on('change', function(e){
508 506 loadRepoRefDiffPreview();
509 507 loadDefaultReviewers();
510 508 });
511 509
512 510 $targetRepo.on('change', function(e){
513 511 var repoName = $(this).val();
514 512 calculateContainerWidth();
515 513 $targetRef.select2('destroy');
516 514 $('#target_ref_loading').show();
517 515
518 516 $.ajax({
519 517 url: pyroutes.url('pullrequest_repo_refs',
520 518 {'repo_name': targetRepoName, 'target_repo_name':repoName}),
521 519 data: {},
522 520 dataType: 'json',
523 521 type: 'GET',
524 522 success: function(data) {
525 523 $('#target_ref_loading').hide();
526 524 targetRepoChanged(data);
527 525 loadRepoRefDiffPreview();
528 526 },
529 527 error: function(data, textStatus, errorThrown) {
530 528 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
531 529 }
532 530 })
533 531
534 532 });
535 533
536 534 var loadDefaultReviewers = function() {
537 535 if (loadDefaultReviewers._currentRequest) {
538 536 loadDefaultReviewers._currentRequest.abort();
539 537 }
540 538 $('.calculate-reviewers').show();
541 539 prButtonLock(true, null, 'reviewers');
542 540
543 541 var url = pyroutes.url('repo_default_reviewers_data', {'repo_name': targetRepoName});
544 542
545 543 var sourceRepo = $sourceRepo.eq(0).val();
546 544 var sourceRef = $sourceRef.eq(0).val().split(':');
547 545 var targetRepo = $targetRepo.eq(0).val();
548 546 var targetRef = $targetRef.eq(0).val().split(':');
549 547 url += '?source_repo=' + sourceRepo;
550 548 url += '&source_ref=' + sourceRef[2];
551 549 url += '&target_repo=' + targetRepo;
552 550 url += '&target_ref=' + targetRef[2];
553 551
554 552 loadDefaultReviewers._currentRequest = $.get(url)
555 553 .done(function(data) {
556 554 loadDefaultReviewers._currentRequest = null;
557 555
558 556 // reset && add the reviewer based on selected repo
559 557 $('#review_members').html('');
560 558 for (var i = 0; i < data.reviewers.length; i++) {
561 559 var reviewer = data.reviewers[i];
562 560 addReviewMember(
563 561 reviewer.user_id, reviewer.firstname,
564 562 reviewer.lastname, reviewer.username,
565 563 reviewer.gravatar_link, reviewer.reasons);
566 564 }
567 565 $('.calculate-reviewers').hide();
568 566 prButtonLock(false, null, 'reviewers');
569 567 });
570 568 };
571 569
572 570 prButtonLock(true, "${_('Please select origin and destination')}", 'all');
573 571
574 572 // auto-load on init, the target refs select2
575 573 calculateContainerWidth();
576 574 targetRepoChanged(defaultTargetRepoData);
577 575
578 576 $('#pullrequest_title').on('keyup', function(e){
579 577 $(this).removeClass('autogenerated-title');
580 578 });
581 579
582 580 % if c.default_source_ref:
583 581 // in case we have a pre-selected value, use it now
584 582 $sourceRef.select2('val', '${c.default_source_ref}');
585 583 loadRepoRefDiffPreview();
586 584 loadDefaultReviewers();
587 585 % endif
588 586
589 587 ReviewerAutoComplete('user');
590 588 });
591 589 </script>
592 590
593 591 </%def>
General Comments 0
You need to be logged in to leave comments. Login now