##// END OF EJS Templates
drafts: draft finize boilerplate
milka -
r4549:883fcc39 default
parent child Browse files
Show More
@@ -1,746 +1,750 b''
1 // comments.less
1 // comments.less
2 // For use in RhodeCode applications;
2 // For use in RhodeCode applications;
3 // see style guide documentation for guidelines.
3 // see style guide documentation for guidelines.
4
4
5
5
6 // Comments
6 // Comments
7 @comment-outdated-opacity: 1.0;
7 @comment-outdated-opacity: 1.0;
8
8
9 .comments {
9 .comments {
10 width: 100%;
10 width: 100%;
11 }
11 }
12
12
13 .comments-heading {
13 .comments-heading {
14 margin-bottom: -1px;
14 margin-bottom: -1px;
15 background: @grey6;
15 background: @grey6;
16 display: block;
16 display: block;
17 padding: 10px 0px;
17 padding: 10px 0px;
18 font-size: 18px
18 font-size: 18px
19 }
19 }
20
20
21 #comment-tr-show {
21 #comment-tr-show {
22 padding: 5px 0;
22 padding: 5px 0;
23 }
23 }
24
24
25 tr.inline-comments div {
25 tr.inline-comments div {
26 max-width: 100%;
26 max-width: 100%;
27
27
28 p {
28 p {
29 white-space: normal;
29 white-space: normal;
30 }
30 }
31
31
32 code, pre, .code, dd {
32 code, pre, .code, dd {
33 overflow-x: auto;
33 overflow-x: auto;
34 width: 1062px;
34 width: 1062px;
35 }
35 }
36
36
37 dd {
37 dd {
38 width: auto;
38 width: auto;
39 }
39 }
40 }
40 }
41
41
42 #injected_page_comments {
42 #injected_page_comments {
43 .comment-previous-link,
43 .comment-previous-link,
44 .comment-next-link,
44 .comment-next-link,
45 .comment-links-divider {
45 .comment-links-divider {
46 display: none;
46 display: none;
47 }
47 }
48 }
48 }
49
49
50 .add-comment {
50 .add-comment {
51 margin-bottom: 10px;
51 margin-bottom: 10px;
52 }
52 }
53 .hide-comment-button .add-comment {
53 .hide-comment-button .add-comment {
54 display: none;
54 display: none;
55 }
55 }
56
56
57 .comment-bubble {
57 .comment-bubble {
58 color: @grey4;
58 color: @grey4;
59 margin-top: 4px;
59 margin-top: 4px;
60 margin-right: 30px;
60 margin-right: 30px;
61 visibility: hidden;
61 visibility: hidden;
62 }
62 }
63
63
64 .comment-draft {
64 .comment-draft {
65 float: left;
65 float: left;
66 margin-right: 10px;
66 margin-right: 10px;
67 font-weight: 400;
67 font-weight: 400;
68 color: @color-draft;
68 color: @color-draft;
69 }
69 }
70
70
71 .comment-new {
71 .comment-new {
72 float: left;
72 float: left;
73 margin-right: 10px;
73 margin-right: 10px;
74 font-weight: 400;
74 font-weight: 400;
75 color: @color-new;
75 color: @color-new;
76 }
76 }
77
77
78 .comment-label {
78 .comment-label {
79 float: left;
79 float: left;
80
80
81 padding: 0 8px 0 0;
81 padding: 0 8px 0 0;
82 min-height: 0;
82 min-height: 0;
83
83
84 text-align: center;
84 text-align: center;
85 font-size: 10px;
85 font-size: 10px;
86
86
87 font-family: @text-italic;
87 font-family: @text-italic;
88 font-style: italic;
88 font-style: italic;
89 background: #fff none;
89 background: #fff none;
90 color: @grey3;
90 color: @grey3;
91 white-space: nowrap;
91 white-space: nowrap;
92
92
93 text-transform: uppercase;
93 text-transform: uppercase;
94 min-width: 50px;
94 min-width: 50px;
95
95
96 &.todo {
96 &.todo {
97 color: @color5;
97 color: @color5;
98 font-style: italic;
98 font-style: italic;
99 font-weight: @text-bold-italic-weight;
99 font-weight: @text-bold-italic-weight;
100 font-family: @text-bold-italic;
100 font-family: @text-bold-italic;
101 }
101 }
102
102
103 .resolve {
103 .resolve {
104 cursor: pointer;
104 cursor: pointer;
105 text-decoration: underline;
105 text-decoration: underline;
106 }
106 }
107
107
108 .resolved {
108 .resolved {
109 text-decoration: line-through;
109 text-decoration: line-through;
110 color: @color1;
110 color: @color1;
111 }
111 }
112 .resolved a {
112 .resolved a {
113 text-decoration: line-through;
113 text-decoration: line-through;
114 color: @color1;
114 color: @color1;
115 }
115 }
116 .resolve-text {
116 .resolve-text {
117 color: @color1;
117 color: @color1;
118 margin: 2px 8px;
118 margin: 2px 8px;
119 font-family: @text-italic;
119 font-family: @text-italic;
120 font-style: italic;
120 font-style: italic;
121 }
121 }
122 }
122 }
123
123
124 .has-spacer-after {
124 .has-spacer-after {
125 &:after {
125 &:after {
126 content: ' | ';
126 content: ' | ';
127 color: @grey5;
127 color: @grey5;
128 }
128 }
129 }
129 }
130
130
131 .has-spacer-before {
131 .has-spacer-before {
132 &:before {
132 &:before {
133 content: ' | ';
133 content: ' | ';
134 color: @grey5;
134 color: @grey5;
135 }
135 }
136 }
136 }
137
137
138 .comment {
138 .comment {
139
139
140 &.comment-general {
140 &.comment-general {
141 border: 1px solid @grey5;
141 border: 1px solid @grey5;
142 padding: 5px 5px 5px 5px;
142 padding: 5px 5px 5px 5px;
143 }
143 }
144
144
145 margin: @padding 0;
145 margin: @padding 0;
146 padding: 4px 0 0 0;
146 padding: 4px 0 0 0;
147 line-height: 1em;
147 line-height: 1em;
148
148
149 .rc-user {
149 .rc-user {
150 min-width: 0;
150 min-width: 0;
151 margin: 0px .5em 0 0;
151 margin: 0px .5em 0 0;
152
152
153 .user {
153 .user {
154 display: inline;
154 display: inline;
155 }
155 }
156 }
156 }
157
157
158 .meta {
158 .meta {
159 position: relative;
159 position: relative;
160 width: 100%;
160 width: 100%;
161 border-bottom: 1px solid @grey5;
161 border-bottom: 1px solid @grey5;
162 margin: -5px 0px;
162 margin: -5px 0px;
163 line-height: 24px;
163 line-height: 24px;
164
164
165 &:hover .permalink {
165 &:hover .permalink {
166 visibility: visible;
166 visibility: visible;
167 color: @rcblue;
167 color: @rcblue;
168 }
168 }
169 }
169 }
170
170
171 .author,
171 .author,
172 .date {
172 .date {
173 display: inline;
173 display: inline;
174
174
175 &:after {
175 &:after {
176 content: ' | ';
176 content: ' | ';
177 color: @grey5;
177 color: @grey5;
178 }
178 }
179 }
179 }
180
180
181 .author-general img {
181 .author-general img {
182 top: 3px;
182 top: 3px;
183 }
183 }
184 .author-inline img {
184 .author-inline img {
185 top: 3px;
185 top: 3px;
186 }
186 }
187
187
188 .status-change,
188 .status-change,
189 .permalink,
189 .permalink,
190 .changeset-status-lbl {
190 .changeset-status-lbl {
191 display: inline;
191 display: inline;
192 }
192 }
193
193
194 .permalink {
194 .permalink {
195 visibility: hidden;
195 visibility: hidden;
196 }
196 }
197
197
198 .comment-links-divider {
198 .comment-links-divider {
199 display: inline;
199 display: inline;
200 }
200 }
201
201
202 .comment-links-block {
202 .comment-links-block {
203 float:right;
203 float:right;
204 text-align: right;
204 text-align: right;
205 min-width: 85px;
205 min-width: 85px;
206
206
207 [class^="icon-"]:before,
207 [class^="icon-"]:before,
208 [class*=" icon-"]:before {
208 [class*=" icon-"]:before {
209 margin-left: 0;
209 margin-left: 0;
210 margin-right: 0;
210 margin-right: 0;
211 }
211 }
212 }
212 }
213
213
214 .comment-previous-link {
214 .comment-previous-link {
215 display: inline-block;
215 display: inline-block;
216
216
217 .arrow_comment_link{
217 .arrow_comment_link{
218 cursor: pointer;
218 cursor: pointer;
219 i {
219 i {
220 font-size:10px;
220 font-size:10px;
221 }
221 }
222 }
222 }
223 .arrow_comment_link.disabled {
223 .arrow_comment_link.disabled {
224 cursor: default;
224 cursor: default;
225 color: @grey5;
225 color: @grey5;
226 }
226 }
227 }
227 }
228
228
229 .comment-next-link {
229 .comment-next-link {
230 display: inline-block;
230 display: inline-block;
231
231
232 .arrow_comment_link{
232 .arrow_comment_link{
233 cursor: pointer;
233 cursor: pointer;
234 i {
234 i {
235 font-size:10px;
235 font-size:10px;
236 }
236 }
237 }
237 }
238 .arrow_comment_link.disabled {
238 .arrow_comment_link.disabled {
239 cursor: default;
239 cursor: default;
240 color: @grey5;
240 color: @grey5;
241 }
241 }
242 }
242 }
243
243
244 .delete-comment {
244 .delete-comment {
245 display: inline-block;
245 display: inline-block;
246 color: @rcblue;
246 color: @rcblue;
247
247
248 &:hover {
248 &:hover {
249 cursor: pointer;
249 cursor: pointer;
250 }
250 }
251 }
251 }
252
252
253 .text {
253 .text {
254 clear: both;
254 clear: both;
255 .border-radius(@border-radius);
255 .border-radius(@border-radius);
256 .box-sizing(border-box);
256 .box-sizing(border-box);
257
257
258 .markdown-block p,
258 .markdown-block p,
259 .rst-block p {
259 .rst-block p {
260 margin: .5em 0 !important;
260 margin: .5em 0 !important;
261 // TODO: lisa: This is needed because of other rst !important rules :[
261 // TODO: lisa: This is needed because of other rst !important rules :[
262 }
262 }
263 }
263 }
264
264
265 .pr-version {
265 .pr-version {
266 display: inline-block;
266 display: inline-block;
267 }
267 }
268 .pr-version-inline {
268 .pr-version-inline {
269 display: inline-block;
269 display: inline-block;
270 }
270 }
271 .pr-version-num {
271 .pr-version-num {
272 font-size: 10px;
272 font-size: 10px;
273 }
273 }
274 }
274 }
275
275
276 @comment-padding: 5px;
276 @comment-padding: 5px;
277
277
278 .general-comments {
278 .general-comments {
279 .comment-outdated {
279 .comment-outdated {
280 opacity: @comment-outdated-opacity;
280 opacity: @comment-outdated-opacity;
281 }
281 }
282
282
283 .comment-outdated-label {
283 .comment-outdated-label {
284 color: @grey3;
284 color: @grey3;
285 padding-right: 4px;
285 padding-right: 4px;
286 }
286 }
287 }
287 }
288
288
289 .inline-comments {
289 .inline-comments {
290
290
291 .comment {
291 .comment {
292 margin: 0;
292 margin: 0;
293 }
293 }
294
294
295 .comment-outdated {
295 .comment-outdated {
296 opacity: @comment-outdated-opacity;
296 opacity: @comment-outdated-opacity;
297 }
297 }
298
298
299 .comment-outdated-label {
299 .comment-outdated-label {
300 color: @grey3;
300 color: @grey3;
301 padding-right: 4px;
301 padding-right: 4px;
302 }
302 }
303
303
304 .comment-inline {
304 .comment-inline {
305
305
306 &:first-child {
306 &:first-child {
307 margin: 4px 4px 0 4px;
307 margin: 4px 4px 0 4px;
308 border-top: 1px solid @grey5;
308 border-top: 1px solid @grey5;
309 border-bottom: 0 solid @grey5;
309 border-bottom: 0 solid @grey5;
310 border-left: 1px solid @grey5;
310 border-left: 1px solid @grey5;
311 border-right: 1px solid @grey5;
311 border-right: 1px solid @grey5;
312 .border-radius-top(4px);
312 .border-radius-top(4px);
313 }
313 }
314
314
315 &:only-child {
315 &:only-child {
316 margin: 4px 4px 0 4px;
316 margin: 4px 4px 0 4px;
317 border-top: 1px solid @grey5;
317 border-top: 1px solid @grey5;
318 border-bottom: 0 solid @grey5;
318 border-bottom: 0 solid @grey5;
319 border-left: 1px solid @grey5;
319 border-left: 1px solid @grey5;
320 border-right: 1px solid @grey5;
320 border-right: 1px solid @grey5;
321 .border-radius-top(4px);
321 .border-radius-top(4px);
322 }
322 }
323
323
324 background: white;
324 background: white;
325 padding: @comment-padding @comment-padding;
325 padding: @comment-padding @comment-padding;
326 margin: 0 4px 0 4px;
326 margin: 0 4px 0 4px;
327 border-top: 0 solid @grey5;
327 border-top: 0 solid @grey5;
328 border-bottom: 0 solid @grey5;
328 border-bottom: 0 solid @grey5;
329 border-left: 1px solid @grey5;
329 border-left: 1px solid @grey5;
330 border-right: 1px solid @grey5;
330 border-right: 1px solid @grey5;
331
331
332 .text {
332 .text {
333 border: none;
333 border: none;
334 }
334 }
335
335
336 .meta {
336 .meta {
337 border-bottom: 1px solid @grey6;
337 border-bottom: 1px solid @grey6;
338 margin: -5px 0px;
338 margin: -5px 0px;
339 line-height: 24px;
339 line-height: 24px;
340 }
340 }
341
341
342 }
342 }
343 .comment-selected {
343 .comment-selected {
344 border-left: 6px solid @comment-highlight-color;
344 border-left: 6px solid @comment-highlight-color;
345 }
345 }
346
346
347 .comment-inline-form-open {
347 .comment-inline-form-open {
348 display: block !important;
348 display: block !important;
349 }
349 }
350
350
351 .comment-inline-form {
351 .comment-inline-form {
352 display: none;
352 display: none;
353 }
353 }
354
354
355 .comment-inline-form-edit {
355 .comment-inline-form-edit {
356 padding: 0;
356 padding: 0;
357 margin: 0px 4px 2px 4px;
357 margin: 0px 4px 2px 4px;
358 }
358 }
359
359
360 .reply-thread-container {
360 .reply-thread-container {
361 display: table;
361 display: table;
362 width: 100%;
362 width: 100%;
363 padding: 0px 4px 4px 4px;
363 padding: 0px 4px 4px 4px;
364 }
364 }
365
365
366 .reply-thread-container-wrapper {
366 .reply-thread-container-wrapper {
367 margin: 0 4px 4px 4px;
367 margin: 0 4px 4px 4px;
368 border-top: 0 solid @grey5;
368 border-top: 0 solid @grey5;
369 border-bottom: 1px solid @grey5;
369 border-bottom: 1px solid @grey5;
370 border-left: 1px solid @grey5;
370 border-left: 1px solid @grey5;
371 border-right: 1px solid @grey5;
371 border-right: 1px solid @grey5;
372 .border-radius-bottom(4px);
372 .border-radius-bottom(4px);
373 }
373 }
374
374
375 .reply-thread-gravatar {
375 .reply-thread-gravatar {
376 display: table-cell;
376 display: table-cell;
377 width: 24px;
377 width: 24px;
378 height: 24px;
378 height: 24px;
379 padding-top: 10px;
379 padding-top: 10px;
380 padding-left: 10px;
380 padding-left: 10px;
381 background-color: #eeeeee;
381 background-color: #eeeeee;
382 vertical-align: top;
382 vertical-align: top;
383 }
383 }
384
384
385 .reply-thread-reply-button {
385 .reply-thread-reply-button {
386 display: table-cell;
386 display: table-cell;
387 width: 100%;
387 width: 100%;
388 height: 33px;
388 height: 33px;
389 padding: 3px 8px;
389 padding: 3px 8px;
390 margin-left: 8px;
390 margin-left: 8px;
391 background-color: #eeeeee;
391 background-color: #eeeeee;
392 }
392 }
393
393
394 .reply-thread-reply-button .cb-comment-add-button {
394 .reply-thread-reply-button .cb-comment-add-button {
395 border-radius: 4px;
395 border-radius: 4px;
396 width: 100%;
396 width: 100%;
397 padding: 6px 2px;
397 padding: 6px 2px;
398 text-align: left;
398 text-align: left;
399 cursor: text;
399 cursor: text;
400 color: @grey3;
400 color: @grey3;
401 }
401 }
402 .reply-thread-reply-button .cb-comment-add-button:hover {
402 .reply-thread-reply-button .cb-comment-add-button:hover {
403 background-color: white;
403 background-color: white;
404 color: @grey2;
404 color: @grey2;
405 }
405 }
406
406
407 .reply-thread-last {
407 .reply-thread-last {
408 display: table-cell;
408 display: table-cell;
409 width: 10px;
409 width: 10px;
410 }
410 }
411
411
412 /* Hide reply box when it's a first element,
412 /* Hide reply box when it's a first element,
413 can happen when drafts are saved but not shown to specific user,
413 can happen when drafts are saved but not shown to specific user,
414 or there are outdated comments hidden
414 or there are outdated comments hidden
415 */
415 */
416 .reply-thread-container-wrapper:first-child:not(.comment-form-active) {
416 .reply-thread-container-wrapper:first-child:not(.comment-form-active) {
417 display: none;
417 display: none;
418 }
418 }
419
419
420 .reply-thread-container-wrapper.comment-outdated {
420 .reply-thread-container-wrapper.comment-outdated {
421 display: none
421 display: none
422 }
422 }
423
423
424 /* hide add comment button when form is open */
424 /* hide add comment button when form is open */
425 .comment-inline-form-open ~ .cb-comment-add-button {
425 .comment-inline-form-open ~ .cb-comment-add-button {
426 display: none;
426 display: none;
427 }
427 }
428
428
429 /* hide add comment button when only comment is being deleted */
429 /* hide add comment button when only comment is being deleted */
430 .comment-deleting:first-child + .cb-comment-add-button {
430 .comment-deleting:first-child + .cb-comment-add-button {
431 display: none;
431 display: none;
432 }
432 }
433
433
434 /* hide add comment button when form but no comments */
434 /* hide add comment button when form but no comments */
435 .comment-inline-form:first-child + .cb-comment-add-button {
435 .comment-inline-form:first-child + .cb-comment-add-button {
436 display: none;
436 display: none;
437 }
437 }
438
438
439 }
439 }
440
440
441 .show-outdated-comments {
441 .show-outdated-comments {
442 display: inline;
442 display: inline;
443 color: @rcblue;
443 color: @rcblue;
444 }
444 }
445
445
446 // Comment Form
446 // Comment Form
447 div.comment-form {
447 div.comment-form {
448 margin-top: 20px;
448 margin-top: 20px;
449 }
449 }
450
450
451 .comment-form strong {
451 .comment-form strong {
452 display: block;
452 display: block;
453 margin-bottom: 15px;
453 margin-bottom: 15px;
454 }
454 }
455
455
456 .comment-form textarea {
456 .comment-form textarea {
457 width: 100%;
457 width: 100%;
458 height: 100px;
458 height: 100px;
459 font-family: @text-monospace;
459 font-family: @text-monospace;
460 }
460 }
461
461
462 form.comment-form {
462 form.comment-form {
463 margin-top: 10px;
463 margin-top: 10px;
464 margin-left: 10px;
464 margin-left: 10px;
465 }
465 }
466
466
467 .comment-inline-form .comment-block-ta,
467 .comment-inline-form .comment-block-ta,
468 .comment-form .comment-block-ta,
468 .comment-form .comment-block-ta,
469 .comment-form .preview-box {
469 .comment-form .preview-box {
470 .border-radius(@border-radius);
470 .border-radius(@border-radius);
471 .box-sizing(border-box);
471 .box-sizing(border-box);
472 background-color: white;
472 background-color: white;
473 }
473 }
474
474
475 .comment-form-submit {
475 .comment-form-submit {
476 margin-top: 5px;
476 margin-top: 5px;
477 margin-left: 525px;
477 margin-left: 525px;
478 }
478 }
479
479
480 .file-comments {
480 .file-comments {
481 display: none;
481 display: none;
482 }
482 }
483
483
484 .comment-form .preview-box.unloaded,
484 .comment-form .preview-box.unloaded,
485 .comment-inline-form .preview-box.unloaded {
485 .comment-inline-form .preview-box.unloaded {
486 height: 50px;
486 height: 50px;
487 text-align: center;
487 text-align: center;
488 padding: 20px;
488 padding: 20px;
489 background-color: white;
489 background-color: white;
490 }
490 }
491
491
492 .comment-footer {
492 .comment-footer {
493 display: table;
493 display: table;
494 width: 100%;
494 width: 100%;
495 height: 42px;
495 height: 42px;
496
496
497 .comment-status-box,
497 .comment-status-box,
498 .cancel-button {
498 .cancel-button {
499 display: inline-block;
499 display: inline-block;
500 }
500 }
501
501
502 .comment-status-box {
502 .comment-status-box {
503 margin-left: 10px;
503 margin-left: 10px;
504 }
504 }
505
505
506 .action-buttons {
506 .action-buttons {
507 display: table-cell;
507 display: table-cell;
508 padding: 5px 0 5px 2px;
508 padding: 5px 0 5px 2px;
509 }
509 }
510
510
511 .toolbar-text {
511 .toolbar-text {
512 height: 42px;
512 height: 42px;
513 display: table-cell;
513 display: table-cell;
514 vertical-align: bottom;
514 vertical-align: bottom;
515 font-size: 11px;
515 font-size: 11px;
516 color: @grey4;
516 color: @grey4;
517 text-align: right;
517 text-align: right;
518
518
519 a {
519 a {
520 color: @grey4;
520 color: @grey4;
521 }
521 }
522
522
523 p {
523 p {
524 padding: 0;
524 padding: 0;
525 margin: 0;
525 margin: 0;
526 }
526 }
527 }
527 }
528
528
529 .action-buttons-extra {
529 .action-buttons-extra {
530 display: inline-block;
530 display: inline-block;
531 }
531 }
532 }
532 }
533
533
534 .comment-form {
534 .comment-form {
535
535
536 .comment {
536 .comment {
537 margin-left: 10px;
537 margin-left: 10px;
538 }
538 }
539
539
540 .comment-help {
540 .comment-help {
541 color: @grey4;
541 color: @grey4;
542 padding: 5px 0 5px 0;
542 padding: 5px 0 5px 0;
543 }
543 }
544
544
545 .comment-title {
545 .comment-title {
546 padding: 5px 0 5px 0;
546 padding: 5px 0 5px 0;
547 }
547 }
548
548
549 .comment-button {
549 .comment-button {
550 display: inline-block;
550 display: inline-block;
551 }
551 }
552
552
553 .comment-button-input {
553 .comment-button-input {
554 margin-right: 0;
554 margin-right: 0;
555 }
555 }
556
556
557 #save_general {
558 margin-left: -6px;
559 }
560
557 }
561 }
558
562
559
563
560 .comment-form-login {
564 .comment-form-login {
561 .comment-help {
565 .comment-help {
562 padding: 0.7em; //same as the button
566 padding: 0.7em; //same as the button
563 }
567 }
564
568
565 div.clearfix {
569 div.clearfix {
566 clear: both;
570 clear: both;
567 width: 100%;
571 width: 100%;
568 display: block;
572 display: block;
569 }
573 }
570 }
574 }
571
575
572 .comment-version-select {
576 .comment-version-select {
573 margin: 0px;
577 margin: 0px;
574 border-radius: inherit;
578 border-radius: inherit;
575 border-color: @grey6;
579 border-color: @grey6;
576 height: 20px;
580 height: 20px;
577 }
581 }
578
582
579 .comment-type {
583 .comment-type {
580 margin: 0px;
584 margin: 0px;
581 border-radius: inherit;
585 border-radius: inherit;
582 border-color: @grey6;
586 border-color: @grey6;
583 }
587 }
584
588
585 .preview-box {
589 .preview-box {
586 min-height: 105px;
590 min-height: 105px;
587 margin-bottom: 15px;
591 margin-bottom: 15px;
588 background-color: white;
592 background-color: white;
589 .border-radius(@border-radius);
593 .border-radius(@border-radius);
590 .box-sizing(border-box);
594 .box-sizing(border-box);
591 }
595 }
592
596
593 .add-another-button {
597 .add-another-button {
594 margin-left: 10px;
598 margin-left: 10px;
595 margin-top: 10px;
599 margin-top: 10px;
596 margin-bottom: 10px;
600 margin-bottom: 10px;
597 }
601 }
598
602
599 .comment .buttons {
603 .comment .buttons {
600 float: right;
604 float: right;
601 margin: -1px 0px 0px 0px;
605 margin: -1px 0px 0px 0px;
602 }
606 }
603
607
604 // Inline Comment Form
608 // Inline Comment Form
605 .injected_diff .comment-inline-form,
609 .injected_diff .comment-inline-form,
606 .comment-inline-form {
610 .comment-inline-form {
607 background-color: white;
611 background-color: white;
608 margin-top: 4px;
612 margin-top: 4px;
609 margin-bottom: 10px;
613 margin-bottom: 10px;
610 }
614 }
611
615
612 .inline-form {
616 .inline-form {
613 padding: 10px 7px;
617 padding: 10px 7px;
614 }
618 }
615
619
616 .inline-form div {
620 .inline-form div {
617 max-width: 100%;
621 max-width: 100%;
618 }
622 }
619
623
620 .overlay {
624 .overlay {
621 display: none;
625 display: none;
622 position: absolute;
626 position: absolute;
623 width: 100%;
627 width: 100%;
624 text-align: center;
628 text-align: center;
625 vertical-align: middle;
629 vertical-align: middle;
626 font-size: 16px;
630 font-size: 16px;
627 background: none repeat scroll 0 0 white;
631 background: none repeat scroll 0 0 white;
628
632
629 &.submitting {
633 &.submitting {
630 display: block;
634 display: block;
631 opacity: 0.5;
635 opacity: 0.5;
632 z-index: 100;
636 z-index: 100;
633 }
637 }
634 }
638 }
635 .comment-inline-form .overlay.submitting .overlay-text {
639 .comment-inline-form .overlay.submitting .overlay-text {
636 margin-top: 5%;
640 margin-top: 5%;
637 }
641 }
638
642
639 .comment-inline-form .clearfix,
643 .comment-inline-form .clearfix,
640 .comment-form .clearfix {
644 .comment-form .clearfix {
641 .border-radius(@border-radius);
645 .border-radius(@border-radius);
642 margin: 0px;
646 margin: 0px;
643 }
647 }
644
648
645
649
646 .hide-inline-form-button {
650 .hide-inline-form-button {
647 margin-left: 5px;
651 margin-left: 5px;
648 }
652 }
649 .comment-button .hide-inline-form {
653 .comment-button .hide-inline-form {
650 background: white;
654 background: white;
651 }
655 }
652
656
653 .comment-area {
657 .comment-area {
654 padding: 6px 8px;
658 padding: 6px 8px;
655 border: 1px solid @grey5;
659 border: 1px solid @grey5;
656 .border-radius(@border-radius);
660 .border-radius(@border-radius);
657
661
658 .resolve-action {
662 .resolve-action {
659 padding: 1px 0px 0px 6px;
663 padding: 1px 0px 0px 6px;
660 }
664 }
661
665
662 }
666 }
663
667
664 comment-area-text {
668 comment-area-text {
665 color: @grey3;
669 color: @grey3;
666 }
670 }
667
671
668 .comment-area-header {
672 .comment-area-header {
669 height: 35px;
673 height: 35px;
670 border-bottom: 1px solid @grey5;
674 border-bottom: 1px solid @grey5;
671 }
675 }
672
676
673 .comment-area-header .nav-links {
677 .comment-area-header .nav-links {
674 display: flex;
678 display: flex;
675 flex-flow: row wrap;
679 flex-flow: row wrap;
676 -webkit-flex-flow: row wrap;
680 -webkit-flex-flow: row wrap;
677 width: 100%;
681 width: 100%;
678 border: none;
682 border: none;
679 }
683 }
680
684
681 .comment-area-footer {
685 .comment-area-footer {
682 min-height: 30px;
686 min-height: 30px;
683 }
687 }
684
688
685 .comment-footer .toolbar {
689 .comment-footer .toolbar {
686
690
687 }
691 }
688
692
689 .comment-attachment-uploader {
693 .comment-attachment-uploader {
690 border: 1px dashed white;
694 border: 1px dashed white;
691 border-radius: @border-radius;
695 border-radius: @border-radius;
692 margin-top: -10px;
696 margin-top: -10px;
693 line-height: 30px;
697 line-height: 30px;
694 &.dz-drag-hover {
698 &.dz-drag-hover {
695 border-color: @grey3;
699 border-color: @grey3;
696 }
700 }
697
701
698 .dz-error-message {
702 .dz-error-message {
699 padding-top: 0;
703 padding-top: 0;
700 }
704 }
701 }
705 }
702
706
703 .comment-attachment-text {
707 .comment-attachment-text {
704 clear: both;
708 clear: both;
705 font-size: 11px;
709 font-size: 11px;
706 color: #8F8F8F;
710 color: #8F8F8F;
707 width: 100%;
711 width: 100%;
708 .pick-attachment {
712 .pick-attachment {
709 color: #8F8F8F;
713 color: #8F8F8F;
710 }
714 }
711 .pick-attachment:hover {
715 .pick-attachment:hover {
712 color: @rcblue;
716 color: @rcblue;
713 }
717 }
714 }
718 }
715
719
716 .nav-links {
720 .nav-links {
717 padding: 0;
721 padding: 0;
718 margin: 0;
722 margin: 0;
719 list-style: none;
723 list-style: none;
720 height: auto;
724 height: auto;
721 border-bottom: 1px solid @grey5;
725 border-bottom: 1px solid @grey5;
722 }
726 }
723 .nav-links li {
727 .nav-links li {
724 display: inline-block;
728 display: inline-block;
725 list-style-type: none;
729 list-style-type: none;
726 }
730 }
727
731
728 .nav-links li a.disabled {
732 .nav-links li a.disabled {
729 cursor: not-allowed;
733 cursor: not-allowed;
730 }
734 }
731
735
732 .nav-links li.active a {
736 .nav-links li.active a {
733 border-bottom: 2px solid @rcblue;
737 border-bottom: 2px solid @rcblue;
734 color: #000;
738 color: #000;
735 font-weight: 600;
739 font-weight: 600;
736 }
740 }
737 .nav-links li a {
741 .nav-links li a {
738 display: inline-block;
742 display: inline-block;
739 padding: 0px 10px 5px 10px;
743 padding: 0px 10px 5px 10px;
740 margin-bottom: -1px;
744 margin-bottom: -1px;
741 font-size: 14px;
745 font-size: 14px;
742 line-height: 28px;
746 line-height: 28px;
743 color: #8f8f8f;
747 color: #8f8f8f;
744 border-bottom: 2px solid transparent;
748 border-bottom: 2px solid transparent;
745 }
749 }
746
750
@@ -1,402 +1,403 b''
1
1
2 /******************************************************************************
2 /******************************************************************************
3 * *
3 * *
4 * DO NOT CHANGE THIS FILE MANUALLY *
4 * DO NOT CHANGE THIS FILE MANUALLY *
5 * *
5 * *
6 * *
6 * *
7 * This file is automatically generated when the app starts up with *
7 * This file is automatically generated when the app starts up with *
8 * generate_js_files = true *
8 * generate_js_files = true *
9 * *
9 * *
10 * To add a route here pass jsroute=True to the route definition in the app *
10 * To add a route here pass jsroute=True to the route definition in the app *
11 * *
11 * *
12 ******************************************************************************/
12 ******************************************************************************/
13 function registerRCRoutes() {
13 function registerRCRoutes() {
14 // routes registration
14 // routes registration
15 pyroutes.register('favicon', '/favicon.ico', []);
15 pyroutes.register('favicon', '/favicon.ico', []);
16 pyroutes.register('robots', '/robots.txt', []);
16 pyroutes.register('robots', '/robots.txt', []);
17 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
17 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
18 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
18 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
19 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
19 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
26 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/_settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
26 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/_settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
27 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/_settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
27 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/_settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
31 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
31 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
33 pyroutes.register('hovercard_user', '/_hovercard/user/%(user_id)s', ['user_id']);
33 pyroutes.register('hovercard_user', '/_hovercard/user/%(user_id)s', ['user_id']);
34 pyroutes.register('hovercard_username', '/_hovercard/username/%(username)s', ['username']);
34 pyroutes.register('hovercard_username', '/_hovercard/username/%(username)s', ['username']);
35 pyroutes.register('hovercard_user_group', '/_hovercard/user_group/%(user_group_id)s', ['user_group_id']);
35 pyroutes.register('hovercard_user_group', '/_hovercard/user_group/%(user_group_id)s', ['user_group_id']);
36 pyroutes.register('hovercard_pull_request', '/_hovercard/pull_request/%(pull_request_id)s', ['pull_request_id']);
36 pyroutes.register('hovercard_pull_request', '/_hovercard/pull_request/%(pull_request_id)s', ['pull_request_id']);
37 pyroutes.register('hovercard_repo_commit', '/_hovercard/commit/%(repo_name)s/%(commit_id)s', ['repo_name', 'commit_id']);
37 pyroutes.register('hovercard_repo_commit', '/_hovercard/commit/%(repo_name)s/%(commit_id)s', ['repo_name', 'commit_id']);
38 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
38 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
39 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
39 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
40 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
40 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
41 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
41 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
42 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
42 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
43 pyroutes.register('admin_home', '/_admin', []);
43 pyroutes.register('admin_home', '/_admin', []);
44 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
44 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
45 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
45 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
46 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
46 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
47 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
47 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
48 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
48 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
49 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
49 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
50 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
50 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
51 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
51 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
52 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
52 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
53 pyroutes.register('admin_settings_exception_tracker', '/_admin/settings/exceptions', []);
53 pyroutes.register('admin_settings_exception_tracker', '/_admin/settings/exceptions', []);
54 pyroutes.register('admin_settings_exception_tracker_delete_all', '/_admin/settings/exceptions/delete', []);
54 pyroutes.register('admin_settings_exception_tracker_delete_all', '/_admin/settings/exceptions/delete', []);
55 pyroutes.register('admin_settings_exception_tracker_show', '/_admin/settings/exceptions/%(exception_id)s', ['exception_id']);
55 pyroutes.register('admin_settings_exception_tracker_show', '/_admin/settings/exceptions/%(exception_id)s', ['exception_id']);
56 pyroutes.register('admin_settings_exception_tracker_delete', '/_admin/settings/exceptions/%(exception_id)s/delete', ['exception_id']);
56 pyroutes.register('admin_settings_exception_tracker_delete', '/_admin/settings/exceptions/%(exception_id)s/delete', ['exception_id']);
57 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
57 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
58 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
58 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
59 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
59 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
60 pyroutes.register('admin_settings_process_management_data', '/_admin/settings/process_management/data', []);
60 pyroutes.register('admin_settings_process_management_data', '/_admin/settings/process_management/data', []);
61 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
61 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
62 pyroutes.register('admin_settings_process_management_master_signal', '/_admin/settings/process_management/master_signal', []);
62 pyroutes.register('admin_settings_process_management_master_signal', '/_admin/settings/process_management/master_signal', []);
63 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
63 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
64 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
64 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
65 pyroutes.register('admin_settings', '/_admin/settings', []);
65 pyroutes.register('admin_settings', '/_admin/settings', []);
66 pyroutes.register('admin_settings_update', '/_admin/settings/update', []);
66 pyroutes.register('admin_settings_update', '/_admin/settings/update', []);
67 pyroutes.register('admin_settings_global', '/_admin/settings/global', []);
67 pyroutes.register('admin_settings_global', '/_admin/settings/global', []);
68 pyroutes.register('admin_settings_global_update', '/_admin/settings/global/update', []);
68 pyroutes.register('admin_settings_global_update', '/_admin/settings/global/update', []);
69 pyroutes.register('admin_settings_vcs', '/_admin/settings/vcs', []);
69 pyroutes.register('admin_settings_vcs', '/_admin/settings/vcs', []);
70 pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []);
70 pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []);
71 pyroutes.register('admin_settings_vcs_svn_pattern_delete', '/_admin/settings/vcs/svn_pattern_delete', []);
71 pyroutes.register('admin_settings_vcs_svn_pattern_delete', '/_admin/settings/vcs/svn_pattern_delete', []);
72 pyroutes.register('admin_settings_mapping', '/_admin/settings/mapping', []);
72 pyroutes.register('admin_settings_mapping', '/_admin/settings/mapping', []);
73 pyroutes.register('admin_settings_mapping_update', '/_admin/settings/mapping/update', []);
73 pyroutes.register('admin_settings_mapping_update', '/_admin/settings/mapping/update', []);
74 pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []);
74 pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []);
75 pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []);
75 pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []);
76 pyroutes.register('admin_settings_issuetracker', '/_admin/settings/issue-tracker', []);
76 pyroutes.register('admin_settings_issuetracker', '/_admin/settings/issue-tracker', []);
77 pyroutes.register('admin_settings_issuetracker_update', '/_admin/settings/issue-tracker/update', []);
77 pyroutes.register('admin_settings_issuetracker_update', '/_admin/settings/issue-tracker/update', []);
78 pyroutes.register('admin_settings_issuetracker_test', '/_admin/settings/issue-tracker/test', []);
78 pyroutes.register('admin_settings_issuetracker_test', '/_admin/settings/issue-tracker/test', []);
79 pyroutes.register('admin_settings_issuetracker_delete', '/_admin/settings/issue-tracker/delete', []);
79 pyroutes.register('admin_settings_issuetracker_delete', '/_admin/settings/issue-tracker/delete', []);
80 pyroutes.register('admin_settings_email', '/_admin/settings/email', []);
80 pyroutes.register('admin_settings_email', '/_admin/settings/email', []);
81 pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []);
81 pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []);
82 pyroutes.register('admin_settings_hooks', '/_admin/settings/hooks', []);
82 pyroutes.register('admin_settings_hooks', '/_admin/settings/hooks', []);
83 pyroutes.register('admin_settings_hooks_update', '/_admin/settings/hooks/update', []);
83 pyroutes.register('admin_settings_hooks_update', '/_admin/settings/hooks/update', []);
84 pyroutes.register('admin_settings_hooks_delete', '/_admin/settings/hooks/delete', []);
84 pyroutes.register('admin_settings_hooks_delete', '/_admin/settings/hooks/delete', []);
85 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
85 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
86 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
86 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
87 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
87 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
88 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
88 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
89 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
89 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
90 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
90 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
91 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
91 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
92 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
92 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
93 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
93 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
94 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
94 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
95 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
95 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
96 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
96 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
97 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
97 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
98 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
98 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
99 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
99 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
100 pyroutes.register('users', '/_admin/users', []);
100 pyroutes.register('users', '/_admin/users', []);
101 pyroutes.register('users_data', '/_admin/users_data', []);
101 pyroutes.register('users_data', '/_admin/users_data', []);
102 pyroutes.register('users_create', '/_admin/users/create', []);
102 pyroutes.register('users_create', '/_admin/users/create', []);
103 pyroutes.register('users_new', '/_admin/users/new', []);
103 pyroutes.register('users_new', '/_admin/users/new', []);
104 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
104 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
105 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
105 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
106 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
106 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
107 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
107 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
108 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
108 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
109 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
109 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
110 pyroutes.register('user_enable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_enable', ['user_id']);
110 pyroutes.register('user_enable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_enable', ['user_id']);
111 pyroutes.register('user_disable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_disable', ['user_id']);
111 pyroutes.register('user_disable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_disable', ['user_id']);
112 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
112 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
113 pyroutes.register('user_notice_dismiss', '/_admin/users/%(user_id)s/notice_dismiss', ['user_id']);
113 pyroutes.register('user_notice_dismiss', '/_admin/users/%(user_id)s/notice_dismiss', ['user_id']);
114 pyroutes.register('edit_user_auth_tokens_view', '/_admin/users/%(user_id)s/edit/auth_tokens/view', ['user_id']);
114 pyroutes.register('edit_user_auth_tokens_view', '/_admin/users/%(user_id)s/edit/auth_tokens/view', ['user_id']);
115 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
115 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
116 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
116 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
117 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
117 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
118 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
118 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
119 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
119 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
120 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
120 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
121 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
121 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
122 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
122 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
123 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
123 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
124 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
124 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
125 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
125 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
126 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
126 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
127 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
127 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
128 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
128 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
129 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
129 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
130 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
130 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
131 pyroutes.register('edit_user_audit_logs_download', '/_admin/users/%(user_id)s/edit/audit/download', ['user_id']);
131 pyroutes.register('edit_user_audit_logs_download', '/_admin/users/%(user_id)s/edit/audit/download', ['user_id']);
132 pyroutes.register('edit_user_caches', '/_admin/users/%(user_id)s/edit/caches', ['user_id']);
132 pyroutes.register('edit_user_caches', '/_admin/users/%(user_id)s/edit/caches', ['user_id']);
133 pyroutes.register('edit_user_caches_update', '/_admin/users/%(user_id)s/edit/caches/update', ['user_id']);
133 pyroutes.register('edit_user_caches_update', '/_admin/users/%(user_id)s/edit/caches/update', ['user_id']);
134 pyroutes.register('user_groups', '/_admin/user_groups', []);
134 pyroutes.register('user_groups', '/_admin/user_groups', []);
135 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
135 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
136 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
136 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
137 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
137 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
138 pyroutes.register('repos', '/_admin/repos', []);
138 pyroutes.register('repos', '/_admin/repos', []);
139 pyroutes.register('repos_data', '/_admin/repos_data', []);
139 pyroutes.register('repos_data', '/_admin/repos_data', []);
140 pyroutes.register('repo_new', '/_admin/repos/new', []);
140 pyroutes.register('repo_new', '/_admin/repos/new', []);
141 pyroutes.register('repo_create', '/_admin/repos/create', []);
141 pyroutes.register('repo_create', '/_admin/repos/create', []);
142 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
142 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
143 pyroutes.register('repo_groups_data', '/_admin/repo_groups_data', []);
143 pyroutes.register('repo_groups_data', '/_admin/repo_groups_data', []);
144 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
144 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
145 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
145 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
146 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
146 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
147 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
147 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
148 pyroutes.register('channelstream_proxy', '/_channelstream', []);
148 pyroutes.register('channelstream_proxy', '/_channelstream', []);
149 pyroutes.register('upload_file', '/_file_store/upload', []);
149 pyroutes.register('upload_file', '/_file_store/upload', []);
150 pyroutes.register('download_file', '/_file_store/download/%(fid)s', ['fid']);
150 pyroutes.register('download_file', '/_file_store/download/%(fid)s', ['fid']);
151 pyroutes.register('download_file_by_token', '/_file_store/token-download/%(_auth_token)s/%(fid)s', ['_auth_token', 'fid']);
151 pyroutes.register('download_file_by_token', '/_file_store/token-download/%(_auth_token)s/%(fid)s', ['_auth_token', 'fid']);
152 pyroutes.register('logout', '/_admin/logout', []);
152 pyroutes.register('logout', '/_admin/logout', []);
153 pyroutes.register('reset_password', '/_admin/password_reset', []);
153 pyroutes.register('reset_password', '/_admin/password_reset', []);
154 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
154 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
155 pyroutes.register('home', '/', []);
155 pyroutes.register('home', '/', []);
156 pyroutes.register('main_page_repos_data', '/_home_repos', []);
156 pyroutes.register('main_page_repos_data', '/_home_repos', []);
157 pyroutes.register('main_page_repo_groups_data', '/_home_repo_groups', []);
157 pyroutes.register('main_page_repo_groups_data', '/_home_repo_groups', []);
158 pyroutes.register('user_autocomplete_data', '/_users', []);
158 pyroutes.register('user_autocomplete_data', '/_users', []);
159 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
159 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
160 pyroutes.register('repo_list_data', '/_repos', []);
160 pyroutes.register('repo_list_data', '/_repos', []);
161 pyroutes.register('repo_group_list_data', '/_repo_groups', []);
161 pyroutes.register('repo_group_list_data', '/_repo_groups', []);
162 pyroutes.register('goto_switcher_data', '/_goto_data', []);
162 pyroutes.register('goto_switcher_data', '/_goto_data', []);
163 pyroutes.register('markup_preview', '/_markup_preview', []);
163 pyroutes.register('markup_preview', '/_markup_preview', []);
164 pyroutes.register('file_preview', '/_file_preview', []);
164 pyroutes.register('file_preview', '/_file_preview', []);
165 pyroutes.register('store_user_session_value', '/_store_session_attr', []);
165 pyroutes.register('store_user_session_value', '/_store_session_attr', []);
166 pyroutes.register('journal', '/_admin/journal', []);
166 pyroutes.register('journal', '/_admin/journal', []);
167 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
167 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
168 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
168 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
169 pyroutes.register('journal_public', '/_admin/public_journal', []);
169 pyroutes.register('journal_public', '/_admin/public_journal', []);
170 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
170 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
171 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
171 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
172 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
172 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
173 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
173 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
174 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
174 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
175 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
175 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
176 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
176 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
177 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
177 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
178 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
178 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
179 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
179 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
180 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
180 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
181 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
181 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
182 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
182 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
183 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
183 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
184 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
184 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
185 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
185 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
186 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
186 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
187 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
187 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
188 pyroutes.register('repo_commit_comment_history_view', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_history_id)s/history_view', ['repo_name', 'commit_id', 'comment_history_id']);
188 pyroutes.register('repo_commit_comment_history_view', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_history_id)s/history_view', ['repo_name', 'commit_id', 'comment_history_id']);
189 pyroutes.register('repo_commit_comment_attachment_upload', '/%(repo_name)s/changeset/%(commit_id)s/comment/attachment_upload', ['repo_name', 'commit_id']);
189 pyroutes.register('repo_commit_comment_attachment_upload', '/%(repo_name)s/changeset/%(commit_id)s/comment/attachment_upload', ['repo_name', 'commit_id']);
190 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
190 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
191 pyroutes.register('repo_commit_comment_edit', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/edit', ['repo_name', 'commit_id', 'comment_id']);
191 pyroutes.register('repo_commit_comment_edit', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/edit', ['repo_name', 'commit_id', 'comment_id']);
192 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
192 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
193 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
193 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
194 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
194 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
195 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
195 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
196 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
196 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
197 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
197 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
198 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
198 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
199 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
199 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
200 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
200 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
201 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
201 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
202 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
202 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
203 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
203 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
204 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
204 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
205 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
205 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
206 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
206 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
207 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
207 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
208 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
208 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
209 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
209 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
210 pyroutes.register('repo_files_check_head', '/%(repo_name)s/check_head/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
210 pyroutes.register('repo_files_check_head', '/%(repo_name)s/check_head/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
211 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
211 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
212 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
212 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
213 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
213 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
214 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
214 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
215 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
215 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
216 pyroutes.register('repo_files_upload_file', '/%(repo_name)s/upload_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
216 pyroutes.register('repo_files_upload_file', '/%(repo_name)s/upload_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
217 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
217 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
218 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
218 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
219 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
219 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
220 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
220 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
221 pyroutes.register('repo_commits', '/%(repo_name)s/commits', ['repo_name']);
221 pyroutes.register('repo_commits', '/%(repo_name)s/commits', ['repo_name']);
222 pyroutes.register('repo_commits_file', '/%(repo_name)s/commits/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
222 pyroutes.register('repo_commits_file', '/%(repo_name)s/commits/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
223 pyroutes.register('repo_commits_elements', '/%(repo_name)s/commits_elements', ['repo_name']);
223 pyroutes.register('repo_commits_elements', '/%(repo_name)s/commits_elements', ['repo_name']);
224 pyroutes.register('repo_commits_elements_file', '/%(repo_name)s/commits_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
224 pyroutes.register('repo_commits_elements_file', '/%(repo_name)s/commits_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
225 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
225 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
226 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
226 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
227 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
227 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
228 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
228 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
229 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
229 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
230 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
230 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
231 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
231 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
232 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
232 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
233 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
233 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
234 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
234 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
235 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
235 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
236 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
236 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
237 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
237 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
238 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
238 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
239 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
239 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
240 pyroutes.register('pullrequest_repo_targets', '/%(repo_name)s/pull-request/repo-targets', ['repo_name']);
240 pyroutes.register('pullrequest_repo_targets', '/%(repo_name)s/pull-request/repo-targets', ['repo_name']);
241 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
241 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
242 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
242 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
243 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
243 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
244 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
244 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
245 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
245 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
246 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
246 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
247 pyroutes.register('pullrequest_comment_edit', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/edit', ['repo_name', 'pull_request_id', 'comment_id']);
247 pyroutes.register('pullrequest_comment_edit', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/edit', ['repo_name', 'pull_request_id', 'comment_id']);
248 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
248 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
249 pyroutes.register('pullrequest_comments', '/%(repo_name)s/pull-request/%(pull_request_id)s/comments', ['repo_name', 'pull_request_id']);
249 pyroutes.register('pullrequest_comments', '/%(repo_name)s/pull-request/%(pull_request_id)s/comments', ['repo_name', 'pull_request_id']);
250 pyroutes.register('pullrequest_todos', '/%(repo_name)s/pull-request/%(pull_request_id)s/todos', ['repo_name', 'pull_request_id']);
250 pyroutes.register('pullrequest_todos', '/%(repo_name)s/pull-request/%(pull_request_id)s/todos', ['repo_name', 'pull_request_id']);
251 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
251 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
252 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
252 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
253 pyroutes.register('edit_repo_advanced_archive', '/%(repo_name)s/settings/advanced/archive', ['repo_name']);
253 pyroutes.register('edit_repo_advanced_archive', '/%(repo_name)s/settings/advanced/archive', ['repo_name']);
254 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
254 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
255 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
255 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
256 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
256 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
257 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
257 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
258 pyroutes.register('edit_repo_advanced_hooks', '/%(repo_name)s/settings/advanced/hooks', ['repo_name']);
258 pyroutes.register('edit_repo_advanced_hooks', '/%(repo_name)s/settings/advanced/hooks', ['repo_name']);
259 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
259 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
260 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
260 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
261 pyroutes.register('edit_repo_perms_set_private', '/%(repo_name)s/settings/permissions/set_private', ['repo_name']);
261 pyroutes.register('edit_repo_perms_set_private', '/%(repo_name)s/settings/permissions/set_private', ['repo_name']);
262 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
262 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
263 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
263 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
264 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
264 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
265 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
265 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
266 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
266 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
267 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
267 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
268 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
268 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
269 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
269 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
270 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
270 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
271 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
271 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
272 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
272 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
273 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
273 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
274 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
274 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
275 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
275 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
276 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
276 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
277 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
277 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
278 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
278 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
279 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
279 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
280 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
280 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
281 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
281 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
282 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
282 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
283 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
283 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
284 pyroutes.register('edit_repo_audit_logs', '/%(repo_name)s/settings/audit_logs', ['repo_name']);
284 pyroutes.register('edit_repo_audit_logs', '/%(repo_name)s/settings/audit_logs', ['repo_name']);
285 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed-rss', ['repo_name']);
285 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed-rss', ['repo_name']);
286 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed-atom', ['repo_name']);
286 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed-atom', ['repo_name']);
287 pyroutes.register('rss_feed_home_old', '/%(repo_name)s/feed/rss', ['repo_name']);
287 pyroutes.register('rss_feed_home_old', '/%(repo_name)s/feed/rss', ['repo_name']);
288 pyroutes.register('atom_feed_home_old', '/%(repo_name)s/feed/atom', ['repo_name']);
288 pyroutes.register('atom_feed_home_old', '/%(repo_name)s/feed/atom', ['repo_name']);
289 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
289 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
290 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
290 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
291 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
291 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
292 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
292 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
293 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
293 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
294 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
294 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
295 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
295 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
296 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
296 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
297 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
297 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
298 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
298 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
299 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
299 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
300 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
300 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
301 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
301 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
302 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
302 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
303 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
303 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
304 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
304 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
305 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
305 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
306 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
306 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
307 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
307 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
308 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
308 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
309 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
309 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
310 pyroutes.register('search', '/_admin/search', []);
310 pyroutes.register('search', '/_admin/search', []);
311 pyroutes.register('search_repo', '/%(repo_name)s/_search', ['repo_name']);
311 pyroutes.register('search_repo', '/%(repo_name)s/_search', ['repo_name']);
312 pyroutes.register('search_repo_alt', '/%(repo_name)s/search', ['repo_name']);
312 pyroutes.register('search_repo_alt', '/%(repo_name)s/search', ['repo_name']);
313 pyroutes.register('search_repo_group', '/%(repo_group_name)s/_search', ['repo_group_name']);
313 pyroutes.register('search_repo_group', '/%(repo_group_name)s/_search', ['repo_group_name']);
314 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
314 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
315 pyroutes.register('user_group_profile', '/_profile_user_group/%(user_group_name)s', ['user_group_name']);
315 pyroutes.register('user_group_profile', '/_profile_user_group/%(user_group_name)s', ['user_group_name']);
316 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
316 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
317 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
317 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
318 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
318 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
319 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
319 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
320 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
320 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
321 pyroutes.register('my_account_auth_tokens_view', '/_admin/my_account/auth_tokens/view', []);
321 pyroutes.register('my_account_auth_tokens_view', '/_admin/my_account/auth_tokens/view', []);
322 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
322 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
323 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
323 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
324 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
324 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
325 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
325 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
326 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
326 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
327 pyroutes.register('my_account_user_group_membership', '/_admin/my_account/user_group_membership', []);
327 pyroutes.register('my_account_user_group_membership', '/_admin/my_account/user_group_membership', []);
328 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
328 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
329 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
329 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
330 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
330 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
331 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
331 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
332 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
332 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
333 pyroutes.register('my_account_bookmarks', '/_admin/my_account/bookmarks', []);
333 pyroutes.register('my_account_bookmarks', '/_admin/my_account/bookmarks', []);
334 pyroutes.register('my_account_bookmarks_update', '/_admin/my_account/bookmarks/update', []);
334 pyroutes.register('my_account_bookmarks_update', '/_admin/my_account/bookmarks/update', []);
335 pyroutes.register('my_account_goto_bookmark', '/_admin/my_account/bookmark/%(bookmark_id)s', ['bookmark_id']);
335 pyroutes.register('my_account_goto_bookmark', '/_admin/my_account/bookmark/%(bookmark_id)s', ['bookmark_id']);
336 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
336 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
337 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
337 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
338 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
338 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
339 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
339 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
340 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
340 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
341 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
341 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
342 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
342 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
343 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
343 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
344 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
344 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
345 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
345 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
346 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
346 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
347 pyroutes.register('gists_show', '/_admin/gists', []);
347 pyroutes.register('gists_show', '/_admin/gists', []);
348 pyroutes.register('gists_new', '/_admin/gists/new', []);
348 pyroutes.register('gists_new', '/_admin/gists/new', []);
349 pyroutes.register('gists_create', '/_admin/gists/create', []);
349 pyroutes.register('gists_create', '/_admin/gists/create', []);
350 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
350 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
351 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
351 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
352 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
352 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
353 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
353 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
354 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
354 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
355 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
355 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
356 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
356 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
357 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
357 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
358 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
358 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
359 pyroutes.register('debug_style_email', '/_admin/debug_style/email/%(email_id)s', ['email_id']);
359 pyroutes.register('debug_style_email', '/_admin/debug_style/email/%(email_id)s', ['email_id']);
360 pyroutes.register('debug_style_email_plain_rendered', '/_admin/debug_style/email-rendered/%(email_id)s', ['email_id']);
360 pyroutes.register('debug_style_email_plain_rendered', '/_admin/debug_style/email-rendered/%(email_id)s', ['email_id']);
361 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
361 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
362 pyroutes.register('apiv2', '/_admin/api', []);
362 pyroutes.register('apiv2', '/_admin/api', []);
363 pyroutes.register('admin_settings_license', '/_admin/settings/license', []);
363 pyroutes.register('admin_settings_license', '/_admin/settings/license', []);
364 pyroutes.register('admin_settings_license_unlock', '/_admin/settings/license_unlock', []);
364 pyroutes.register('admin_settings_license_unlock', '/_admin/settings/license_unlock', []);
365 pyroutes.register('login', '/_admin/login', []);
365 pyroutes.register('login', '/_admin/login', []);
366 pyroutes.register('register', '/_admin/register', []);
366 pyroutes.register('register', '/_admin/register', []);
367 pyroutes.register('repo_reviewers_review_rule_new', '/%(repo_name)s/settings/review/rules/new', ['repo_name']);
367 pyroutes.register('repo_reviewers_review_rule_new', '/%(repo_name)s/settings/review/rules/new', ['repo_name']);
368 pyroutes.register('repo_reviewers_review_rule_edit', '/%(repo_name)s/settings/review/rules/%(rule_id)s', ['repo_name', 'rule_id']);
368 pyroutes.register('repo_reviewers_review_rule_edit', '/%(repo_name)s/settings/review/rules/%(rule_id)s', ['repo_name', 'rule_id']);
369 pyroutes.register('repo_reviewers_review_rule_delete', '/%(repo_name)s/settings/review/rules/%(rule_id)s/delete', ['repo_name', 'rule_id']);
369 pyroutes.register('repo_reviewers_review_rule_delete', '/%(repo_name)s/settings/review/rules/%(rule_id)s/delete', ['repo_name', 'rule_id']);
370 pyroutes.register('plugin_admin_chat', '/_admin/plugin_admin_chat/%(action)s', ['action']);
370 pyroutes.register('plugin_admin_chat', '/_admin/plugin_admin_chat/%(action)s', ['action']);
371 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
371 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
372 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
372 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
373 pyroutes.register('admin_settings_scheduler_show_tasks', '/_admin/settings/scheduler/_tasks', []);
373 pyroutes.register('admin_settings_scheduler_show_tasks', '/_admin/settings/scheduler/_tasks', []);
374 pyroutes.register('admin_settings_scheduler_show_all', '/_admin/settings/scheduler', []);
374 pyroutes.register('admin_settings_scheduler_show_all', '/_admin/settings/scheduler', []);
375 pyroutes.register('admin_settings_scheduler_new', '/_admin/settings/scheduler/new', []);
375 pyroutes.register('admin_settings_scheduler_new', '/_admin/settings/scheduler/new', []);
376 pyroutes.register('admin_settings_scheduler_create', '/_admin/settings/scheduler/create', []);
376 pyroutes.register('admin_settings_scheduler_create', '/_admin/settings/scheduler/create', []);
377 pyroutes.register('admin_settings_scheduler_edit', '/_admin/settings/scheduler/%(schedule_id)s', ['schedule_id']);
377 pyroutes.register('admin_settings_scheduler_edit', '/_admin/settings/scheduler/%(schedule_id)s', ['schedule_id']);
378 pyroutes.register('admin_settings_scheduler_update', '/_admin/settings/scheduler/%(schedule_id)s/update', ['schedule_id']);
378 pyroutes.register('admin_settings_scheduler_update', '/_admin/settings/scheduler/%(schedule_id)s/update', ['schedule_id']);
379 pyroutes.register('admin_settings_scheduler_delete', '/_admin/settings/scheduler/%(schedule_id)s/delete', ['schedule_id']);
379 pyroutes.register('admin_settings_scheduler_delete', '/_admin/settings/scheduler/%(schedule_id)s/delete', ['schedule_id']);
380 pyroutes.register('admin_settings_scheduler_execute', '/_admin/settings/scheduler/%(schedule_id)s/execute', ['schedule_id']);
380 pyroutes.register('admin_settings_scheduler_execute', '/_admin/settings/scheduler/%(schedule_id)s/execute', ['schedule_id']);
381 pyroutes.register('admin_settings_automation', '/_admin/settings/automation', []);
381 pyroutes.register('admin_settings_automation', '/_admin/settings/automation', []);
382 pyroutes.register('admin_settings_automation_update', '/_admin/settings/automation/%(entry_id)s/update', ['entry_id']);
382 pyroutes.register('admin_settings_automation_update', '/_admin/settings/automation/%(entry_id)s/update', ['entry_id']);
383 pyroutes.register('admin_permissions_branch', '/_admin/permissions/branch', []);
383 pyroutes.register('admin_permissions_branch', '/_admin/permissions/branch', []);
384 pyroutes.register('admin_permissions_branch_update', '/_admin/permissions/branch/update', []);
384 pyroutes.register('admin_permissions_branch_update', '/_admin/permissions/branch/update', []);
385 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
385 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
386 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
386 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
387 pyroutes.register('my_account_external_identity', '/_admin/my_account/external-identity', []);
387 pyroutes.register('my_account_external_identity', '/_admin/my_account/external-identity', []);
388 pyroutes.register('my_account_external_identity_delete', '/_admin/my_account/external-identity/delete', []);
388 pyroutes.register('my_account_external_identity_delete', '/_admin/my_account/external-identity/delete', []);
389 pyroutes.register('pullrequest_draft_comments_submit', '/%(repo_name)s/pull-request/%(pull_request_id)s/draft_comments_submit', ['repo_name', 'pull_request_id']);
389 pyroutes.register('repo_artifacts_list', '/%(repo_name)s/artifacts', ['repo_name']);
390 pyroutes.register('repo_artifacts_list', '/%(repo_name)s/artifacts', ['repo_name']);
390 pyroutes.register('repo_artifacts_data', '/%(repo_name)s/artifacts_data', ['repo_name']);
391 pyroutes.register('repo_artifacts_data', '/%(repo_name)s/artifacts_data', ['repo_name']);
391 pyroutes.register('repo_artifacts_new', '/%(repo_name)s/artifacts/new', ['repo_name']);
392 pyroutes.register('repo_artifacts_new', '/%(repo_name)s/artifacts/new', ['repo_name']);
392 pyroutes.register('repo_artifacts_get', '/%(repo_name)s/artifacts/download/%(uid)s', ['repo_name', 'uid']);
393 pyroutes.register('repo_artifacts_get', '/%(repo_name)s/artifacts/download/%(uid)s', ['repo_name', 'uid']);
393 pyroutes.register('repo_artifacts_store', '/%(repo_name)s/artifacts/store', ['repo_name']);
394 pyroutes.register('repo_artifacts_store', '/%(repo_name)s/artifacts/store', ['repo_name']);
394 pyroutes.register('repo_artifacts_info', '/%(repo_name)s/artifacts/info/%(uid)s', ['repo_name', 'uid']);
395 pyroutes.register('repo_artifacts_info', '/%(repo_name)s/artifacts/info/%(uid)s', ['repo_name', 'uid']);
395 pyroutes.register('repo_artifacts_delete', '/%(repo_name)s/artifacts/delete/%(uid)s', ['repo_name', 'uid']);
396 pyroutes.register('repo_artifacts_delete', '/%(repo_name)s/artifacts/delete/%(uid)s', ['repo_name', 'uid']);
396 pyroutes.register('repo_artifacts_update', '/%(repo_name)s/artifacts/update/%(uid)s', ['repo_name', 'uid']);
397 pyroutes.register('repo_artifacts_update', '/%(repo_name)s/artifacts/update/%(uid)s', ['repo_name', 'uid']);
397 pyroutes.register('repo_automation', '/%(repo_name)s/settings/automation', ['repo_name']);
398 pyroutes.register('repo_automation', '/%(repo_name)s/settings/automation', ['repo_name']);
398 pyroutes.register('repo_automation_update', '/%(repo_name)s/settings/automation/%(entry_id)s/update', ['repo_name', 'entry_id']);
399 pyroutes.register('repo_automation_update', '/%(repo_name)s/settings/automation/%(entry_id)s/update', ['repo_name', 'entry_id']);
399 pyroutes.register('edit_repo_remote_push', '/%(repo_name)s/settings/remote/push', ['repo_name']);
400 pyroutes.register('edit_repo_remote_push', '/%(repo_name)s/settings/remote/push', ['repo_name']);
400 pyroutes.register('edit_repo_perms_branch', '/%(repo_name)s/settings/branch_permissions', ['repo_name']);
401 pyroutes.register('edit_repo_perms_branch', '/%(repo_name)s/settings/branch_permissions', ['repo_name']);
401 pyroutes.register('edit_repo_perms_branch_delete', '/%(repo_name)s/settings/branch_permissions/%(rule_id)s/delete', ['repo_name', 'rule_id']);
402 pyroutes.register('edit_repo_perms_branch_delete', '/%(repo_name)s/settings/branch_permissions/%(rule_id)s/delete', ['repo_name', 'rule_id']);
402 }
403 }
@@ -1,1450 +1,1473 b''
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 var firefoxAnchorFix = function() {
19 var firefoxAnchorFix = function() {
20 // hack to make anchor links behave properly on firefox, in our inline
20 // hack to make anchor links behave properly on firefox, in our inline
21 // comments generation when comments are injected firefox is misbehaving
21 // comments generation when comments are injected firefox is misbehaving
22 // when jumping to anchor links
22 // when jumping to anchor links
23 if (location.href.indexOf('#') > -1) {
23 if (location.href.indexOf('#') > -1) {
24 location.href += '';
24 location.href += '';
25 }
25 }
26 };
26 };
27
27
28 var linkifyComments = function(comments) {
28 var linkifyComments = function(comments) {
29 var firstCommentId = null;
29 var firstCommentId = null;
30 if (comments) {
30 if (comments) {
31 firstCommentId = $(comments[0]).data('comment-id');
31 firstCommentId = $(comments[0]).data('comment-id');
32 }
32 }
33
33
34 if (firstCommentId){
34 if (firstCommentId){
35 $('#inline-comments-counter').attr('href', '#comment-' + firstCommentId);
35 $('#inline-comments-counter').attr('href', '#comment-' + firstCommentId);
36 }
36 }
37 };
37 };
38
38
39 var bindToggleButtons = function() {
39 var bindToggleButtons = function() {
40 $('.comment-toggle').on('click', function() {
40 $('.comment-toggle').on('click', function() {
41 $(this).parent().nextUntil('tr.line').toggle('inline-comments');
41 $(this).parent().nextUntil('tr.line').toggle('inline-comments');
42 });
42 });
43 };
43 };
44
44
45
45
46
46
47 var _submitAjaxPOST = function(url, postData, successHandler, failHandler) {
47 var _submitAjaxPOST = function(url, postData, successHandler, failHandler) {
48 failHandler = failHandler || function() {};
48 failHandler = failHandler || function() {};
49 postData = toQueryString(postData);
49 postData = toQueryString(postData);
50 var request = $.ajax({
50 var request = $.ajax({
51 url: url,
51 url: url,
52 type: 'POST',
52 type: 'POST',
53 data: postData,
53 data: postData,
54 headers: {'X-PARTIAL-XHR': true}
54 headers: {'X-PARTIAL-XHR': true}
55 })
55 })
56 .done(function (data) {
56 .done(function (data) {
57 successHandler(data);
57 successHandler(data);
58 })
58 })
59 .fail(function (data, textStatus, errorThrown) {
59 .fail(function (data, textStatus, errorThrown) {
60 failHandler(data, textStatus, errorThrown)
60 failHandler(data, textStatus, errorThrown)
61 });
61 });
62 return request;
62 return request;
63 };
63 };
64
64
65
65
66
66
67
67
68 /* Comment form for main and inline comments */
68 /* Comment form for main and inline comments */
69 (function(mod) {
69 (function(mod) {
70
70
71 if (typeof exports == "object" && typeof module == "object") {
71 if (typeof exports == "object" && typeof module == "object") {
72 // CommonJS
72 // CommonJS
73 module.exports = mod();
73 module.exports = mod();
74 }
74 }
75 else {
75 else {
76 // Plain browser env
76 // Plain browser env
77 (this || window).CommentForm = mod();
77 (this || window).CommentForm = mod();
78 }
78 }
79
79
80 })(function() {
80 })(function() {
81 "use strict";
81 "use strict";
82
82
83 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId, edit, comment_id) {
83 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId, edit, comment_id) {
84
84
85 if (!(this instanceof CommentForm)) {
85 if (!(this instanceof CommentForm)) {
86 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId, edit, comment_id);
86 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId, edit, comment_id);
87 }
87 }
88
88
89 // bind the element instance to our Form
89 // bind the element instance to our Form
90 $(formElement).get(0).CommentForm = this;
90 $(formElement).get(0).CommentForm = this;
91
91
92 this.withLineNo = function(selector) {
92 this.withLineNo = function(selector) {
93 var lineNo = this.lineNo;
93 var lineNo = this.lineNo;
94 if (lineNo === undefined) {
94 if (lineNo === undefined) {
95 return selector
95 return selector
96 } else {
96 } else {
97 return selector + '_' + lineNo;
97 return selector + '_' + lineNo;
98 }
98 }
99 };
99 };
100
100
101 this.commitId = commitId;
101 this.commitId = commitId;
102 this.pullRequestId = pullRequestId;
102 this.pullRequestId = pullRequestId;
103 this.lineNo = lineNo;
103 this.lineNo = lineNo;
104 this.initAutocompleteActions = initAutocompleteActions;
104 this.initAutocompleteActions = initAutocompleteActions;
105
105
106 this.previewButton = this.withLineNo('#preview-btn');
106 this.previewButton = this.withLineNo('#preview-btn');
107 this.previewContainer = this.withLineNo('#preview-container');
107 this.previewContainer = this.withLineNo('#preview-container');
108
108
109 this.previewBoxSelector = this.withLineNo('#preview-box');
109 this.previewBoxSelector = this.withLineNo('#preview-box');
110
110
111 this.editButton = this.withLineNo('#edit-btn');
111 this.editButton = this.withLineNo('#edit-btn');
112 this.editContainer = this.withLineNo('#edit-container');
112 this.editContainer = this.withLineNo('#edit-container');
113 this.cancelButton = this.withLineNo('#cancel-btn');
113 this.cancelButton = this.withLineNo('#cancel-btn');
114 this.commentType = this.withLineNo('#comment_type');
114 this.commentType = this.withLineNo('#comment_type');
115
115
116 this.resolvesId = null;
116 this.resolvesId = null;
117 this.resolvesActionId = null;
117 this.resolvesActionId = null;
118
118
119 this.closesPr = '#close_pull_request';
119 this.closesPr = '#close_pull_request';
120
120
121 this.cmBox = this.withLineNo('#text');
121 this.cmBox = this.withLineNo('#text');
122 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
122 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
123
123
124 this.statusChange = this.withLineNo('#change_status');
124 this.statusChange = this.withLineNo('#change_status');
125
125
126 this.submitForm = formElement;
126 this.submitForm = formElement;
127
127
128 this.submitButton = $(this.submitForm).find('.submit-comment-action');
128 this.submitButton = $(this.submitForm).find('.submit-comment-action');
129 this.submitButtonText = this.submitButton.val();
129 this.submitButtonText = this.submitButton.val();
130
130
131 this.submitDraftButton = $(this.submitForm).find('.submit-draft-action');
131 this.submitDraftButton = $(this.submitForm).find('.submit-draft-action');
132 this.submitDraftButtonText = this.submitDraftButton.val();
132 this.submitDraftButtonText = this.submitDraftButton.val();
133
133
134 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
134 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
135 {'repo_name': templateContext.repo_name,
135 {'repo_name': templateContext.repo_name,
136 'commit_id': templateContext.commit_data.commit_id});
136 'commit_id': templateContext.commit_data.commit_id});
137
137
138 if (edit){
138 if (edit){
139 this.submitDraftButton.hide();
139 this.submitDraftButton.hide();
140 this.submitButtonText = _gettext('Update Comment');
140 this.submitButtonText = _gettext('Update Comment');
141 $(this.commentType).prop('disabled', true);
141 $(this.commentType).prop('disabled', true);
142 $(this.commentType).addClass('disabled');
142 $(this.commentType).addClass('disabled');
143 var editInfo =
143 var editInfo =
144 '';
144 '';
145 $(editInfo).insertBefore($(this.editButton).parent());
145 $(editInfo).insertBefore($(this.editButton).parent());
146 }
146 }
147
147
148 if (resolvesCommentId){
148 if (resolvesCommentId){
149 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
149 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
150 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
150 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
151 $(this.commentType).prop('disabled', true);
151 $(this.commentType).prop('disabled', true);
152 $(this.commentType).addClass('disabled');
152 $(this.commentType).addClass('disabled');
153
153
154 // disable select
154 // disable select
155 setTimeout(function() {
155 setTimeout(function() {
156 $(self.statusChange).select2('readonly', true);
156 $(self.statusChange).select2('readonly', true);
157 }, 10);
157 }, 10);
158
158
159 var resolvedInfo = (
159 var resolvedInfo = (
160 '<li class="resolve-action">' +
160 '<li class="resolve-action">' +
161 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
161 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
162 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
162 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
163 '</li>'
163 '</li>'
164 ).format(resolvesCommentId, _gettext('resolve comment'));
164 ).format(resolvesCommentId, _gettext('resolve comment'));
165 $(resolvedInfo).insertAfter($(this.commentType).parent());
165 $(resolvedInfo).insertAfter($(this.commentType).parent());
166 }
166 }
167
167
168 // based on commitId, or pullRequestId decide where do we submit
168 // based on commitId, or pullRequestId decide where do we submit
169 // out data
169 // out data
170 if (this.commitId){
170 if (this.commitId){
171 var pyurl = 'repo_commit_comment_create';
171 var pyurl = 'repo_commit_comment_create';
172 if(edit){
172 if(edit){
173 pyurl = 'repo_commit_comment_edit';
173 pyurl = 'repo_commit_comment_edit';
174 }
174 }
175 this.submitUrl = pyroutes.url(pyurl,
175 this.submitUrl = pyroutes.url(pyurl,
176 {'repo_name': templateContext.repo_name,
176 {'repo_name': templateContext.repo_name,
177 'commit_id': this.commitId,
177 'commit_id': this.commitId,
178 'comment_id': comment_id});
178 'comment_id': comment_id});
179 this.selfUrl = pyroutes.url('repo_commit',
179 this.selfUrl = pyroutes.url('repo_commit',
180 {'repo_name': templateContext.repo_name,
180 {'repo_name': templateContext.repo_name,
181 'commit_id': this.commitId});
181 'commit_id': this.commitId});
182
182
183 } else if (this.pullRequestId) {
183 } else if (this.pullRequestId) {
184 var pyurl = 'pullrequest_comment_create';
184 var pyurl = 'pullrequest_comment_create';
185 if(edit){
185 if(edit){
186 pyurl = 'pullrequest_comment_edit';
186 pyurl = 'pullrequest_comment_edit';
187 }
187 }
188 this.submitUrl = pyroutes.url(pyurl,
188 this.submitUrl = pyroutes.url(pyurl,
189 {'repo_name': templateContext.repo_name,
189 {'repo_name': templateContext.repo_name,
190 'pull_request_id': this.pullRequestId,
190 'pull_request_id': this.pullRequestId,
191 'comment_id': comment_id});
191 'comment_id': comment_id});
192 this.selfUrl = pyroutes.url('pullrequest_show',
192 this.selfUrl = pyroutes.url('pullrequest_show',
193 {'repo_name': templateContext.repo_name,
193 {'repo_name': templateContext.repo_name,
194 'pull_request_id': this.pullRequestId});
194 'pull_request_id': this.pullRequestId});
195
195
196 } else {
196 } else {
197 throw new Error(
197 throw new Error(
198 'CommentForm requires pullRequestId, or commitId to be specified.')
198 'CommentForm requires pullRequestId, or commitId to be specified.')
199 }
199 }
200
200
201 // FUNCTIONS and helpers
201 // FUNCTIONS and helpers
202 var self = this;
202 var self = this;
203
203
204 this.isInline = function(){
204 this.isInline = function(){
205 return this.lineNo && this.lineNo != 'general';
205 return this.lineNo && this.lineNo != 'general';
206 };
206 };
207
207
208 this.getCmInstance = function(){
208 this.getCmInstance = function(){
209 return this.cm
209 return this.cm
210 };
210 };
211
211
212 this.setPlaceholder = function(placeholder) {
212 this.setPlaceholder = function(placeholder) {
213 var cm = this.getCmInstance();
213 var cm = this.getCmInstance();
214 if (cm){
214 if (cm){
215 cm.setOption('placeholder', placeholder);
215 cm.setOption('placeholder', placeholder);
216 }
216 }
217 };
217 };
218
218
219 this.getCommentStatus = function() {
219 this.getCommentStatus = function() {
220 return $(this.submitForm).find(this.statusChange).val();
220 return $(this.submitForm).find(this.statusChange).val();
221 };
221 };
222
222
223 this.getCommentType = function() {
223 this.getCommentType = function() {
224 return $(this.submitForm).find(this.commentType).val();
224 return $(this.submitForm).find(this.commentType).val();
225 };
225 };
226
226
227 this.getDraftState = function () {
227 this.getDraftState = function () {
228 var submitterElem = $(this.submitForm).find('input[type="submit"].submitter');
228 var submitterElem = $(this.submitForm).find('input[type="submit"].submitter');
229 var data = $(submitterElem).data('isDraft');
229 var data = $(submitterElem).data('isDraft');
230 return data
230 return data
231 }
231 }
232
232
233 this.getResolvesId = function() {
233 this.getResolvesId = function() {
234 return $(this.submitForm).find(this.resolvesId).val() || null;
234 return $(this.submitForm).find(this.resolvesId).val() || null;
235 };
235 };
236
236
237 this.getClosePr = function() {
237 this.getClosePr = function() {
238 return $(this.submitForm).find(this.closesPr).val() || null;
238 return $(this.submitForm).find(this.closesPr).val() || null;
239 };
239 };
240
240
241 this.markCommentResolved = function(resolvedCommentId){
241 this.markCommentResolved = function(resolvedCommentId){
242 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
242 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
243 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
243 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
244 };
244 };
245
245
246 this.isAllowedToSubmit = function() {
246 this.isAllowedToSubmit = function() {
247 var commentDisabled = $(this.submitButton).prop('disabled');
247 var commentDisabled = $(this.submitButton).prop('disabled');
248 var draftDisabled = $(this.submitDraftButton).prop('disabled');
248 var draftDisabled = $(this.submitDraftButton).prop('disabled');
249 return !commentDisabled && !draftDisabled;
249 return !commentDisabled && !draftDisabled;
250 };
250 };
251
251
252 this.initStatusChangeSelector = function(){
252 this.initStatusChangeSelector = function(){
253 var formatChangeStatus = function(state, escapeMarkup) {
253 var formatChangeStatus = function(state, escapeMarkup) {
254 var originalOption = state.element;
254 var originalOption = state.element;
255 var tmpl = '<i class="icon-circle review-status-{0}"></i><span>{1}</span>'.format($(originalOption).data('status'), escapeMarkup(state.text));
255 var tmpl = '<i class="icon-circle review-status-{0}"></i><span>{1}</span>'.format($(originalOption).data('status'), escapeMarkup(state.text));
256 return tmpl
256 return tmpl
257 };
257 };
258 var formatResult = function(result, container, query, escapeMarkup) {
258 var formatResult = function(result, container, query, escapeMarkup) {
259 return formatChangeStatus(result, escapeMarkup);
259 return formatChangeStatus(result, escapeMarkup);
260 };
260 };
261
261
262 var formatSelection = function(data, container, escapeMarkup) {
262 var formatSelection = function(data, container, escapeMarkup) {
263 return formatChangeStatus(data, escapeMarkup);
263 return formatChangeStatus(data, escapeMarkup);
264 };
264 };
265
265
266 $(this.submitForm).find(this.statusChange).select2({
266 $(this.submitForm).find(this.statusChange).select2({
267 placeholder: _gettext('Status Review'),
267 placeholder: _gettext('Status Review'),
268 formatResult: formatResult,
268 formatResult: formatResult,
269 formatSelection: formatSelection,
269 formatSelection: formatSelection,
270 containerCssClass: "drop-menu status_box_menu",
270 containerCssClass: "drop-menu status_box_menu",
271 dropdownCssClass: "drop-menu-dropdown",
271 dropdownCssClass: "drop-menu-dropdown",
272 dropdownAutoWidth: true,
272 dropdownAutoWidth: true,
273 minimumResultsForSearch: -1
273 minimumResultsForSearch: -1
274 });
274 });
275
275
276 $(this.submitForm).find(this.statusChange).on('change', function() {
276 $(this.submitForm).find(this.statusChange).on('change', function() {
277 var status = self.getCommentStatus();
277 var status = self.getCommentStatus();
278
278
279 if (status && !self.isInline()) {
279 if (status && !self.isInline()) {
280 $(self.submitButton).prop('disabled', false);
280 $(self.submitButton).prop('disabled', false);
281 $(self.submitDraftButton).prop('disabled', false);
281 $(self.submitDraftButton).prop('disabled', false);
282 }
282 }
283
283
284 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
284 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
285 self.setPlaceholder(placeholderText)
285 self.setPlaceholder(placeholderText)
286 })
286 })
287 };
287 };
288
288
289 // reset the comment form into it's original state
289 // reset the comment form into it's original state
290 this.resetCommentFormState = function(content) {
290 this.resetCommentFormState = function(content) {
291 content = content || '';
291 content = content || '';
292
292
293 $(this.editContainer).show();
293 $(this.editContainer).show();
294 $(this.editButton).parent().addClass('active');
294 $(this.editButton).parent().addClass('active');
295
295
296 $(this.previewContainer).hide();
296 $(this.previewContainer).hide();
297 $(this.previewButton).parent().removeClass('active');
297 $(this.previewButton).parent().removeClass('active');
298
298
299 this.setActionButtonsDisabled(true);
299 this.setActionButtonsDisabled(true);
300 self.cm.setValue(content);
300 self.cm.setValue(content);
301 self.cm.setOption("readOnly", false);
301 self.cm.setOption("readOnly", false);
302
302
303 if (this.resolvesId) {
303 if (this.resolvesId) {
304 // destroy the resolve action
304 // destroy the resolve action
305 $(this.resolvesId).parent().remove();
305 $(this.resolvesId).parent().remove();
306 }
306 }
307 // reset closingPR flag
307 // reset closingPR flag
308 $('.close-pr-input').remove();
308 $('.close-pr-input').remove();
309
309
310 $(this.statusChange).select2('readonly', false);
310 $(this.statusChange).select2('readonly', false);
311 };
311 };
312
312
313 this.globalSubmitSuccessCallback = function(comment){
313 this.globalSubmitSuccessCallback = function(comment){
314 // default behaviour is to call GLOBAL hook, if it's registered.
314 // default behaviour is to call GLOBAL hook, if it's registered.
315 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
315 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
316 commentFormGlobalSubmitSuccessCallback(comment);
316 commentFormGlobalSubmitSuccessCallback(comment);
317 }
317 }
318 };
318 };
319
319
320 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
320 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
321 return _submitAjaxPOST(url, postData, successHandler, failHandler);
321 return _submitAjaxPOST(url, postData, successHandler, failHandler);
322 };
322 };
323
323
324 // overwrite a submitHandler, we need to do it for inline comments
324 // overwrite a submitHandler, we need to do it for inline comments
325 this.setHandleFormSubmit = function(callback) {
325 this.setHandleFormSubmit = function(callback) {
326 this.handleFormSubmit = callback;
326 this.handleFormSubmit = callback;
327 };
327 };
328
328
329 // overwrite a submitSuccessHandler
329 // overwrite a submitSuccessHandler
330 this.setGlobalSubmitSuccessCallback = function(callback) {
330 this.setGlobalSubmitSuccessCallback = function(callback) {
331 this.globalSubmitSuccessCallback = callback;
331 this.globalSubmitSuccessCallback = callback;
332 };
332 };
333
333
334 // default handler for for submit for main comments
334 // default handler for for submit for main comments
335 this.handleFormSubmit = function() {
335 this.handleFormSubmit = function() {
336 var text = self.cm.getValue();
336 var text = self.cm.getValue();
337 var status = self.getCommentStatus();
337 var status = self.getCommentStatus();
338 var commentType = self.getCommentType();
338 var commentType = self.getCommentType();
339 var isDraft = self.getDraftState();
339 var isDraft = self.getDraftState();
340 var resolvesCommentId = self.getResolvesId();
340 var resolvesCommentId = self.getResolvesId();
341 var closePullRequest = self.getClosePr();
341 var closePullRequest = self.getClosePr();
342
342
343 if (text === "" && !status) {
343 if (text === "" && !status) {
344 return;
344 return;
345 }
345 }
346
346
347 var excludeCancelBtn = false;
347 var excludeCancelBtn = false;
348 var submitEvent = true;
348 var submitEvent = true;
349 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
349 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
350 self.cm.setOption("readOnly", true);
350 self.cm.setOption("readOnly", true);
351
351
352 var postData = {
352 var postData = {
353 'text': text,
353 'text': text,
354 'changeset_status': status,
354 'changeset_status': status,
355 'comment_type': commentType,
355 'comment_type': commentType,
356 'csrf_token': CSRF_TOKEN
356 'csrf_token': CSRF_TOKEN
357 };
357 };
358
358
359 if (resolvesCommentId) {
359 if (resolvesCommentId) {
360 postData['resolves_comment_id'] = resolvesCommentId;
360 postData['resolves_comment_id'] = resolvesCommentId;
361 }
361 }
362
362
363 if (closePullRequest) {
363 if (closePullRequest) {
364 postData['close_pull_request'] = true;
364 postData['close_pull_request'] = true;
365 }
365 }
366
366
367 // submitSuccess for general comments
367 // submitSuccess for general comments
368 var submitSuccessCallback = function(json_data) {
368 var submitSuccessCallback = function(json_data) {
369 // reload page if we change status for single commit.
369 // reload page if we change status for single commit.
370 if (status && self.commitId) {
370 if (status && self.commitId) {
371 location.reload(true);
371 location.reload(true);
372 } else {
372 } else {
373 // inject newly created comments, json_data is {<comment_id>: {}}
373 // inject newly created comments, json_data is {<comment_id>: {}}
374 self.attachGeneralComment(json_data)
374 self.attachGeneralComment(json_data)
375
375
376 self.resetCommentFormState();
376 self.resetCommentFormState();
377 timeagoActivate();
377 timeagoActivate();
378 tooltipActivate();
378 tooltipActivate();
379
379
380 // mark visually which comment was resolved
380 // mark visually which comment was resolved
381 if (resolvesCommentId) {
381 if (resolvesCommentId) {
382 self.markCommentResolved(resolvesCommentId);
382 self.markCommentResolved(resolvesCommentId);
383 }
383 }
384 }
384 }
385
385
386 // run global callback on submit
386 // run global callback on submit
387 self.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id});
387 self.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id});
388
388
389 };
389 };
390 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
390 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
391 var prefix = "Error while submitting comment.\n"
391 var prefix = "Error while submitting comment.\n"
392 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
392 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
393 ajaxErrorSwal(message);
393 ajaxErrorSwal(message);
394 self.resetCommentFormState(text);
394 self.resetCommentFormState(text);
395 };
395 };
396 self.submitAjaxPOST(
396 self.submitAjaxPOST(
397 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
397 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
398 };
398 };
399
399
400 this.previewSuccessCallback = function(o) {
400 this.previewSuccessCallback = function(o) {
401 $(self.previewBoxSelector).html(o);
401 $(self.previewBoxSelector).html(o);
402 $(self.previewBoxSelector).removeClass('unloaded');
402 $(self.previewBoxSelector).removeClass('unloaded');
403
403
404 // swap buttons, making preview active
404 // swap buttons, making preview active
405 $(self.previewButton).parent().addClass('active');
405 $(self.previewButton).parent().addClass('active');
406 $(self.editButton).parent().removeClass('active');
406 $(self.editButton).parent().removeClass('active');
407
407
408 // unlock buttons
408 // unlock buttons
409 self.setActionButtonsDisabled(false);
409 self.setActionButtonsDisabled(false);
410 };
410 };
411
411
412 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
412 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
413 excludeCancelBtn = excludeCancelBtn || false;
413 excludeCancelBtn = excludeCancelBtn || false;
414 submitEvent = submitEvent || false;
414 submitEvent = submitEvent || false;
415
415
416 $(this.editButton).prop('disabled', state);
416 $(this.editButton).prop('disabled', state);
417 $(this.previewButton).prop('disabled', state);
417 $(this.previewButton).prop('disabled', state);
418
418
419 if (!excludeCancelBtn) {
419 if (!excludeCancelBtn) {
420 $(this.cancelButton).prop('disabled', state);
420 $(this.cancelButton).prop('disabled', state);
421 }
421 }
422
422
423 var submitState = state;
423 var submitState = state;
424 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
424 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
425 // if the value of commit review status is set, we allow
425 // if the value of commit review status is set, we allow
426 // submit button, but only on Main form, isInline means inline
426 // submit button, but only on Main form, isInline means inline
427 submitState = false
427 submitState = false
428 }
428 }
429
429
430 $(this.submitButton).prop('disabled', submitState);
430 $(this.submitButton).prop('disabled', submitState);
431 $(this.submitDraftButton).prop('disabled', submitState);
431 $(this.submitDraftButton).prop('disabled', submitState);
432
432
433 if (submitEvent) {
433 if (submitEvent) {
434 var isDraft = self.getDraftState();
434 var isDraft = self.getDraftState();
435
435
436 if (isDraft) {
436 if (isDraft) {
437 $(this.submitDraftButton).val(_gettext('Saving Draft...'));
437 $(this.submitDraftButton).val(_gettext('Saving Draft...'));
438 } else {
438 } else {
439 $(this.submitButton).val(_gettext('Submitting...'));
439 $(this.submitButton).val(_gettext('Submitting...'));
440 }
440 }
441
441
442 } else {
442 } else {
443 $(this.submitButton).val(this.submitButtonText);
443 $(this.submitButton).val(this.submitButtonText);
444 $(this.submitDraftButton).val(this.submitDraftButtonText);
444 $(this.submitDraftButton).val(this.submitDraftButtonText);
445 }
445 }
446
446
447 };
447 };
448
448
449 // lock preview/edit/submit buttons on load, but exclude cancel button
449 // lock preview/edit/submit buttons on load, but exclude cancel button
450 var excludeCancelBtn = true;
450 var excludeCancelBtn = true;
451 this.setActionButtonsDisabled(true, excludeCancelBtn);
451 this.setActionButtonsDisabled(true, excludeCancelBtn);
452
452
453 // anonymous users don't have access to initialized CM instance
453 // anonymous users don't have access to initialized CM instance
454 if (this.cm !== undefined){
454 if (this.cm !== undefined){
455 this.cm.on('change', function(cMirror) {
455 this.cm.on('change', function(cMirror) {
456 if (cMirror.getValue() === "") {
456 if (cMirror.getValue() === "") {
457 self.setActionButtonsDisabled(true, excludeCancelBtn)
457 self.setActionButtonsDisabled(true, excludeCancelBtn)
458 } else {
458 } else {
459 self.setActionButtonsDisabled(false, excludeCancelBtn)
459 self.setActionButtonsDisabled(false, excludeCancelBtn)
460 }
460 }
461 });
461 });
462 }
462 }
463
463
464 $(this.editButton).on('click', function(e) {
464 $(this.editButton).on('click', function(e) {
465 e.preventDefault();
465 e.preventDefault();
466
466
467 $(self.previewButton).parent().removeClass('active');
467 $(self.previewButton).parent().removeClass('active');
468 $(self.previewContainer).hide();
468 $(self.previewContainer).hide();
469
469
470 $(self.editButton).parent().addClass('active');
470 $(self.editButton).parent().addClass('active');
471 $(self.editContainer).show();
471 $(self.editContainer).show();
472
472
473 });
473 });
474
474
475 $(this.previewButton).on('click', function(e) {
475 $(this.previewButton).on('click', function(e) {
476 e.preventDefault();
476 e.preventDefault();
477 var text = self.cm.getValue();
477 var text = self.cm.getValue();
478
478
479 if (text === "") {
479 if (text === "") {
480 return;
480 return;
481 }
481 }
482
482
483 var postData = {
483 var postData = {
484 'text': text,
484 'text': text,
485 'renderer': templateContext.visual.default_renderer,
485 'renderer': templateContext.visual.default_renderer,
486 'csrf_token': CSRF_TOKEN
486 'csrf_token': CSRF_TOKEN
487 };
487 };
488
488
489 // lock ALL buttons on preview
489 // lock ALL buttons on preview
490 self.setActionButtonsDisabled(true);
490 self.setActionButtonsDisabled(true);
491
491
492 $(self.previewBoxSelector).addClass('unloaded');
492 $(self.previewBoxSelector).addClass('unloaded');
493 $(self.previewBoxSelector).html(_gettext('Loading ...'));
493 $(self.previewBoxSelector).html(_gettext('Loading ...'));
494
494
495 $(self.editContainer).hide();
495 $(self.editContainer).hide();
496 $(self.previewContainer).show();
496 $(self.previewContainer).show();
497
497
498 // by default we reset state of comment preserving the text
498 // by default we reset state of comment preserving the text
499 var previewFailCallback = function(jqXHR, textStatus, errorThrown) {
499 var previewFailCallback = function(jqXHR, textStatus, errorThrown) {
500 var prefix = "Error while preview of comment.\n"
500 var prefix = "Error while preview of comment.\n"
501 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
501 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
502 ajaxErrorSwal(message);
502 ajaxErrorSwal(message);
503
503
504 self.resetCommentFormState(text)
504 self.resetCommentFormState(text)
505 };
505 };
506 self.submitAjaxPOST(
506 self.submitAjaxPOST(
507 self.previewUrl, postData, self.previewSuccessCallback,
507 self.previewUrl, postData, self.previewSuccessCallback,
508 previewFailCallback);
508 previewFailCallback);
509
509
510 $(self.previewButton).parent().addClass('active');
510 $(self.previewButton).parent().addClass('active');
511 $(self.editButton).parent().removeClass('active');
511 $(self.editButton).parent().removeClass('active');
512 });
512 });
513
513
514 $(this.submitForm).submit(function(e) {
514 $(this.submitForm).submit(function(e) {
515 e.preventDefault();
515 e.preventDefault();
516 var allowedToSubmit = self.isAllowedToSubmit();
516 var allowedToSubmit = self.isAllowedToSubmit();
517 if (!allowedToSubmit){
517 if (!allowedToSubmit){
518 return false;
518 return false;
519 }
519 }
520
520
521 self.handleFormSubmit();
521 self.handleFormSubmit();
522 });
522 });
523
523
524 }
524 }
525
525
526 return CommentForm;
526 return CommentForm;
527 });
527 });
528
528
529 /* selector for comment versions */
529 /* selector for comment versions */
530 var initVersionSelector = function(selector, initialData) {
530 var initVersionSelector = function(selector, initialData) {
531
531
532 var formatResult = function(result, container, query, escapeMarkup) {
532 var formatResult = function(result, container, query, escapeMarkup) {
533
533
534 return renderTemplate('commentVersion', {
534 return renderTemplate('commentVersion', {
535 show_disabled: true,
535 show_disabled: true,
536 version: result.comment_version,
536 version: result.comment_version,
537 user_name: result.comment_author_username,
537 user_name: result.comment_author_username,
538 gravatar_url: result.comment_author_gravatar,
538 gravatar_url: result.comment_author_gravatar,
539 size: 16,
539 size: 16,
540 timeago_component: result.comment_created_on,
540 timeago_component: result.comment_created_on,
541 })
541 })
542 };
542 };
543
543
544 $(selector).select2({
544 $(selector).select2({
545 placeholder: "Edited",
545 placeholder: "Edited",
546 containerCssClass: "drop-menu-comment-history",
546 containerCssClass: "drop-menu-comment-history",
547 dropdownCssClass: "drop-menu-dropdown",
547 dropdownCssClass: "drop-menu-dropdown",
548 dropdownAutoWidth: true,
548 dropdownAutoWidth: true,
549 minimumResultsForSearch: -1,
549 minimumResultsForSearch: -1,
550 data: initialData,
550 data: initialData,
551 formatResult: formatResult,
551 formatResult: formatResult,
552 });
552 });
553
553
554 $(selector).on('select2-selecting', function (e) {
554 $(selector).on('select2-selecting', function (e) {
555 // hide the mast as we later do preventDefault()
555 // hide the mast as we later do preventDefault()
556 $("#select2-drop-mask").click();
556 $("#select2-drop-mask").click();
557 e.preventDefault();
557 e.preventDefault();
558 e.choice.action();
558 e.choice.action();
559 });
559 });
560
560
561 $(selector).on("select2-open", function() {
561 $(selector).on("select2-open", function() {
562 timeagoActivate();
562 timeagoActivate();
563 });
563 });
564 };
564 };
565
565
566 /* comments controller */
566 /* comments controller */
567 var CommentsController = function() {
567 var CommentsController = function() {
568 var mainComment = '#text';
568 var mainComment = '#text';
569 var self = this;
569 var self = this;
570
570
571 this.showVersion = function (comment_id, comment_history_id) {
571 this.showVersion = function (comment_id, comment_history_id) {
572
572
573 var historyViewUrl = pyroutes.url(
573 var historyViewUrl = pyroutes.url(
574 'repo_commit_comment_history_view',
574 'repo_commit_comment_history_view',
575 {
575 {
576 'repo_name': templateContext.repo_name,
576 'repo_name': templateContext.repo_name,
577 'commit_id': comment_id,
577 'commit_id': comment_id,
578 'comment_history_id': comment_history_id,
578 'comment_history_id': comment_history_id,
579 }
579 }
580 );
580 );
581 successRenderCommit = function (data) {
581 successRenderCommit = function (data) {
582 SwalNoAnimation.fire({
582 SwalNoAnimation.fire({
583 html: data,
583 html: data,
584 title: '',
584 title: '',
585 });
585 });
586 };
586 };
587 failRenderCommit = function () {
587 failRenderCommit = function () {
588 SwalNoAnimation.fire({
588 SwalNoAnimation.fire({
589 html: 'Error while loading comment history',
589 html: 'Error while loading comment history',
590 title: '',
590 title: '',
591 });
591 });
592 };
592 };
593 _submitAjaxPOST(
593 _submitAjaxPOST(
594 historyViewUrl, {'csrf_token': CSRF_TOKEN},
594 historyViewUrl, {'csrf_token': CSRF_TOKEN},
595 successRenderCommit,
595 successRenderCommit,
596 failRenderCommit
596 failRenderCommit
597 );
597 );
598 };
598 };
599
599
600 this.getLineNumber = function(node) {
600 this.getLineNumber = function(node) {
601 var $node = $(node);
601 var $node = $(node);
602 var lineNo = $node.closest('td').attr('data-line-no');
602 var lineNo = $node.closest('td').attr('data-line-no');
603 if (lineNo === undefined && $node.data('commentInline')){
603 if (lineNo === undefined && $node.data('commentInline')){
604 lineNo = $node.data('commentLineNo')
604 lineNo = $node.data('commentLineNo')
605 }
605 }
606
606
607 return lineNo
607 return lineNo
608 };
608 };
609
609
610 this.scrollToComment = function(node, offset, outdated) {
610 this.scrollToComment = function(node, offset, outdated) {
611 if (offset === undefined) {
611 if (offset === undefined) {
612 offset = 0;
612 offset = 0;
613 }
613 }
614 var outdated = outdated || false;
614 var outdated = outdated || false;
615 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
615 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
616
616
617 if (!node) {
617 if (!node) {
618 node = $('.comment-selected');
618 node = $('.comment-selected');
619 if (!node.length) {
619 if (!node.length) {
620 node = $('comment-current')
620 node = $('comment-current')
621 }
621 }
622 }
622 }
623
623
624 $wrapper = $(node).closest('div.comment');
624 $wrapper = $(node).closest('div.comment');
625
625
626 // show hidden comment when referenced.
626 // show hidden comment when referenced.
627 if (!$wrapper.is(':visible')){
627 if (!$wrapper.is(':visible')){
628 $wrapper.show();
628 $wrapper.show();
629 }
629 }
630
630
631 $comment = $(node).closest(klass);
631 $comment = $(node).closest(klass);
632 $comments = $(klass);
632 $comments = $(klass);
633
633
634 $('.comment-selected').removeClass('comment-selected');
634 $('.comment-selected').removeClass('comment-selected');
635
635
636 var nextIdx = $(klass).index($comment) + offset;
636 var nextIdx = $(klass).index($comment) + offset;
637 if (nextIdx >= $comments.length) {
637 if (nextIdx >= $comments.length) {
638 nextIdx = 0;
638 nextIdx = 0;
639 }
639 }
640 var $next = $(klass).eq(nextIdx);
640 var $next = $(klass).eq(nextIdx);
641
641
642 var $cb = $next.closest('.cb');
642 var $cb = $next.closest('.cb');
643 $cb.removeClass('cb-collapsed');
643 $cb.removeClass('cb-collapsed');
644
644
645 var $filediffCollapseState = $cb.closest('.filediff').prev();
645 var $filediffCollapseState = $cb.closest('.filediff').prev();
646 $filediffCollapseState.prop('checked', false);
646 $filediffCollapseState.prop('checked', false);
647 $next.addClass('comment-selected');
647 $next.addClass('comment-selected');
648 scrollToElement($next);
648 scrollToElement($next);
649 return false;
649 return false;
650 };
650 };
651
651
652 this.nextComment = function(node) {
652 this.nextComment = function(node) {
653 return self.scrollToComment(node, 1);
653 return self.scrollToComment(node, 1);
654 };
654 };
655
655
656 this.prevComment = function(node) {
656 this.prevComment = function(node) {
657 return self.scrollToComment(node, -1);
657 return self.scrollToComment(node, -1);
658 };
658 };
659
659
660 this.nextOutdatedComment = function(node) {
660 this.nextOutdatedComment = function(node) {
661 return self.scrollToComment(node, 1, true);
661 return self.scrollToComment(node, 1, true);
662 };
662 };
663
663
664 this.prevOutdatedComment = function(node) {
664 this.prevOutdatedComment = function(node) {
665 return self.scrollToComment(node, -1, true);
665 return self.scrollToComment(node, -1, true);
666 };
666 };
667
667
668 this.cancelComment = function (node) {
668 this.cancelComment = function (node) {
669 var $node = $(node);
669 var $node = $(node);
670 var edit = $(this).attr('edit');
670 var edit = $(this).attr('edit');
671 var $inlineComments = $node.closest('div.inline-comments');
671 var $inlineComments = $node.closest('div.inline-comments');
672
672
673 if (edit) {
673 if (edit) {
674 var $general_comments = null;
674 var $general_comments = null;
675 if (!$inlineComments.length) {
675 if (!$inlineComments.length) {
676 $general_comments = $('#comments');
676 $general_comments = $('#comments');
677 var $comment = $general_comments.parent().find('div.comment:hidden');
677 var $comment = $general_comments.parent().find('div.comment:hidden');
678 // show hidden general comment form
678 // show hidden general comment form
679 $('#cb-comment-general-form-placeholder').show();
679 $('#cb-comment-general-form-placeholder').show();
680 } else {
680 } else {
681 var $comment = $inlineComments.find('div.comment:hidden');
681 var $comment = $inlineComments.find('div.comment:hidden');
682 }
682 }
683 $comment.show();
683 $comment.show();
684 }
684 }
685 var $replyWrapper = $node.closest('.comment-inline-form').closest('.reply-thread-container-wrapper')
685 var $replyWrapper = $node.closest('.comment-inline-form').closest('.reply-thread-container-wrapper')
686 $replyWrapper.removeClass('comment-form-active');
686 $replyWrapper.removeClass('comment-form-active');
687
687
688 var lastComment = $inlineComments.find('.comment-inline').last();
688 var lastComment = $inlineComments.find('.comment-inline').last();
689 if ($(lastComment).hasClass('comment-outdated')) {
689 if ($(lastComment).hasClass('comment-outdated')) {
690 $replyWrapper.hide();
690 $replyWrapper.hide();
691 }
691 }
692
692
693 $node.closest('.comment-inline-form').remove();
693 $node.closest('.comment-inline-form').remove();
694 return false;
694 return false;
695 };
695 };
696
696
697 this._deleteComment = function(node) {
697 this._deleteComment = function(node) {
698 var $node = $(node);
698 var $node = $(node);
699 var $td = $node.closest('td');
699 var $td = $node.closest('td');
700 var $comment = $node.closest('.comment');
700 var $comment = $node.closest('.comment');
701 var comment_id = $($comment).data('commentId');
701 var comment_id = $($comment).data('commentId');
702 var isDraft = $($comment).data('commentDraft');
702 var isDraft = $($comment).data('commentDraft');
703 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
703 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
704 var postData = {
704 var postData = {
705 'csrf_token': CSRF_TOKEN
705 'csrf_token': CSRF_TOKEN
706 };
706 };
707
707
708 $comment.addClass('comment-deleting');
708 $comment.addClass('comment-deleting');
709 $comment.hide('fast');
709 $comment.hide('fast');
710
710
711 var success = function(response) {
711 var success = function(response) {
712 $comment.remove();
712 $comment.remove();
713
713
714 if (window.updateSticky !== undefined) {
714 if (window.updateSticky !== undefined) {
715 // potentially our comments change the active window size, so we
715 // potentially our comments change the active window size, so we
716 // notify sticky elements
716 // notify sticky elements
717 updateSticky()
717 updateSticky()
718 }
718 }
719
719
720 if (window.refreshAllComments !== undefined && !isDraft) {
720 if (window.refreshAllComments !== undefined && !isDraft) {
721 // if we have this handler, run it, and refresh all comments boxes
721 // if we have this handler, run it, and refresh all comments boxes
722 refreshAllComments()
722 refreshAllComments()
723 }
723 }
724 return false;
724 return false;
725 };
725 };
726
726
727 var failure = function(jqXHR, textStatus, errorThrown) {
727 var failure = function(jqXHR, textStatus, errorThrown) {
728 var prefix = "Error while deleting this comment.\n"
728 var prefix = "Error while deleting this comment.\n"
729 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
729 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
730 ajaxErrorSwal(message);
730 ajaxErrorSwal(message);
731
731
732 $comment.show('fast');
732 $comment.show('fast');
733 $comment.removeClass('comment-deleting');
733 $comment.removeClass('comment-deleting');
734 return false;
734 return false;
735 };
735 };
736 ajaxPOST(url, postData, success, failure);
736 ajaxPOST(url, postData, success, failure);
737
737
738
739
740 }
738 }
741
739
742 this.deleteComment = function(node) {
740 this.deleteComment = function(node) {
743 var $comment = $(node).closest('.comment');
741 var $comment = $(node).closest('.comment');
744 var comment_id = $comment.attr('data-comment-id');
742 var comment_id = $comment.attr('data-comment-id');
745
743
746 SwalNoAnimation.fire({
744 SwalNoAnimation.fire({
747 title: 'Delete this comment?',
745 title: 'Delete this comment?',
748 icon: 'warning',
746 icon: 'warning',
749 showCancelButton: true,
747 showCancelButton: true,
750 confirmButtonText: _gettext('Yes, delete comment #{0}!').format(comment_id),
748 confirmButtonText: _gettext('Yes, delete comment #{0}!').format(comment_id),
751
749
752 }).then(function(result) {
750 }).then(function(result) {
753 if (result.value) {
751 if (result.value) {
754 self._deleteComment(node);
752 self._deleteComment(node);
755 }
753 }
756 })
754 })
757 };
755 };
758
756
759 this._finalizeDrafts = function(commentIds) {
757 this._finalizeDrafts = function(commentIds) {
760 window.finalizeDrafts(commentIds)
758
759 // remove the drafts so we can lock them before submit.
760 $.each(commentIds, function(idx, val){
761 $('#comment-{0}'.format(val)).remove();
762 })
763
764 var params = {
765 'pull_request_id': templateContext.pull_request_data.pull_request_id,
766 'repo_name': templateContext.repo_name,
767 };
768 var url = pyroutes.url('pullrequest_draft_comments_submit', params)
769 var postData = {'comments': commentIds, 'csrf_token': CSRF_TOKEN};
770
771 var submitSuccessCallback = function(json_data) {
772 self.attachInlineComment(json_data);
773
774 if (window.refreshDraftComments !== undefined) {
775 // if we have this handler, run it, and refresh all comments boxes
776 refreshDraftComments()
777 }
778
779 return false;
780 };
781
782 ajaxPOST(url, postData, submitSuccessCallback)
783
761 }
784 }
762
785
763 this.finalizeDrafts = function(commentIds) {
786 this.finalizeDrafts = function(commentIds) {
764
787
765 SwalNoAnimation.fire({
788 SwalNoAnimation.fire({
766 title: _ngettext('Submit {0} draft comment.', 'Submit {0} draft comments.', commentIds.length).format(commentIds.length),
789 title: _ngettext('Submit {0} draft comment.', 'Submit {0} draft comments.', commentIds.length).format(commentIds.length),
767 icon: 'warning',
790 icon: 'warning',
768 showCancelButton: true,
791 showCancelButton: true,
769 confirmButtonText: _gettext('Yes, finalize drafts'),
792 confirmButtonText: _gettext('Yes'),
770
793
771 }).then(function(result) {
794 }).then(function(result) {
772 if (result.value) {
795 if (result.value) {
773 self._finalizeDrafts(commentIds);
796 self._finalizeDrafts(commentIds);
774 }
797 }
775 })
798 })
776 };
799 };
777
800
778 this.toggleWideMode = function (node) {
801 this.toggleWideMode = function (node) {
779
802
780 if ($('#content').hasClass('wrapper')) {
803 if ($('#content').hasClass('wrapper')) {
781 $('#content').removeClass("wrapper");
804 $('#content').removeClass("wrapper");
782 $('#content').addClass("wide-mode-wrapper");
805 $('#content').addClass("wide-mode-wrapper");
783 $(node).addClass('btn-success');
806 $(node).addClass('btn-success');
784 return true
807 return true
785 } else {
808 } else {
786 $('#content').removeClass("wide-mode-wrapper");
809 $('#content').removeClass("wide-mode-wrapper");
787 $('#content').addClass("wrapper");
810 $('#content').addClass("wrapper");
788 $(node).removeClass('btn-success');
811 $(node).removeClass('btn-success');
789 return false
812 return false
790 }
813 }
791
814
792 };
815 };
793
816
794 /**
817 /**
795 * Turn off/on all comments in file diff
818 * Turn off/on all comments in file diff
796 */
819 */
797 this.toggleDiffComments = function(node) {
820 this.toggleDiffComments = function(node) {
798 // Find closes filediff container
821 // Find closes filediff container
799 var $filediff = $(node).closest('.filediff');
822 var $filediff = $(node).closest('.filediff');
800 if ($(node).hasClass('toggle-on')) {
823 if ($(node).hasClass('toggle-on')) {
801 var show = false;
824 var show = false;
802 } else if ($(node).hasClass('toggle-off')) {
825 } else if ($(node).hasClass('toggle-off')) {
803 var show = true;
826 var show = true;
804 }
827 }
805
828
806 // Toggle each individual comment block, so we can un-toggle single ones
829 // Toggle each individual comment block, so we can un-toggle single ones
807 $.each($filediff.find('.toggle-comment-action'), function(idx, val) {
830 $.each($filediff.find('.toggle-comment-action'), function(idx, val) {
808 self.toggleLineComments($(val), show)
831 self.toggleLineComments($(val), show)
809 })
832 })
810
833
811 // since we change the height of the diff container that has anchor points for upper
834 // since we change the height of the diff container that has anchor points for upper
812 // sticky header, we need to tell it to re-calculate those
835 // sticky header, we need to tell it to re-calculate those
813 if (window.updateSticky !== undefined) {
836 if (window.updateSticky !== undefined) {
814 // potentially our comments change the active window size, so we
837 // potentially our comments change the active window size, so we
815 // notify sticky elements
838 // notify sticky elements
816 updateSticky()
839 updateSticky()
817 }
840 }
818
841
819 return false;
842 return false;
820 }
843 }
821
844
822 this.toggleLineComments = function(node, show) {
845 this.toggleLineComments = function(node, show) {
823
846
824 var trElem = $(node).closest('tr')
847 var trElem = $(node).closest('tr')
825
848
826 if (show === true) {
849 if (show === true) {
827 // mark outdated comments as visible before the toggle;
850 // mark outdated comments as visible before the toggle;
828 $(trElem).find('.comment-outdated').show();
851 $(trElem).find('.comment-outdated').show();
829 $(trElem).removeClass('hide-line-comments');
852 $(trElem).removeClass('hide-line-comments');
830 } else if (show === false) {
853 } else if (show === false) {
831 $(trElem).find('.comment-outdated').hide();
854 $(trElem).find('.comment-outdated').hide();
832 $(trElem).addClass('hide-line-comments');
855 $(trElem).addClass('hide-line-comments');
833 } else {
856 } else {
834 // mark outdated comments as visible before the toggle;
857 // mark outdated comments as visible before the toggle;
835 $(trElem).find('.comment-outdated').show();
858 $(trElem).find('.comment-outdated').show();
836 $(trElem).toggleClass('hide-line-comments');
859 $(trElem).toggleClass('hide-line-comments');
837 }
860 }
838
861
839 // since we change the height of the diff container that has anchor points for upper
862 // since we change the height of the diff container that has anchor points for upper
840 // sticky header, we need to tell it to re-calculate those
863 // sticky header, we need to tell it to re-calculate those
841 if (window.updateSticky !== undefined) {
864 if (window.updateSticky !== undefined) {
842 // potentially our comments change the active window size, so we
865 // potentially our comments change the active window size, so we
843 // notify sticky elements
866 // notify sticky elements
844 updateSticky()
867 updateSticky()
845 }
868 }
846
869
847 };
870 };
848
871
849 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId, edit, comment_id){
872 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId, edit, comment_id){
850 var pullRequestId = templateContext.pull_request_data.pull_request_id;
873 var pullRequestId = templateContext.pull_request_data.pull_request_id;
851 var commitId = templateContext.commit_data.commit_id;
874 var commitId = templateContext.commit_data.commit_id;
852
875
853 var commentForm = new CommentForm(
876 var commentForm = new CommentForm(
854 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId, edit, comment_id);
877 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId, edit, comment_id);
855 var cm = commentForm.getCmInstance();
878 var cm = commentForm.getCmInstance();
856
879
857 if (resolvesCommentId){
880 if (resolvesCommentId){
858 placeholderText = _gettext('Leave a resolution comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
881 placeholderText = _gettext('Leave a resolution comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
859 }
882 }
860
883
861 setTimeout(function() {
884 setTimeout(function() {
862 // callbacks
885 // callbacks
863 if (cm !== undefined) {
886 if (cm !== undefined) {
864 commentForm.setPlaceholder(placeholderText);
887 commentForm.setPlaceholder(placeholderText);
865 if (commentForm.isInline()) {
888 if (commentForm.isInline()) {
866 cm.focus();
889 cm.focus();
867 cm.refresh();
890 cm.refresh();
868 }
891 }
869 }
892 }
870 }, 10);
893 }, 10);
871
894
872 // trigger scrolldown to the resolve comment, since it might be away
895 // trigger scrolldown to the resolve comment, since it might be away
873 // from the clicked
896 // from the clicked
874 if (resolvesCommentId){
897 if (resolvesCommentId){
875 var actionNode = $(commentForm.resolvesActionId).offset();
898 var actionNode = $(commentForm.resolvesActionId).offset();
876
899
877 setTimeout(function() {
900 setTimeout(function() {
878 if (actionNode) {
901 if (actionNode) {
879 $('body, html').animate({scrollTop: actionNode.top}, 10);
902 $('body, html').animate({scrollTop: actionNode.top}, 10);
880 }
903 }
881 }, 100);
904 }, 100);
882 }
905 }
883
906
884 // add dropzone support
907 // add dropzone support
885 var insertAttachmentText = function (cm, attachmentName, attachmentStoreUrl, isRendered) {
908 var insertAttachmentText = function (cm, attachmentName, attachmentStoreUrl, isRendered) {
886 var renderer = templateContext.visual.default_renderer;
909 var renderer = templateContext.visual.default_renderer;
887 if (renderer == 'rst') {
910 if (renderer == 'rst') {
888 var attachmentUrl = '`#{0} <{1}>`_'.format(attachmentName, attachmentStoreUrl);
911 var attachmentUrl = '`#{0} <{1}>`_'.format(attachmentName, attachmentStoreUrl);
889 if (isRendered){
912 if (isRendered){
890 attachmentUrl = '\n.. image:: {0}'.format(attachmentStoreUrl);
913 attachmentUrl = '\n.. image:: {0}'.format(attachmentStoreUrl);
891 }
914 }
892 } else if (renderer == 'markdown') {
915 } else if (renderer == 'markdown') {
893 var attachmentUrl = '[{0}]({1})'.format(attachmentName, attachmentStoreUrl);
916 var attachmentUrl = '[{0}]({1})'.format(attachmentName, attachmentStoreUrl);
894 if (isRendered){
917 if (isRendered){
895 attachmentUrl = '!' + attachmentUrl;
918 attachmentUrl = '!' + attachmentUrl;
896 }
919 }
897 } else {
920 } else {
898 var attachmentUrl = '{}'.format(attachmentStoreUrl);
921 var attachmentUrl = '{}'.format(attachmentStoreUrl);
899 }
922 }
900 cm.replaceRange(attachmentUrl+'\n', CodeMirror.Pos(cm.lastLine()));
923 cm.replaceRange(attachmentUrl+'\n', CodeMirror.Pos(cm.lastLine()));
901
924
902 return false;
925 return false;
903 };
926 };
904
927
905 //see: https://www.dropzonejs.com/#configuration
928 //see: https://www.dropzonejs.com/#configuration
906 var storeUrl = pyroutes.url('repo_commit_comment_attachment_upload',
929 var storeUrl = pyroutes.url('repo_commit_comment_attachment_upload',
907 {'repo_name': templateContext.repo_name,
930 {'repo_name': templateContext.repo_name,
908 'commit_id': templateContext.commit_data.commit_id})
931 'commit_id': templateContext.commit_data.commit_id})
909
932
910 var previewTmpl = $(formElement).find('.comment-attachment-uploader-template').get(0);
933 var previewTmpl = $(formElement).find('.comment-attachment-uploader-template').get(0);
911 if (previewTmpl !== undefined){
934 if (previewTmpl !== undefined){
912 var selectLink = $(formElement).find('.pick-attachment').get(0);
935 var selectLink = $(formElement).find('.pick-attachment').get(0);
913 $(formElement).find('.comment-attachment-uploader').dropzone({
936 $(formElement).find('.comment-attachment-uploader').dropzone({
914 url: storeUrl,
937 url: storeUrl,
915 headers: {"X-CSRF-Token": CSRF_TOKEN},
938 headers: {"X-CSRF-Token": CSRF_TOKEN},
916 paramName: function () {
939 paramName: function () {
917 return "attachment"
940 return "attachment"
918 }, // The name that will be used to transfer the file
941 }, // The name that will be used to transfer the file
919 clickable: selectLink,
942 clickable: selectLink,
920 parallelUploads: 1,
943 parallelUploads: 1,
921 maxFiles: 10,
944 maxFiles: 10,
922 maxFilesize: templateContext.attachment_store.max_file_size_mb,
945 maxFilesize: templateContext.attachment_store.max_file_size_mb,
923 uploadMultiple: false,
946 uploadMultiple: false,
924 autoProcessQueue: true, // if false queue will not be processed automatically.
947 autoProcessQueue: true, // if false queue will not be processed automatically.
925 createImageThumbnails: false,
948 createImageThumbnails: false,
926 previewTemplate: previewTmpl.innerHTML,
949 previewTemplate: previewTmpl.innerHTML,
927
950
928 accept: function (file, done) {
951 accept: function (file, done) {
929 done();
952 done();
930 },
953 },
931 init: function () {
954 init: function () {
932
955
933 this.on("sending", function (file, xhr, formData) {
956 this.on("sending", function (file, xhr, formData) {
934 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').hide();
957 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').hide();
935 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').show();
958 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').show();
936 });
959 });
937
960
938 this.on("success", function (file, response) {
961 this.on("success", function (file, response) {
939 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').show();
962 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').show();
940 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
963 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
941
964
942 var isRendered = false;
965 var isRendered = false;
943 var ext = file.name.split('.').pop();
966 var ext = file.name.split('.').pop();
944 var imageExts = templateContext.attachment_store.image_ext;
967 var imageExts = templateContext.attachment_store.image_ext;
945 if (imageExts.indexOf(ext) !== -1){
968 if (imageExts.indexOf(ext) !== -1){
946 isRendered = true;
969 isRendered = true;
947 }
970 }
948
971
949 insertAttachmentText(cm, file.name, response.repo_fqn_access_path, isRendered)
972 insertAttachmentText(cm, file.name, response.repo_fqn_access_path, isRendered)
950 });
973 });
951
974
952 this.on("error", function (file, errorMessage, xhr) {
975 this.on("error", function (file, errorMessage, xhr) {
953 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
976 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
954
977
955 var error = null;
978 var error = null;
956
979
957 if (xhr !== undefined){
980 if (xhr !== undefined){
958 var httpStatus = xhr.status + " " + xhr.statusText;
981 var httpStatus = xhr.status + " " + xhr.statusText;
959 if (xhr !== undefined && xhr.status >= 500) {
982 if (xhr !== undefined && xhr.status >= 500) {
960 error = httpStatus;
983 error = httpStatus;
961 }
984 }
962 }
985 }
963
986
964 if (error === null) {
987 if (error === null) {
965 error = errorMessage.error || errorMessage || httpStatus;
988 error = errorMessage.error || errorMessage || httpStatus;
966 }
989 }
967 $(file.previewElement).find('.dz-error-message').html('ERROR: {0}'.format(error));
990 $(file.previewElement).find('.dz-error-message').html('ERROR: {0}'.format(error));
968
991
969 });
992 });
970 }
993 }
971 });
994 });
972 }
995 }
973 return commentForm;
996 return commentForm;
974 };
997 };
975
998
976 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
999 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
977
1000
978 var tmpl = $('#cb-comment-general-form-template').html();
1001 var tmpl = $('#cb-comment-general-form-template').html();
979 tmpl = tmpl.format(null, 'general');
1002 tmpl = tmpl.format(null, 'general');
980 var $form = $(tmpl);
1003 var $form = $(tmpl);
981
1004
982 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
1005 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
983 var curForm = $formPlaceholder.find('form');
1006 var curForm = $formPlaceholder.find('form');
984 if (curForm){
1007 if (curForm){
985 curForm.remove();
1008 curForm.remove();
986 }
1009 }
987 $formPlaceholder.append($form);
1010 $formPlaceholder.append($form);
988
1011
989 var _form = $($form[0]);
1012 var _form = $($form[0]);
990 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
1013 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
991 var edit = false;
1014 var edit = false;
992 var comment_id = null;
1015 var comment_id = null;
993 var commentForm = this.createCommentForm(
1016 var commentForm = this.createCommentForm(
994 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId, edit, comment_id);
1017 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId, edit, comment_id);
995 commentForm.initStatusChangeSelector();
1018 commentForm.initStatusChangeSelector();
996
1019
997 return commentForm;
1020 return commentForm;
998 };
1021 };
999
1022
1000 this.editComment = function(node, line_no, f_path) {
1023 this.editComment = function(node, line_no, f_path) {
1001 self.edit = true;
1024 self.edit = true;
1002 var $node = $(node);
1025 var $node = $(node);
1003 var $td = $node.closest('td');
1026 var $td = $node.closest('td');
1004
1027
1005 var $comment = $(node).closest('.comment');
1028 var $comment = $(node).closest('.comment');
1006 var comment_id = $($comment).data('commentId');
1029 var comment_id = $($comment).data('commentId');
1007 var isDraft = $($comment).data('commentDraft');
1030 var isDraft = $($comment).data('commentDraft');
1008 var $editForm = null
1031 var $editForm = null
1009
1032
1010 var $comments = $node.closest('div.inline-comments');
1033 var $comments = $node.closest('div.inline-comments');
1011 var $general_comments = null;
1034 var $general_comments = null;
1012
1035
1013 if($comments.length){
1036 if($comments.length){
1014 // inline comments setup
1037 // inline comments setup
1015 $editForm = $comments.find('.comment-inline-form');
1038 $editForm = $comments.find('.comment-inline-form');
1016 line_no = self.getLineNumber(node)
1039 line_no = self.getLineNumber(node)
1017 }
1040 }
1018 else{
1041 else{
1019 // general comments setup
1042 // general comments setup
1020 $comments = $('#comments');
1043 $comments = $('#comments');
1021 $editForm = $comments.find('.comment-inline-form');
1044 $editForm = $comments.find('.comment-inline-form');
1022 line_no = $comment[0].id
1045 line_no = $comment[0].id
1023 $('#cb-comment-general-form-placeholder').hide();
1046 $('#cb-comment-general-form-placeholder').hide();
1024 }
1047 }
1025
1048
1026 if ($editForm.length === 0) {
1049 if ($editForm.length === 0) {
1027
1050
1028 // unhide all comments if they are hidden for a proper REPLY mode
1051 // unhide all comments if they are hidden for a proper REPLY mode
1029 var $filediff = $node.closest('.filediff');
1052 var $filediff = $node.closest('.filediff');
1030 $filediff.removeClass('hide-comments');
1053 $filediff.removeClass('hide-comments');
1031
1054
1032 $editForm = self.createNewFormWrapper(f_path, line_no);
1055 $editForm = self.createNewFormWrapper(f_path, line_no);
1033 if(f_path && line_no) {
1056 if(f_path && line_no) {
1034 $editForm.addClass('comment-inline-form-edit')
1057 $editForm.addClass('comment-inline-form-edit')
1035 }
1058 }
1036
1059
1037 $comment.after($editForm)
1060 $comment.after($editForm)
1038
1061
1039 var _form = $($editForm[0]).find('form');
1062 var _form = $($editForm[0]).find('form');
1040 var autocompleteActions = ['as_note',];
1063 var autocompleteActions = ['as_note',];
1041 var commentForm = this.createCommentForm(
1064 var commentForm = this.createCommentForm(
1042 _form, line_no, '', autocompleteActions, resolvesCommentId,
1065 _form, line_no, '', autocompleteActions, resolvesCommentId,
1043 this.edit, comment_id);
1066 this.edit, comment_id);
1044 var old_comment_text_binary = $comment.attr('data-comment-text');
1067 var old_comment_text_binary = $comment.attr('data-comment-text');
1045 var old_comment_text = b64DecodeUnicode(old_comment_text_binary);
1068 var old_comment_text = b64DecodeUnicode(old_comment_text_binary);
1046 commentForm.cm.setValue(old_comment_text);
1069 commentForm.cm.setValue(old_comment_text);
1047 $comment.hide();
1070 $comment.hide();
1048 tooltipActivate();
1071 tooltipActivate();
1049
1072
1050 // set a CUSTOM submit handler for inline comment edit action.
1073 // set a CUSTOM submit handler for inline comment edit action.
1051 commentForm.setHandleFormSubmit(function(o) {
1074 commentForm.setHandleFormSubmit(function(o) {
1052 var text = commentForm.cm.getValue();
1075 var text = commentForm.cm.getValue();
1053 var commentType = commentForm.getCommentType();
1076 var commentType = commentForm.getCommentType();
1054
1077
1055 if (text === "") {
1078 if (text === "") {
1056 return;
1079 return;
1057 }
1080 }
1058
1081
1059 if (old_comment_text == text) {
1082 if (old_comment_text == text) {
1060 SwalNoAnimation.fire({
1083 SwalNoAnimation.fire({
1061 title: 'Unable to edit comment',
1084 title: 'Unable to edit comment',
1062 html: _gettext('Comment body was not changed.'),
1085 html: _gettext('Comment body was not changed.'),
1063 });
1086 });
1064 return;
1087 return;
1065 }
1088 }
1066 var excludeCancelBtn = false;
1089 var excludeCancelBtn = false;
1067 var submitEvent = true;
1090 var submitEvent = true;
1068 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
1091 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
1069 commentForm.cm.setOption("readOnly", true);
1092 commentForm.cm.setOption("readOnly", true);
1070
1093
1071 // Read last version known
1094 // Read last version known
1072 var versionSelector = $('#comment_versions_{0}'.format(comment_id));
1095 var versionSelector = $('#comment_versions_{0}'.format(comment_id));
1073 var version = versionSelector.data('lastVersion');
1096 var version = versionSelector.data('lastVersion');
1074
1097
1075 if (!version) {
1098 if (!version) {
1076 version = 0;
1099 version = 0;
1077 }
1100 }
1078
1101
1079 var postData = {
1102 var postData = {
1080 'text': text,
1103 'text': text,
1081 'f_path': f_path,
1104 'f_path': f_path,
1082 'line': line_no,
1105 'line': line_no,
1083 'comment_type': commentType,
1106 'comment_type': commentType,
1084 'draft': isDraft,
1107 'draft': isDraft,
1085 'version': version,
1108 'version': version,
1086 'csrf_token': CSRF_TOKEN
1109 'csrf_token': CSRF_TOKEN
1087 };
1110 };
1088
1111
1089 var submitSuccessCallback = function(json_data) {
1112 var submitSuccessCallback = function(json_data) {
1090 $editForm.remove();
1113 $editForm.remove();
1091 $comment.show();
1114 $comment.show();
1092 var postData = {
1115 var postData = {
1093 'text': text,
1116 'text': text,
1094 'renderer': $comment.attr('data-comment-renderer'),
1117 'renderer': $comment.attr('data-comment-renderer'),
1095 'csrf_token': CSRF_TOKEN
1118 'csrf_token': CSRF_TOKEN
1096 };
1119 };
1097
1120
1098 /* Inject new edited version selector */
1121 /* Inject new edited version selector */
1099 var updateCommentVersionDropDown = function () {
1122 var updateCommentVersionDropDown = function () {
1100 var versionSelectId = '#comment_versions_'+comment_id;
1123 var versionSelectId = '#comment_versions_'+comment_id;
1101 var preLoadVersionData = [
1124 var preLoadVersionData = [
1102 {
1125 {
1103 id: json_data['comment_version'],
1126 id: json_data['comment_version'],
1104 text: "v{0}".format(json_data['comment_version']),
1127 text: "v{0}".format(json_data['comment_version']),
1105 action: function () {
1128 action: function () {
1106 Rhodecode.comments.showVersion(
1129 Rhodecode.comments.showVersion(
1107 json_data['comment_id'],
1130 json_data['comment_id'],
1108 json_data['comment_history_id']
1131 json_data['comment_history_id']
1109 )
1132 )
1110 },
1133 },
1111 comment_version: json_data['comment_version'],
1134 comment_version: json_data['comment_version'],
1112 comment_author_username: json_data['comment_author_username'],
1135 comment_author_username: json_data['comment_author_username'],
1113 comment_author_gravatar: json_data['comment_author_gravatar'],
1136 comment_author_gravatar: json_data['comment_author_gravatar'],
1114 comment_created_on: json_data['comment_created_on'],
1137 comment_created_on: json_data['comment_created_on'],
1115 },
1138 },
1116 ]
1139 ]
1117
1140
1118
1141
1119 if ($(versionSelectId).data('select2')) {
1142 if ($(versionSelectId).data('select2')) {
1120 var oldData = $(versionSelectId).data('select2').opts.data.results;
1143 var oldData = $(versionSelectId).data('select2').opts.data.results;
1121 $(versionSelectId).select2("destroy");
1144 $(versionSelectId).select2("destroy");
1122 preLoadVersionData = oldData.concat(preLoadVersionData)
1145 preLoadVersionData = oldData.concat(preLoadVersionData)
1123 }
1146 }
1124
1147
1125 initVersionSelector(versionSelectId, {results: preLoadVersionData});
1148 initVersionSelector(versionSelectId, {results: preLoadVersionData});
1126
1149
1127 $comment.attr('data-comment-text', utf8ToB64(text));
1150 $comment.attr('data-comment-text', utf8ToB64(text));
1128
1151
1129 var versionSelector = $('#comment_versions_'+comment_id);
1152 var versionSelector = $('#comment_versions_'+comment_id);
1130
1153
1131 // set lastVersion so we know our last edit version
1154 // set lastVersion so we know our last edit version
1132 versionSelector.data('lastVersion', json_data['comment_version'])
1155 versionSelector.data('lastVersion', json_data['comment_version'])
1133 versionSelector.parent().show();
1156 versionSelector.parent().show();
1134 }
1157 }
1135 updateCommentVersionDropDown();
1158 updateCommentVersionDropDown();
1136
1159
1137 // by default we reset state of comment preserving the text
1160 // by default we reset state of comment preserving the text
1138 var failRenderCommit = function(jqXHR, textStatus, errorThrown) {
1161 var failRenderCommit = function(jqXHR, textStatus, errorThrown) {
1139 var prefix = "Error while editing this comment.\n"
1162 var prefix = "Error while editing this comment.\n"
1140 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1163 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1141 ajaxErrorSwal(message);
1164 ajaxErrorSwal(message);
1142 };
1165 };
1143
1166
1144 var successRenderCommit = function(o){
1167 var successRenderCommit = function(o){
1145 $comment.show();
1168 $comment.show();
1146 $comment[0].lastElementChild.innerHTML = o;
1169 $comment[0].lastElementChild.innerHTML = o;
1147 };
1170 };
1148
1171
1149 var previewUrl = pyroutes.url(
1172 var previewUrl = pyroutes.url(
1150 'repo_commit_comment_preview',
1173 'repo_commit_comment_preview',
1151 {'repo_name': templateContext.repo_name,
1174 {'repo_name': templateContext.repo_name,
1152 'commit_id': templateContext.commit_data.commit_id});
1175 'commit_id': templateContext.commit_data.commit_id});
1153
1176
1154 _submitAjaxPOST(
1177 _submitAjaxPOST(
1155 previewUrl, postData, successRenderCommit, failRenderCommit
1178 previewUrl, postData, successRenderCommit, failRenderCommit
1156 );
1179 );
1157
1180
1158 try {
1181 try {
1159 var html = json_data.rendered_text;
1182 var html = json_data.rendered_text;
1160 var lineno = json_data.line_no;
1183 var lineno = json_data.line_no;
1161 var target_id = json_data.target_id;
1184 var target_id = json_data.target_id;
1162
1185
1163 $comments.find('.cb-comment-add-button').before(html);
1186 $comments.find('.cb-comment-add-button').before(html);
1164
1187
1165 // run global callback on submit
1188 // run global callback on submit
1166 commentForm.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id});
1189 commentForm.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id});
1167
1190
1168 } catch (e) {
1191 } catch (e) {
1169 console.error(e);
1192 console.error(e);
1170 }
1193 }
1171
1194
1172 // re trigger the linkification of next/prev navigation
1195 // re trigger the linkification of next/prev navigation
1173 linkifyComments($('.inline-comment-injected'));
1196 linkifyComments($('.inline-comment-injected'));
1174 timeagoActivate();
1197 timeagoActivate();
1175 tooltipActivate();
1198 tooltipActivate();
1176
1199
1177 if (window.updateSticky !== undefined) {
1200 if (window.updateSticky !== undefined) {
1178 // potentially our comments change the active window size, so we
1201 // potentially our comments change the active window size, so we
1179 // notify sticky elements
1202 // notify sticky elements
1180 updateSticky()
1203 updateSticky()
1181 }
1204 }
1182
1205
1183 if (window.refreshAllComments !== undefined && !isDraft) {
1206 if (window.refreshAllComments !== undefined && !isDraft) {
1184 // if we have this handler, run it, and refresh all comments boxes
1207 // if we have this handler, run it, and refresh all comments boxes
1185 refreshAllComments()
1208 refreshAllComments()
1186 }
1209 }
1187
1210
1188 commentForm.setActionButtonsDisabled(false);
1211 commentForm.setActionButtonsDisabled(false);
1189
1212
1190 };
1213 };
1191
1214
1192 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
1215 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
1193 var prefix = "Error while editing comment.\n"
1216 var prefix = "Error while editing comment.\n"
1194 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1217 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1195 if (jqXHR.status == 409){
1218 if (jqXHR.status == 409){
1196 message = 'This comment was probably changed somewhere else. Please reload the content of this comment.'
1219 message = 'This comment was probably changed somewhere else. Please reload the content of this comment.'
1197 ajaxErrorSwal(message, 'Comment version mismatch.');
1220 ajaxErrorSwal(message, 'Comment version mismatch.');
1198 } else {
1221 } else {
1199 ajaxErrorSwal(message);
1222 ajaxErrorSwal(message);
1200 }
1223 }
1201
1224
1202 commentForm.resetCommentFormState(text)
1225 commentForm.resetCommentFormState(text)
1203 };
1226 };
1204 commentForm.submitAjaxPOST(
1227 commentForm.submitAjaxPOST(
1205 commentForm.submitUrl, postData,
1228 commentForm.submitUrl, postData,
1206 submitSuccessCallback,
1229 submitSuccessCallback,
1207 submitFailCallback);
1230 submitFailCallback);
1208 });
1231 });
1209 }
1232 }
1210
1233
1211 $editForm.addClass('comment-inline-form-open');
1234 $editForm.addClass('comment-inline-form-open');
1212 };
1235 };
1213
1236
1214 this.attachComment = function(json_data) {
1237 this.attachComment = function(json_data) {
1215 var self = this;
1238 var self = this;
1216 $.each(json_data, function(idx, val) {
1239 $.each(json_data, function(idx, val) {
1217 var json_data_elem = [val]
1240 var json_data_elem = [val]
1218 var isInline = val.comment_f_path && val.comment_lineno
1241 var isInline = val.comment_f_path && val.comment_lineno
1219
1242
1220 if (isInline) {
1243 if (isInline) {
1221 self.attachInlineComment(json_data_elem)
1244 self.attachInlineComment(json_data_elem)
1222 } else {
1245 } else {
1223 self.attachGeneralComment(json_data_elem)
1246 self.attachGeneralComment(json_data_elem)
1224 }
1247 }
1225 })
1248 })
1226
1249
1227 }
1250 }
1228
1251
1229 this.attachGeneralComment = function(json_data) {
1252 this.attachGeneralComment = function(json_data) {
1230 $.each(json_data, function(idx, val) {
1253 $.each(json_data, function(idx, val) {
1231 $('#injected_page_comments').append(val.rendered_text);
1254 $('#injected_page_comments').append(val.rendered_text);
1232 })
1255 })
1233 }
1256 }
1234
1257
1235 this.attachInlineComment = function(json_data) {
1258 this.attachInlineComment = function(json_data) {
1236
1259
1237 $.each(json_data, function (idx, val) {
1260 $.each(json_data, function (idx, val) {
1238 var line_qry = '*[data-line-no="{0}"]'.format(val.line_no);
1261 var line_qry = '*[data-line-no="{0}"]'.format(val.line_no);
1239 var html = val.rendered_text;
1262 var html = val.rendered_text;
1240 var $inlineComments = $('#' + val.target_id)
1263 var $inlineComments = $('#' + val.target_id)
1241 .find(line_qry)
1264 .find(line_qry)
1242 .find('.inline-comments');
1265 .find('.inline-comments');
1243
1266
1244 var lastComment = $inlineComments.find('.comment-inline').last();
1267 var lastComment = $inlineComments.find('.comment-inline').last();
1245
1268
1246 if (lastComment.length === 0) {
1269 if (lastComment.length === 0) {
1247 // first comment, we append simply
1270 // first comment, we append simply
1248 $inlineComments.find('.reply-thread-container-wrapper').before(html);
1271 $inlineComments.find('.reply-thread-container-wrapper').before(html);
1249 } else {
1272 } else {
1250 $(lastComment).after(html)
1273 $(lastComment).after(html)
1251 }
1274 }
1252
1275
1253 })
1276 })
1254
1277
1255 };
1278 };
1256
1279
1257 this.createNewFormWrapper = function(f_path, line_no) {
1280 this.createNewFormWrapper = function(f_path, line_no) {
1258 // create a new reply HTML form from template
1281 // create a new reply HTML form from template
1259 var tmpl = $('#cb-comment-inline-form-template').html();
1282 var tmpl = $('#cb-comment-inline-form-template').html();
1260 tmpl = tmpl.format(escapeHtml(f_path), line_no);
1283 tmpl = tmpl.format(escapeHtml(f_path), line_no);
1261 return $(tmpl);
1284 return $(tmpl);
1262 }
1285 }
1263
1286
1264 this.createComment = function(node, f_path, line_no, resolutionComment) {
1287 this.createComment = function(node, f_path, line_no, resolutionComment) {
1265 self.edit = false;
1288 self.edit = false;
1266 var $node = $(node);
1289 var $node = $(node);
1267 var $td = $node.closest('td');
1290 var $td = $node.closest('td');
1268 var resolvesCommentId = resolutionComment || null;
1291 var resolvesCommentId = resolutionComment || null;
1269
1292
1270 var $replyForm = $td.find('.comment-inline-form');
1293 var $replyForm = $td.find('.comment-inline-form');
1271
1294
1272 // if form isn't existing, we're generating a new one and injecting it.
1295 // if form isn't existing, we're generating a new one and injecting it.
1273 if ($replyForm.length === 0) {
1296 if ($replyForm.length === 0) {
1274
1297
1275 // unhide/expand all comments if they are hidden for a proper REPLY mode
1298 // unhide/expand all comments if they are hidden for a proper REPLY mode
1276 self.toggleLineComments($node, true);
1299 self.toggleLineComments($node, true);
1277
1300
1278 $replyForm = self.createNewFormWrapper(f_path, line_no);
1301 $replyForm = self.createNewFormWrapper(f_path, line_no);
1279
1302
1280 var $comments = $td.find('.inline-comments');
1303 var $comments = $td.find('.inline-comments');
1281
1304
1282 // There aren't any comments, we init the `.inline-comments` with `reply-thread-container` first
1305 // There aren't any comments, we init the `.inline-comments` with `reply-thread-container` first
1283 if ($comments.length===0) {
1306 if ($comments.length===0) {
1284 var replBtn = '<button class="cb-comment-add-button" onclick="return Rhodecode.comments.createComment(this, \'{0}\', \'{1}\', null)">Reply...</button>'.format(f_path, line_no)
1307 var replBtn = '<button class="cb-comment-add-button" onclick="return Rhodecode.comments.createComment(this, \'{0}\', \'{1}\', null)">Reply...</button>'.format(f_path, line_no)
1285 var $reply_container = $('#cb-comments-inline-container-template')
1308 var $reply_container = $('#cb-comments-inline-container-template')
1286 $reply_container.find('button.cb-comment-add-button').replaceWith(replBtn);
1309 $reply_container.find('button.cb-comment-add-button').replaceWith(replBtn);
1287 $td.append($($reply_container).html());
1310 $td.append($($reply_container).html());
1288 }
1311 }
1289
1312
1290 // default comment button exists, so we prepend the form for leaving initial comment
1313 // default comment button exists, so we prepend the form for leaving initial comment
1291 $td.find('.cb-comment-add-button').before($replyForm);
1314 $td.find('.cb-comment-add-button').before($replyForm);
1292 // set marker, that we have a open form
1315 // set marker, that we have a open form
1293 var $replyWrapper = $td.find('.reply-thread-container-wrapper')
1316 var $replyWrapper = $td.find('.reply-thread-container-wrapper')
1294 $replyWrapper.addClass('comment-form-active');
1317 $replyWrapper.addClass('comment-form-active');
1295
1318
1296 var lastComment = $comments.find('.comment-inline').last();
1319 var lastComment = $comments.find('.comment-inline').last();
1297 if ($(lastComment).hasClass('comment-outdated')) {
1320 if ($(lastComment).hasClass('comment-outdated')) {
1298 $replyWrapper.show();
1321 $replyWrapper.show();
1299 }
1322 }
1300
1323
1301 var _form = $($replyForm[0]).find('form');
1324 var _form = $($replyForm[0]).find('form');
1302 var autocompleteActions = ['as_note', 'as_todo'];
1325 var autocompleteActions = ['as_note', 'as_todo'];
1303 var comment_id=null;
1326 var comment_id=null;
1304 var placeholderText = _gettext('Leave a comment on file {0} line {1}.').format(f_path, line_no);
1327 var placeholderText = _gettext('Leave a comment on file {0} line {1}.').format(f_path, line_no);
1305 var commentForm = self.createCommentForm(
1328 var commentForm = self.createCommentForm(
1306 _form, line_no, placeholderText, autocompleteActions, resolvesCommentId,
1329 _form, line_no, placeholderText, autocompleteActions, resolvesCommentId,
1307 self.edit, comment_id);
1330 self.edit, comment_id);
1308
1331
1309 // set a CUSTOM submit handler for inline comments.
1332 // set a CUSTOM submit handler for inline comments.
1310 commentForm.setHandleFormSubmit(function(o) {
1333 commentForm.setHandleFormSubmit(function(o) {
1311 var text = commentForm.cm.getValue();
1334 var text = commentForm.cm.getValue();
1312 var commentType = commentForm.getCommentType();
1335 var commentType = commentForm.getCommentType();
1313 var resolvesCommentId = commentForm.getResolvesId();
1336 var resolvesCommentId = commentForm.getResolvesId();
1314 var isDraft = commentForm.getDraftState();
1337 var isDraft = commentForm.getDraftState();
1315
1338
1316 if (text === "") {
1339 if (text === "") {
1317 return;
1340 return;
1318 }
1341 }
1319
1342
1320 if (line_no === undefined) {
1343 if (line_no === undefined) {
1321 alert('Error: unable to fetch line number for this inline comment !');
1344 alert('Error: unable to fetch line number for this inline comment !');
1322 return;
1345 return;
1323 }
1346 }
1324
1347
1325 if (f_path === undefined) {
1348 if (f_path === undefined) {
1326 alert('Error: unable to fetch file path for this inline comment !');
1349 alert('Error: unable to fetch file path for this inline comment !');
1327 return;
1350 return;
1328 }
1351 }
1329
1352
1330 var excludeCancelBtn = false;
1353 var excludeCancelBtn = false;
1331 var submitEvent = true;
1354 var submitEvent = true;
1332 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
1355 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
1333 commentForm.cm.setOption("readOnly", true);
1356 commentForm.cm.setOption("readOnly", true);
1334 var postData = {
1357 var postData = {
1335 'text': text,
1358 'text': text,
1336 'f_path': f_path,
1359 'f_path': f_path,
1337 'line': line_no,
1360 'line': line_no,
1338 'comment_type': commentType,
1361 'comment_type': commentType,
1339 'draft': isDraft,
1362 'draft': isDraft,
1340 'csrf_token': CSRF_TOKEN
1363 'csrf_token': CSRF_TOKEN
1341 };
1364 };
1342 if (resolvesCommentId){
1365 if (resolvesCommentId){
1343 postData['resolves_comment_id'] = resolvesCommentId;
1366 postData['resolves_comment_id'] = resolvesCommentId;
1344 }
1367 }
1345
1368
1346 // submitSuccess for inline commits
1369 // submitSuccess for inline commits
1347 var submitSuccessCallback = function(json_data) {
1370 var submitSuccessCallback = function(json_data) {
1348
1371
1349 $replyForm.remove();
1372 $replyForm.remove();
1350 $td.find('.reply-thread-container-wrapper').removeClass('comment-form-active');
1373 $td.find('.reply-thread-container-wrapper').removeClass('comment-form-active');
1351
1374
1352 try {
1375 try {
1353
1376
1354 // inject newly created comments, json_data is {<comment_id>: {}}
1377 // inject newly created comments, json_data is {<comment_id>: {}}
1355 self.attachInlineComment(json_data)
1378 self.attachInlineComment(json_data)
1356
1379
1357 //mark visually which comment was resolved
1380 //mark visually which comment was resolved
1358 if (resolvesCommentId) {
1381 if (resolvesCommentId) {
1359 commentForm.markCommentResolved(resolvesCommentId);
1382 commentForm.markCommentResolved(resolvesCommentId);
1360 }
1383 }
1361
1384
1362 // run global callback on submit
1385 // run global callback on submit
1363 commentForm.globalSubmitSuccessCallback({
1386 commentForm.globalSubmitSuccessCallback({
1364 draft: isDraft,
1387 draft: isDraft,
1365 comment_id: comment_id
1388 comment_id: comment_id
1366 });
1389 });
1367
1390
1368 } catch (e) {
1391 } catch (e) {
1369 console.error(e);
1392 console.error(e);
1370 }
1393 }
1371
1394
1372 if (window.updateSticky !== undefined) {
1395 if (window.updateSticky !== undefined) {
1373 // potentially our comments change the active window size, so we
1396 // potentially our comments change the active window size, so we
1374 // notify sticky elements
1397 // notify sticky elements
1375 updateSticky()
1398 updateSticky()
1376 }
1399 }
1377
1400
1378 if (window.refreshAllComments !== undefined && !isDraft) {
1401 if (window.refreshAllComments !== undefined && !isDraft) {
1379 // if we have this handler, run it, and refresh all comments boxes
1402 // if we have this handler, run it, and refresh all comments boxes
1380 refreshAllComments()
1403 refreshAllComments()
1381 }
1404 }
1382
1405
1383 commentForm.setActionButtonsDisabled(false);
1406 commentForm.setActionButtonsDisabled(false);
1384
1407
1385 // re trigger the linkification of next/prev navigation
1408 // re trigger the linkification of next/prev navigation
1386 linkifyComments($('.inline-comment-injected'));
1409 linkifyComments($('.inline-comment-injected'));
1387 timeagoActivate();
1410 timeagoActivate();
1388 tooltipActivate();
1411 tooltipActivate();
1389 };
1412 };
1390
1413
1391 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
1414 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
1392 var prefix = "Error while submitting comment.\n"
1415 var prefix = "Error while submitting comment.\n"
1393 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1416 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1394 ajaxErrorSwal(message);
1417 ajaxErrorSwal(message);
1395 commentForm.resetCommentFormState(text)
1418 commentForm.resetCommentFormState(text)
1396 };
1419 };
1397
1420
1398 commentForm.submitAjaxPOST(
1421 commentForm.submitAjaxPOST(
1399 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
1422 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
1400 });
1423 });
1401 }
1424 }
1402
1425
1403 // Finally "open" our reply form, since we know there are comments and we have the "attached" old form
1426 // Finally "open" our reply form, since we know there are comments and we have the "attached" old form
1404 $replyForm.addClass('comment-inline-form-open');
1427 $replyForm.addClass('comment-inline-form-open');
1405 tooltipActivate();
1428 tooltipActivate();
1406 };
1429 };
1407
1430
1408 this.createResolutionComment = function(commentId){
1431 this.createResolutionComment = function(commentId){
1409 // hide the trigger text
1432 // hide the trigger text
1410 $('#resolve-comment-{0}'.format(commentId)).hide();
1433 $('#resolve-comment-{0}'.format(commentId)).hide();
1411
1434
1412 var comment = $('#comment-'+commentId);
1435 var comment = $('#comment-'+commentId);
1413 var commentData = comment.data();
1436 var commentData = comment.data();
1414 if (commentData.commentInline) {
1437 if (commentData.commentInline) {
1415 var f_path = commentData.fPath;
1438 var f_path = commentData.fPath;
1416 var line_no = commentData.lineNo;
1439 var line_no = commentData.lineNo;
1417 //TODO check this if we need to give f_path/line_no
1440 //TODO check this if we need to give f_path/line_no
1418 this.createComment(comment, f_path, line_no, commentId)
1441 this.createComment(comment, f_path, line_no, commentId)
1419 } else {
1442 } else {
1420 this.createGeneralComment('general', "$placeholder", commentId)
1443 this.createGeneralComment('general', "$placeholder", commentId)
1421 }
1444 }
1422
1445
1423 return false;
1446 return false;
1424 };
1447 };
1425
1448
1426 this.submitResolution = function(commentId){
1449 this.submitResolution = function(commentId){
1427 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
1450 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
1428 var commentForm = form.get(0).CommentForm;
1451 var commentForm = form.get(0).CommentForm;
1429
1452
1430 var cm = commentForm.getCmInstance();
1453 var cm = commentForm.getCmInstance();
1431 var renderer = templateContext.visual.default_renderer;
1454 var renderer = templateContext.visual.default_renderer;
1432 if (renderer == 'rst'){
1455 if (renderer == 'rst'){
1433 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
1456 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
1434 } else if (renderer == 'markdown') {
1457 } else if (renderer == 'markdown') {
1435 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
1458 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
1436 } else {
1459 } else {
1437 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
1460 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
1438 }
1461 }
1439
1462
1440 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
1463 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
1441 form.submit();
1464 form.submit();
1442 return false;
1465 return false;
1443 };
1466 };
1444
1467
1445 };
1468 };
1446
1469
1447 window.commentHelp = function(renderer) {
1470 window.commentHelp = function(renderer) {
1448 var funcData = {'renderer': renderer}
1471 var funcData = {'renderer': renderer}
1449 return renderTemplate('commentHelpHovercard', funcData)
1472 return renderTemplate('commentHelpHovercard', funcData)
1450 } No newline at end of file
1473 }
@@ -1,1191 +1,1195 b''
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19
19
20 var prButtonLockChecks = {
20 var prButtonLockChecks = {
21 'compare': false,
21 'compare': false,
22 'reviewers': false
22 'reviewers': false
23 };
23 };
24
24
25 /**
25 /**
26 * lock button until all checks and loads are made. E.g reviewer calculation
26 * lock button until all checks and loads are made. E.g reviewer calculation
27 * should prevent from submitting a PR
27 * should prevent from submitting a PR
28 * @param lockEnabled
28 * @param lockEnabled
29 * @param msg
29 * @param msg
30 * @param scope
30 * @param scope
31 */
31 */
32 var prButtonLock = function(lockEnabled, msg, scope) {
32 var prButtonLock = function(lockEnabled, msg, scope) {
33 scope = scope || 'all';
33 scope = scope || 'all';
34 if (scope == 'all'){
34 if (scope == 'all'){
35 prButtonLockChecks['compare'] = !lockEnabled;
35 prButtonLockChecks['compare'] = !lockEnabled;
36 prButtonLockChecks['reviewers'] = !lockEnabled;
36 prButtonLockChecks['reviewers'] = !lockEnabled;
37 } else if (scope == 'compare') {
37 } else if (scope == 'compare') {
38 prButtonLockChecks['compare'] = !lockEnabled;
38 prButtonLockChecks['compare'] = !lockEnabled;
39 } else if (scope == 'reviewers'){
39 } else if (scope == 'reviewers'){
40 prButtonLockChecks['reviewers'] = !lockEnabled;
40 prButtonLockChecks['reviewers'] = !lockEnabled;
41 }
41 }
42 var checksMeet = prButtonLockChecks.compare && prButtonLockChecks.reviewers;
42 var checksMeet = prButtonLockChecks.compare && prButtonLockChecks.reviewers;
43 if (lockEnabled) {
43 if (lockEnabled) {
44 $('#pr_submit').attr('disabled', 'disabled');
44 $('#pr_submit').attr('disabled', 'disabled');
45 }
45 }
46 else if (checksMeet) {
46 else if (checksMeet) {
47 $('#pr_submit').removeAttr('disabled');
47 $('#pr_submit').removeAttr('disabled');
48 }
48 }
49
49
50 if (msg) {
50 if (msg) {
51 $('#pr_open_message').html(msg);
51 $('#pr_open_message').html(msg);
52 }
52 }
53 };
53 };
54
54
55
55
56 /**
56 /**
57 Generate Title and Description for a PullRequest.
57 Generate Title and Description for a PullRequest.
58 In case of 1 commits, the title and description is that one commit
58 In case of 1 commits, the title and description is that one commit
59 in case of multiple commits, we iterate on them with max N number of commits,
59 in case of multiple commits, we iterate on them with max N number of commits,
60 and build description in a form
60 and build description in a form
61 - commitN
61 - commitN
62 - commitN+1
62 - commitN+1
63 ...
63 ...
64
64
65 Title is then constructed from branch names, or other references,
65 Title is then constructed from branch names, or other references,
66 replacing '-' and '_' into spaces
66 replacing '-' and '_' into spaces
67
67
68 * @param sourceRef
68 * @param sourceRef
69 * @param elements
69 * @param elements
70 * @param limit
70 * @param limit
71 * @returns {*[]}
71 * @returns {*[]}
72 */
72 */
73 var getTitleAndDescription = function(sourceRefType, sourceRef, elements, limit) {
73 var getTitleAndDescription = function(sourceRefType, sourceRef, elements, limit) {
74 var title = '';
74 var title = '';
75 var desc = '';
75 var desc = '';
76
76
77 $.each($(elements).get().reverse().slice(0, limit), function(idx, value) {
77 $.each($(elements).get().reverse().slice(0, limit), function(idx, value) {
78 var rawMessage = value['message'];
78 var rawMessage = value['message'];
79 desc += '- ' + rawMessage.split('\n')[0].replace(/\n+$/, "") + '\n';
79 desc += '- ' + rawMessage.split('\n')[0].replace(/\n+$/, "") + '\n';
80 });
80 });
81 // only 1 commit, use commit message as title
81 // only 1 commit, use commit message as title
82 if (elements.length === 1) {
82 if (elements.length === 1) {
83 var rawMessage = elements[0]['message'];
83 var rawMessage = elements[0]['message'];
84 title = rawMessage.split('\n')[0];
84 title = rawMessage.split('\n')[0];
85 }
85 }
86 else {
86 else {
87 // use reference name
87 // use reference name
88 var normalizedRef = sourceRef.replace(/-/g, ' ').replace(/_/g, ' ').capitalizeFirstLetter()
88 var normalizedRef = sourceRef.replace(/-/g, ' ').replace(/_/g, ' ').capitalizeFirstLetter()
89 var refType = sourceRefType;
89 var refType = sourceRefType;
90 title = 'Changes from {0}: {1}'.format(refType, normalizedRef);
90 title = 'Changes from {0}: {1}'.format(refType, normalizedRef);
91 }
91 }
92
92
93 return [title, desc]
93 return [title, desc]
94 };
94 };
95
95
96
96
97 window.ReviewersController = function () {
97 window.ReviewersController = function () {
98 var self = this;
98 var self = this;
99 this.$loadingIndicator = $('.calculate-reviewers');
99 this.$loadingIndicator = $('.calculate-reviewers');
100 this.$reviewRulesContainer = $('#review_rules');
100 this.$reviewRulesContainer = $('#review_rules');
101 this.$rulesList = this.$reviewRulesContainer.find('.pr-reviewer-rules');
101 this.$rulesList = this.$reviewRulesContainer.find('.pr-reviewer-rules');
102 this.$userRule = $('.pr-user-rule-container');
102 this.$userRule = $('.pr-user-rule-container');
103 this.$reviewMembers = $('#review_members');
103 this.$reviewMembers = $('#review_members');
104 this.$observerMembers = $('#observer_members');
104 this.$observerMembers = $('#observer_members');
105
105
106 this.currentRequest = null;
106 this.currentRequest = null;
107 this.diffData = null;
107 this.diffData = null;
108 this.enabledRules = [];
108 this.enabledRules = [];
109 // sync with db.py entries
109 // sync with db.py entries
110 this.ROLE_REVIEWER = 'reviewer';
110 this.ROLE_REVIEWER = 'reviewer';
111 this.ROLE_OBSERVER = 'observer'
111 this.ROLE_OBSERVER = 'observer'
112
112
113 //dummy handler, we might register our own later
113 //dummy handler, we might register our own later
114 this.diffDataHandler = function (data) {};
114 this.diffDataHandler = function (data) {};
115
115
116 this.defaultForbidUsers = function () {
116 this.defaultForbidUsers = function () {
117 return [
117 return [
118 {
118 {
119 'username': 'default',
119 'username': 'default',
120 'user_id': templateContext.default_user.user_id
120 'user_id': templateContext.default_user.user_id
121 }
121 }
122 ];
122 ];
123 };
123 };
124
124
125 // init default forbidden users
125 // init default forbidden users
126 this.forbidUsers = this.defaultForbidUsers();
126 this.forbidUsers = this.defaultForbidUsers();
127
127
128 this.hideReviewRules = function () {
128 this.hideReviewRules = function () {
129 self.$reviewRulesContainer.hide();
129 self.$reviewRulesContainer.hide();
130 $(self.$userRule.selector).hide();
130 $(self.$userRule.selector).hide();
131 };
131 };
132
132
133 this.showReviewRules = function () {
133 this.showReviewRules = function () {
134 self.$reviewRulesContainer.show();
134 self.$reviewRulesContainer.show();
135 $(self.$userRule.selector).show();
135 $(self.$userRule.selector).show();
136 };
136 };
137
137
138 this.addRule = function (ruleText) {
138 this.addRule = function (ruleText) {
139 self.showReviewRules();
139 self.showReviewRules();
140 self.enabledRules.push(ruleText);
140 self.enabledRules.push(ruleText);
141 return '<div>- {0}</div>'.format(ruleText)
141 return '<div>- {0}</div>'.format(ruleText)
142 };
142 };
143
143
144 this.increaseCounter = function(role) {
144 this.increaseCounter = function(role) {
145 if (role === self.ROLE_REVIEWER) {
145 if (role === self.ROLE_REVIEWER) {
146 var $elem = $('#reviewers-cnt')
146 var $elem = $('#reviewers-cnt')
147 var cnt = parseInt($elem.data('count') || 0)
147 var cnt = parseInt($elem.data('count') || 0)
148 cnt +=1
148 cnt +=1
149 $elem.html(cnt);
149 $elem.html(cnt);
150 $elem.data('count', cnt);
150 $elem.data('count', cnt);
151 }
151 }
152 else if (role === self.ROLE_OBSERVER) {
152 else if (role === self.ROLE_OBSERVER) {
153 var $elem = $('#observers-cnt');
153 var $elem = $('#observers-cnt');
154 var cnt = parseInt($elem.data('count') || 0)
154 var cnt = parseInt($elem.data('count') || 0)
155 cnt +=1
155 cnt +=1
156 $elem.html(cnt);
156 $elem.html(cnt);
157 $elem.data('count', cnt);
157 $elem.data('count', cnt);
158 }
158 }
159 }
159 }
160
160
161 this.resetCounter = function () {
161 this.resetCounter = function () {
162 var $elem = $('#reviewers-cnt');
162 var $elem = $('#reviewers-cnt');
163
163
164 $elem.data('count', 0);
164 $elem.data('count', 0);
165 $elem.html(0);
165 $elem.html(0);
166
166
167 var $elem = $('#observers-cnt');
167 var $elem = $('#observers-cnt');
168
168
169 $elem.data('count', 0);
169 $elem.data('count', 0);
170 $elem.html(0);
170 $elem.html(0);
171 }
171 }
172
172
173 this.loadReviewRules = function (data) {
173 this.loadReviewRules = function (data) {
174 self.diffData = data;
174 self.diffData = data;
175
175
176 // reset forbidden Users
176 // reset forbidden Users
177 this.forbidUsers = self.defaultForbidUsers();
177 this.forbidUsers = self.defaultForbidUsers();
178
178
179 // reset state of review rules
179 // reset state of review rules
180 self.$rulesList.html('');
180 self.$rulesList.html('');
181
181
182 if (!data || data.rules === undefined || $.isEmptyObject(data.rules)) {
182 if (!data || data.rules === undefined || $.isEmptyObject(data.rules)) {
183 // default rule, case for older repo that don't have any rules stored
183 // default rule, case for older repo that don't have any rules stored
184 self.$rulesList.append(
184 self.$rulesList.append(
185 self.addRule(
185 self.addRule(
186 _gettext('All reviewers must vote.'))
186 _gettext('All reviewers must vote.'))
187 );
187 );
188 return self.forbidUsers
188 return self.forbidUsers
189 }
189 }
190
190
191 if (data.rules.voting !== undefined) {
191 if (data.rules.voting !== undefined) {
192 if (data.rules.voting < 0) {
192 if (data.rules.voting < 0) {
193 self.$rulesList.append(
193 self.$rulesList.append(
194 self.addRule(
194 self.addRule(
195 _gettext('All individual reviewers must vote.'))
195 _gettext('All individual reviewers must vote.'))
196 )
196 )
197 } else if (data.rules.voting === 1) {
197 } else if (data.rules.voting === 1) {
198 self.$rulesList.append(
198 self.$rulesList.append(
199 self.addRule(
199 self.addRule(
200 _gettext('At least {0} reviewer must vote.').format(data.rules.voting))
200 _gettext('At least {0} reviewer must vote.').format(data.rules.voting))
201 )
201 )
202
202
203 } else {
203 } else {
204 self.$rulesList.append(
204 self.$rulesList.append(
205 self.addRule(
205 self.addRule(
206 _gettext('At least {0} reviewers must vote.').format(data.rules.voting))
206 _gettext('At least {0} reviewers must vote.').format(data.rules.voting))
207 )
207 )
208 }
208 }
209 }
209 }
210
210
211 if (data.rules.voting_groups !== undefined) {
211 if (data.rules.voting_groups !== undefined) {
212 $.each(data.rules.voting_groups, function (index, rule_data) {
212 $.each(data.rules.voting_groups, function (index, rule_data) {
213 self.$rulesList.append(
213 self.$rulesList.append(
214 self.addRule(rule_data.text)
214 self.addRule(rule_data.text)
215 )
215 )
216 });
216 });
217 }
217 }
218
218
219 if (data.rules.use_code_authors_for_review) {
219 if (data.rules.use_code_authors_for_review) {
220 self.$rulesList.append(
220 self.$rulesList.append(
221 self.addRule(
221 self.addRule(
222 _gettext('Reviewers picked from source code changes.'))
222 _gettext('Reviewers picked from source code changes.'))
223 )
223 )
224 }
224 }
225
225
226 if (data.rules.forbid_adding_reviewers) {
226 if (data.rules.forbid_adding_reviewers) {
227 $('#add_reviewer_input').remove();
227 $('#add_reviewer_input').remove();
228 self.$rulesList.append(
228 self.$rulesList.append(
229 self.addRule(
229 self.addRule(
230 _gettext('Adding new reviewers is forbidden.'))
230 _gettext('Adding new reviewers is forbidden.'))
231 )
231 )
232 }
232 }
233
233
234 if (data.rules.forbid_author_to_review) {
234 if (data.rules.forbid_author_to_review) {
235 self.forbidUsers.push(data.rules_data.pr_author);
235 self.forbidUsers.push(data.rules_data.pr_author);
236 self.$rulesList.append(
236 self.$rulesList.append(
237 self.addRule(
237 self.addRule(
238 _gettext('Author is not allowed to be a reviewer.'))
238 _gettext('Author is not allowed to be a reviewer.'))
239 )
239 )
240 }
240 }
241
241
242 if (data.rules.forbid_commit_author_to_review) {
242 if (data.rules.forbid_commit_author_to_review) {
243
243
244 if (data.rules_data.forbidden_users) {
244 if (data.rules_data.forbidden_users) {
245 $.each(data.rules_data.forbidden_users, function (index, member_data) {
245 $.each(data.rules_data.forbidden_users, function (index, member_data) {
246 self.forbidUsers.push(member_data)
246 self.forbidUsers.push(member_data)
247 });
247 });
248 }
248 }
249
249
250 self.$rulesList.append(
250 self.$rulesList.append(
251 self.addRule(
251 self.addRule(
252 _gettext('Commit Authors are not allowed to be a reviewer.'))
252 _gettext('Commit Authors are not allowed to be a reviewer.'))
253 )
253 )
254 }
254 }
255
255
256 // we don't have any rules set, so we inform users about it
256 // we don't have any rules set, so we inform users about it
257 if (self.enabledRules.length === 0) {
257 if (self.enabledRules.length === 0) {
258 self.addRule(
258 self.addRule(
259 _gettext('No review rules set.'))
259 _gettext('No review rules set.'))
260 }
260 }
261
261
262 return self.forbidUsers
262 return self.forbidUsers
263 };
263 };
264
264
265 this.emptyTables = function () {
265 this.emptyTables = function () {
266 self.emptyReviewersTable();
266 self.emptyReviewersTable();
267 self.emptyObserversTable();
267 self.emptyObserversTable();
268
268
269 // Also reset counters.
269 // Also reset counters.
270 self.resetCounter();
270 self.resetCounter();
271 }
271 }
272
272
273 this.emptyReviewersTable = function (withText) {
273 this.emptyReviewersTable = function (withText) {
274 self.$reviewMembers.empty();
274 self.$reviewMembers.empty();
275 if (withText !== undefined) {
275 if (withText !== undefined) {
276 self.$reviewMembers.html(withText)
276 self.$reviewMembers.html(withText)
277 }
277 }
278 };
278 };
279
279
280 this.emptyObserversTable = function (withText) {
280 this.emptyObserversTable = function (withText) {
281 self.$observerMembers.empty();
281 self.$observerMembers.empty();
282 if (withText !== undefined) {
282 if (withText !== undefined) {
283 self.$observerMembers.html(withText)
283 self.$observerMembers.html(withText)
284 }
284 }
285 }
285 }
286
286
287 this.loadDefaultReviewers = function (sourceRepo, sourceRef, targetRepo, targetRef) {
287 this.loadDefaultReviewers = function (sourceRepo, sourceRef, targetRepo, targetRef) {
288
288
289 if (self.currentRequest) {
289 if (self.currentRequest) {
290 // make sure we cleanup old running requests before triggering this again
290 // make sure we cleanup old running requests before triggering this again
291 self.currentRequest.abort();
291 self.currentRequest.abort();
292 }
292 }
293
293
294 self.$loadingIndicator.show();
294 self.$loadingIndicator.show();
295
295
296 // reset reviewer/observe members
296 // reset reviewer/observe members
297 self.emptyTables();
297 self.emptyTables();
298
298
299 prButtonLock(true, null, 'reviewers');
299 prButtonLock(true, null, 'reviewers');
300 $('#user').hide(); // hide user autocomplete before load
300 $('#user').hide(); // hide user autocomplete before load
301 $('#observer').hide(); //hide observer autocomplete before load
301 $('#observer').hide(); //hide observer autocomplete before load
302
302
303 // lock PR button, so we cannot send PR before it's calculated
303 // lock PR button, so we cannot send PR before it's calculated
304 prButtonLock(true, _gettext('Loading diff ...'), 'compare');
304 prButtonLock(true, _gettext('Loading diff ...'), 'compare');
305
305
306 if (sourceRef.length !== 3 || targetRef.length !== 3) {
306 if (sourceRef.length !== 3 || targetRef.length !== 3) {
307 // don't load defaults in case we're missing some refs...
307 // don't load defaults in case we're missing some refs...
308 self.$loadingIndicator.hide();
308 self.$loadingIndicator.hide();
309 return
309 return
310 }
310 }
311
311
312 var url = pyroutes.url('repo_default_reviewers_data',
312 var url = pyroutes.url('repo_default_reviewers_data',
313 {
313 {
314 'repo_name': templateContext.repo_name,
314 'repo_name': templateContext.repo_name,
315 'source_repo': sourceRepo,
315 'source_repo': sourceRepo,
316 'source_ref_type': sourceRef[0],
316 'source_ref_type': sourceRef[0],
317 'source_ref_name': sourceRef[1],
317 'source_ref_name': sourceRef[1],
318 'source_ref': sourceRef[2],
318 'source_ref': sourceRef[2],
319 'target_repo': targetRepo,
319 'target_repo': targetRepo,
320 'target_ref': targetRef[2],
320 'target_ref': targetRef[2],
321 'target_ref_type': sourceRef[0],
321 'target_ref_type': sourceRef[0],
322 'target_ref_name': sourceRef[1]
322 'target_ref_name': sourceRef[1]
323 });
323 });
324
324
325 self.currentRequest = $.ajax({
325 self.currentRequest = $.ajax({
326 url: url,
326 url: url,
327 headers: {'X-PARTIAL-XHR': true},
327 headers: {'X-PARTIAL-XHR': true},
328 type: 'GET',
328 type: 'GET',
329 success: function (data) {
329 success: function (data) {
330
330
331 self.currentRequest = null;
331 self.currentRequest = null;
332
332
333 // review rules
333 // review rules
334 self.loadReviewRules(data);
334 self.loadReviewRules(data);
335 var diffHandled = self.handleDiffData(data["diff_info"]);
335 var diffHandled = self.handleDiffData(data["diff_info"]);
336 if (diffHandled === false) {
336 if (diffHandled === false) {
337 return
337 return
338 }
338 }
339
339
340 for (var i = 0; i < data.reviewers.length; i++) {
340 for (var i = 0; i < data.reviewers.length; i++) {
341 var reviewer = data.reviewers[i];
341 var reviewer = data.reviewers[i];
342 // load reviewer rules from the repo data
342 // load reviewer rules from the repo data
343 self.addMember(reviewer, reviewer.reasons, reviewer.mandatory, reviewer.role);
343 self.addMember(reviewer, reviewer.reasons, reviewer.mandatory, reviewer.role);
344 }
344 }
345
345
346
346
347 self.$loadingIndicator.hide();
347 self.$loadingIndicator.hide();
348 prButtonLock(false, null, 'reviewers');
348 prButtonLock(false, null, 'reviewers');
349
349
350 $('#user').show(); // show user autocomplete before load
350 $('#user').show(); // show user autocomplete before load
351 $('#observer').show(); // show observer autocomplete before load
351 $('#observer').show(); // show observer autocomplete before load
352
352
353 var commitElements = data["diff_info"]['commits'];
353 var commitElements = data["diff_info"]['commits'];
354
354
355 if (commitElements.length === 0) {
355 if (commitElements.length === 0) {
356 var noCommitsMsg = '<span class="alert-text-warning">{0}</span>'.format(
356 var noCommitsMsg = '<span class="alert-text-warning">{0}</span>'.format(
357 _gettext('There are no commits to merge.'));
357 _gettext('There are no commits to merge.'));
358 prButtonLock(true, noCommitsMsg, 'all');
358 prButtonLock(true, noCommitsMsg, 'all');
359
359
360 } else {
360 } else {
361 // un-lock PR button, so we cannot send PR before it's calculated
361 // un-lock PR button, so we cannot send PR before it's calculated
362 prButtonLock(false, null, 'compare');
362 prButtonLock(false, null, 'compare');
363 }
363 }
364
364
365 },
365 },
366 error: function (jqXHR, textStatus, errorThrown) {
366 error: function (jqXHR, textStatus, errorThrown) {
367 var prefix = "Loading diff and reviewers/observers failed\n"
367 var prefix = "Loading diff and reviewers/observers failed\n"
368 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
368 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
369 ajaxErrorSwal(message);
369 ajaxErrorSwal(message);
370 }
370 }
371 });
371 });
372
372
373 };
373 };
374
374
375 // check those, refactor
375 // check those, refactor
376 this.removeMember = function (reviewer_id, mark_delete) {
376 this.removeMember = function (reviewer_id, mark_delete) {
377 var reviewer = $('#reviewer_{0}'.format(reviewer_id));
377 var reviewer = $('#reviewer_{0}'.format(reviewer_id));
378
378
379 if (typeof (mark_delete) === undefined) {
379 if (typeof (mark_delete) === undefined) {
380 mark_delete = false;
380 mark_delete = false;
381 }
381 }
382
382
383 if (mark_delete === true) {
383 if (mark_delete === true) {
384 if (reviewer) {
384 if (reviewer) {
385 // now delete the input
385 // now delete the input
386 $('#reviewer_{0} input'.format(reviewer_id)).remove();
386 $('#reviewer_{0} input'.format(reviewer_id)).remove();
387 $('#reviewer_{0}_rules input'.format(reviewer_id)).remove();
387 $('#reviewer_{0}_rules input'.format(reviewer_id)).remove();
388 // mark as to-delete
388 // mark as to-delete
389 var obj = $('#reviewer_{0}_name'.format(reviewer_id));
389 var obj = $('#reviewer_{0}_name'.format(reviewer_id));
390 obj.addClass('to-delete');
390 obj.addClass('to-delete');
391 obj.css({"text-decoration": "line-through", "opacity": 0.5});
391 obj.css({"text-decoration": "line-through", "opacity": 0.5});
392 }
392 }
393 } else {
393 } else {
394 $('#reviewer_{0}'.format(reviewer_id)).remove();
394 $('#reviewer_{0}'.format(reviewer_id)).remove();
395 }
395 }
396 };
396 };
397
397
398 this.addMember = function (reviewer_obj, reasons, mandatory, role) {
398 this.addMember = function (reviewer_obj, reasons, mandatory, role) {
399
399
400 var id = reviewer_obj.user_id;
400 var id = reviewer_obj.user_id;
401 var username = reviewer_obj.username;
401 var username = reviewer_obj.username;
402
402
403 reasons = reasons || [];
403 reasons = reasons || [];
404 mandatory = mandatory || false;
404 mandatory = mandatory || false;
405 role = role || self.ROLE_REVIEWER
405 role = role || self.ROLE_REVIEWER
406
406
407 // register current set IDS to check if we don't have this ID already in
407 // register current set IDS to check if we don't have this ID already in
408 // and prevent duplicates
408 // and prevent duplicates
409 var currentIds = [];
409 var currentIds = [];
410
410
411 $.each($('.reviewer_entry'), function (index, value) {
411 $.each($('.reviewer_entry'), function (index, value) {
412 currentIds.push($(value).data('reviewerUserId'))
412 currentIds.push($(value).data('reviewerUserId'))
413 })
413 })
414
414
415 var userAllowedReview = function (userId) {
415 var userAllowedReview = function (userId) {
416 var allowed = true;
416 var allowed = true;
417 $.each(self.forbidUsers, function (index, member_data) {
417 $.each(self.forbidUsers, function (index, member_data) {
418 if (parseInt(userId) === member_data['user_id']) {
418 if (parseInt(userId) === member_data['user_id']) {
419 allowed = false;
419 allowed = false;
420 return false // breaks the loop
420 return false // breaks the loop
421 }
421 }
422 });
422 });
423 return allowed
423 return allowed
424 };
424 };
425
425
426 var userAllowed = userAllowedReview(id);
426 var userAllowed = userAllowedReview(id);
427
427
428 if (!userAllowed) {
428 if (!userAllowed) {
429 alert(_gettext('User `{0}` not allowed to be a reviewer').format(username));
429 alert(_gettext('User `{0}` not allowed to be a reviewer').format(username));
430 } else {
430 } else {
431 // only add if it's not there
431 // only add if it's not there
432 var alreadyReviewer = currentIds.indexOf(id) != -1;
432 var alreadyReviewer = currentIds.indexOf(id) != -1;
433
433
434 if (alreadyReviewer) {
434 if (alreadyReviewer) {
435 alert(_gettext('User `{0}` already in reviewers/observers').format(username));
435 alert(_gettext('User `{0}` already in reviewers/observers').format(username));
436 } else {
436 } else {
437
437
438 var reviewerEntry = renderTemplate('reviewMemberEntry', {
438 var reviewerEntry = renderTemplate('reviewMemberEntry', {
439 'member': reviewer_obj,
439 'member': reviewer_obj,
440 'mandatory': mandatory,
440 'mandatory': mandatory,
441 'role': role,
441 'role': role,
442 'reasons': reasons,
442 'reasons': reasons,
443 'allowed_to_update': true,
443 'allowed_to_update': true,
444 'review_status': 'not_reviewed',
444 'review_status': 'not_reviewed',
445 'review_status_label': _gettext('Not Reviewed'),
445 'review_status_label': _gettext('Not Reviewed'),
446 'user_group': reviewer_obj.user_group,
446 'user_group': reviewer_obj.user_group,
447 'create': true,
447 'create': true,
448 'rule_show': true,
448 'rule_show': true,
449 })
449 })
450
450
451 if (role === self.ROLE_REVIEWER) {
451 if (role === self.ROLE_REVIEWER) {
452 $(self.$reviewMembers.selector).append(reviewerEntry);
452 $(self.$reviewMembers.selector).append(reviewerEntry);
453 self.increaseCounter(self.ROLE_REVIEWER);
453 self.increaseCounter(self.ROLE_REVIEWER);
454 $('#reviewer-empty-msg').remove()
454 $('#reviewer-empty-msg').remove()
455 }
455 }
456 else if (role === self.ROLE_OBSERVER) {
456 else if (role === self.ROLE_OBSERVER) {
457 $(self.$observerMembers.selector).append(reviewerEntry);
457 $(self.$observerMembers.selector).append(reviewerEntry);
458 self.increaseCounter(self.ROLE_OBSERVER);
458 self.increaseCounter(self.ROLE_OBSERVER);
459 $('#observer-empty-msg').remove();
459 $('#observer-empty-msg').remove();
460 }
460 }
461
461
462 tooltipActivate();
462 tooltipActivate();
463 }
463 }
464 }
464 }
465
465
466 };
466 };
467
467
468 this.updateReviewers = function (repo_name, pull_request_id, role) {
468 this.updateReviewers = function (repo_name, pull_request_id, role) {
469 if (role === 'reviewer') {
469 if (role === 'reviewer') {
470 var postData = $('#reviewers input').serialize();
470 var postData = $('#reviewers input').serialize();
471 _updatePullRequest(repo_name, pull_request_id, postData);
471 _updatePullRequest(repo_name, pull_request_id, postData);
472 } else if (role === 'observer') {
472 } else if (role === 'observer') {
473 var postData = $('#observers input').serialize();
473 var postData = $('#observers input').serialize();
474 _updatePullRequest(repo_name, pull_request_id, postData);
474 _updatePullRequest(repo_name, pull_request_id, postData);
475 }
475 }
476 };
476 };
477
477
478 this.handleDiffData = function (data) {
478 this.handleDiffData = function (data) {
479 return self.diffDataHandler(data)
479 return self.diffDataHandler(data)
480 }
480 }
481 };
481 };
482
482
483
483
484 var _updatePullRequest = function(repo_name, pull_request_id, postData) {
484 var _updatePullRequest = function(repo_name, pull_request_id, postData) {
485 var url = pyroutes.url(
485 var url = pyroutes.url(
486 'pullrequest_update',
486 'pullrequest_update',
487 {"repo_name": repo_name, "pull_request_id": pull_request_id});
487 {"repo_name": repo_name, "pull_request_id": pull_request_id});
488 if (typeof postData === 'string' ) {
488 if (typeof postData === 'string' ) {
489 postData += '&csrf_token=' + CSRF_TOKEN;
489 postData += '&csrf_token=' + CSRF_TOKEN;
490 } else {
490 } else {
491 postData.csrf_token = CSRF_TOKEN;
491 postData.csrf_token = CSRF_TOKEN;
492 }
492 }
493
493
494 var success = function(o) {
494 var success = function(o) {
495 var redirectUrl = o['redirect_url'];
495 var redirectUrl = o['redirect_url'];
496 if (redirectUrl !== undefined && redirectUrl !== null && redirectUrl !== '') {
496 if (redirectUrl !== undefined && redirectUrl !== null && redirectUrl !== '') {
497 window.location = redirectUrl;
497 window.location = redirectUrl;
498 } else {
498 } else {
499 window.location.reload();
499 window.location.reload();
500 }
500 }
501 };
501 };
502
502
503 ajaxPOST(url, postData, success);
503 ajaxPOST(url, postData, success);
504 };
504 };
505
505
506 /**
506 /**
507 * PULL REQUEST update commits
507 * PULL REQUEST update commits
508 */
508 */
509 var updateCommits = function(repo_name, pull_request_id, force) {
509 var updateCommits = function(repo_name, pull_request_id, force) {
510 var postData = {
510 var postData = {
511 'update_commits': true
511 'update_commits': true
512 };
512 };
513 if (force !== undefined && force === true) {
513 if (force !== undefined && force === true) {
514 postData['force_refresh'] = true
514 postData['force_refresh'] = true
515 }
515 }
516 _updatePullRequest(repo_name, pull_request_id, postData);
516 _updatePullRequest(repo_name, pull_request_id, postData);
517 };
517 };
518
518
519
519
520 /**
520 /**
521 * PULL REQUEST edit info
521 * PULL REQUEST edit info
522 */
522 */
523 var editPullRequest = function(repo_name, pull_request_id, title, description, renderer) {
523 var editPullRequest = function(repo_name, pull_request_id, title, description, renderer) {
524 var url = pyroutes.url(
524 var url = pyroutes.url(
525 'pullrequest_update',
525 'pullrequest_update',
526 {"repo_name": repo_name, "pull_request_id": pull_request_id});
526 {"repo_name": repo_name, "pull_request_id": pull_request_id});
527
527
528 var postData = {
528 var postData = {
529 'title': title,
529 'title': title,
530 'description': description,
530 'description': description,
531 'description_renderer': renderer,
531 'description_renderer': renderer,
532 'edit_pull_request': true,
532 'edit_pull_request': true,
533 'csrf_token': CSRF_TOKEN
533 'csrf_token': CSRF_TOKEN
534 };
534 };
535 var success = function(o) {
535 var success = function(o) {
536 window.location.reload();
536 window.location.reload();
537 };
537 };
538 ajaxPOST(url, postData, success);
538 ajaxPOST(url, postData, success);
539 };
539 };
540
540
541
541
542 /**
542 /**
543 * autocomplete handler for reviewers/observers
543 * autocomplete handler for reviewers/observers
544 */
544 */
545 var autoCompleteHandler = function (inputId, controller, role) {
545 var autoCompleteHandler = function (inputId, controller, role) {
546
546
547 return function (element, data) {
547 return function (element, data) {
548 var mandatory = false;
548 var mandatory = false;
549 var reasons = [_gettext('added manually by "{0}"').format(
549 var reasons = [_gettext('added manually by "{0}"').format(
550 templateContext.rhodecode_user.username)];
550 templateContext.rhodecode_user.username)];
551
551
552 // add whole user groups
552 // add whole user groups
553 if (data.value_type == 'user_group') {
553 if (data.value_type == 'user_group') {
554 reasons.push(_gettext('member of "{0}"').format(data.value_display));
554 reasons.push(_gettext('member of "{0}"').format(data.value_display));
555
555
556 $.each(data.members, function (index, member_data) {
556 $.each(data.members, function (index, member_data) {
557 var reviewer = member_data;
557 var reviewer = member_data;
558 reviewer['user_id'] = member_data['id'];
558 reviewer['user_id'] = member_data['id'];
559 reviewer['gravatar_link'] = member_data['icon_link'];
559 reviewer['gravatar_link'] = member_data['icon_link'];
560 reviewer['user_link'] = member_data['profile_link'];
560 reviewer['user_link'] = member_data['profile_link'];
561 reviewer['rules'] = [];
561 reviewer['rules'] = [];
562 controller.addMember(reviewer, reasons, mandatory, role);
562 controller.addMember(reviewer, reasons, mandatory, role);
563 })
563 })
564 }
564 }
565 // add single user
565 // add single user
566 else {
566 else {
567 var reviewer = data;
567 var reviewer = data;
568 reviewer['user_id'] = data['id'];
568 reviewer['user_id'] = data['id'];
569 reviewer['gravatar_link'] = data['icon_link'];
569 reviewer['gravatar_link'] = data['icon_link'];
570 reviewer['user_link'] = data['profile_link'];
570 reviewer['user_link'] = data['profile_link'];
571 reviewer['rules'] = [];
571 reviewer['rules'] = [];
572 controller.addMember(reviewer, reasons, mandatory, role);
572 controller.addMember(reviewer, reasons, mandatory, role);
573 }
573 }
574
574
575 $(inputId).val('');
575 $(inputId).val('');
576 }
576 }
577 }
577 }
578
578
579 /**
579 /**
580 * Reviewer autocomplete
580 * Reviewer autocomplete
581 */
581 */
582 var ReviewerAutoComplete = function (inputId, controller) {
582 var ReviewerAutoComplete = function (inputId, controller) {
583 var self = this;
583 var self = this;
584 self.controller = controller;
584 self.controller = controller;
585 self.inputId = inputId;
585 self.inputId = inputId;
586 var handler = autoCompleteHandler(inputId, controller, controller.ROLE_REVIEWER);
586 var handler = autoCompleteHandler(inputId, controller, controller.ROLE_REVIEWER);
587
587
588 $(inputId).autocomplete({
588 $(inputId).autocomplete({
589 serviceUrl: pyroutes.url('user_autocomplete_data'),
589 serviceUrl: pyroutes.url('user_autocomplete_data'),
590 minChars: 2,
590 minChars: 2,
591 maxHeight: 400,
591 maxHeight: 400,
592 deferRequestBy: 300, //miliseconds
592 deferRequestBy: 300, //miliseconds
593 showNoSuggestionNotice: true,
593 showNoSuggestionNotice: true,
594 tabDisabled: true,
594 tabDisabled: true,
595 autoSelectFirst: true,
595 autoSelectFirst: true,
596 params: {
596 params: {
597 user_id: templateContext.rhodecode_user.user_id,
597 user_id: templateContext.rhodecode_user.user_id,
598 user_groups: true,
598 user_groups: true,
599 user_groups_expand: true,
599 user_groups_expand: true,
600 skip_default_user: true
600 skip_default_user: true
601 },
601 },
602 formatResult: autocompleteFormatResult,
602 formatResult: autocompleteFormatResult,
603 lookupFilter: autocompleteFilterResult,
603 lookupFilter: autocompleteFilterResult,
604 onSelect: handler
604 onSelect: handler
605 });
605 });
606 };
606 };
607
607
608 /**
608 /**
609 * Observers autocomplete
609 * Observers autocomplete
610 */
610 */
611 var ObserverAutoComplete = function(inputId, controller) {
611 var ObserverAutoComplete = function(inputId, controller) {
612 var self = this;
612 var self = this;
613 self.controller = controller;
613 self.controller = controller;
614 self.inputId = inputId;
614 self.inputId = inputId;
615 var handler = autoCompleteHandler(inputId, controller, controller.ROLE_OBSERVER);
615 var handler = autoCompleteHandler(inputId, controller, controller.ROLE_OBSERVER);
616
616
617 $(inputId).autocomplete({
617 $(inputId).autocomplete({
618 serviceUrl: pyroutes.url('user_autocomplete_data'),
618 serviceUrl: pyroutes.url('user_autocomplete_data'),
619 minChars: 2,
619 minChars: 2,
620 maxHeight: 400,
620 maxHeight: 400,
621 deferRequestBy: 300, //miliseconds
621 deferRequestBy: 300, //miliseconds
622 showNoSuggestionNotice: true,
622 showNoSuggestionNotice: true,
623 tabDisabled: true,
623 tabDisabled: true,
624 autoSelectFirst: true,
624 autoSelectFirst: true,
625 params: {
625 params: {
626 user_id: templateContext.rhodecode_user.user_id,
626 user_id: templateContext.rhodecode_user.user_id,
627 user_groups: true,
627 user_groups: true,
628 user_groups_expand: true,
628 user_groups_expand: true,
629 skip_default_user: true
629 skip_default_user: true
630 },
630 },
631 formatResult: autocompleteFormatResult,
631 formatResult: autocompleteFormatResult,
632 lookupFilter: autocompleteFilterResult,
632 lookupFilter: autocompleteFilterResult,
633 onSelect: handler
633 onSelect: handler
634 });
634 });
635 }
635 }
636
636
637
637
638 window.VersionController = function () {
638 window.VersionController = function () {
639 var self = this;
639 var self = this;
640 this.$verSource = $('input[name=ver_source]');
640 this.$verSource = $('input[name=ver_source]');
641 this.$verTarget = $('input[name=ver_target]');
641 this.$verTarget = $('input[name=ver_target]');
642 this.$showVersionDiff = $('#show-version-diff');
642 this.$showVersionDiff = $('#show-version-diff');
643
643
644 this.adjustRadioSelectors = function (curNode) {
644 this.adjustRadioSelectors = function (curNode) {
645 var getVal = function (item) {
645 var getVal = function (item) {
646 if (item === 'latest') {
646 if (item === 'latest') {
647 return Number.MAX_SAFE_INTEGER
647 return Number.MAX_SAFE_INTEGER
648 }
648 }
649 else {
649 else {
650 return parseInt(item)
650 return parseInt(item)
651 }
651 }
652 };
652 };
653
653
654 var curVal = getVal($(curNode).val());
654 var curVal = getVal($(curNode).val());
655 var cleared = false;
655 var cleared = false;
656
656
657 $.each(self.$verSource, function (index, value) {
657 $.each(self.$verSource, function (index, value) {
658 var elVal = getVal($(value).val());
658 var elVal = getVal($(value).val());
659
659
660 if (elVal > curVal) {
660 if (elVal > curVal) {
661 if ($(value).is(':checked')) {
661 if ($(value).is(':checked')) {
662 cleared = true;
662 cleared = true;
663 }
663 }
664 $(value).attr('disabled', 'disabled');
664 $(value).attr('disabled', 'disabled');
665 $(value).removeAttr('checked');
665 $(value).removeAttr('checked');
666 $(value).css({'opacity': 0.1});
666 $(value).css({'opacity': 0.1});
667 }
667 }
668 else {
668 else {
669 $(value).css({'opacity': 1});
669 $(value).css({'opacity': 1});
670 $(value).removeAttr('disabled');
670 $(value).removeAttr('disabled');
671 }
671 }
672 });
672 });
673
673
674 if (cleared) {
674 if (cleared) {
675 // if we unchecked an active, set the next one to same loc.
675 // if we unchecked an active, set the next one to same loc.
676 $(this.$verSource).filter('[value={0}]'.format(
676 $(this.$verSource).filter('[value={0}]'.format(
677 curVal)).attr('checked', 'checked');
677 curVal)).attr('checked', 'checked');
678 }
678 }
679
679
680 self.setLockAction(false,
680 self.setLockAction(false,
681 $(curNode).data('verPos'),
681 $(curNode).data('verPos'),
682 $(this.$verSource).filter(':checked').data('verPos')
682 $(this.$verSource).filter(':checked').data('verPos')
683 );
683 );
684 };
684 };
685
685
686
686
687 this.attachVersionListener = function () {
687 this.attachVersionListener = function () {
688 self.$verTarget.change(function (e) {
688 self.$verTarget.change(function (e) {
689 self.adjustRadioSelectors(this)
689 self.adjustRadioSelectors(this)
690 });
690 });
691 self.$verSource.change(function (e) {
691 self.$verSource.change(function (e) {
692 self.adjustRadioSelectors(self.$verTarget.filter(':checked'))
692 self.adjustRadioSelectors(self.$verTarget.filter(':checked'))
693 });
693 });
694 };
694 };
695
695
696 this.init = function () {
696 this.init = function () {
697
697
698 var curNode = self.$verTarget.filter(':checked');
698 var curNode = self.$verTarget.filter(':checked');
699 self.adjustRadioSelectors(curNode);
699 self.adjustRadioSelectors(curNode);
700 self.setLockAction(true);
700 self.setLockAction(true);
701 self.attachVersionListener();
701 self.attachVersionListener();
702
702
703 };
703 };
704
704
705 this.setLockAction = function (state, selectedVersion, otherVersion) {
705 this.setLockAction = function (state, selectedVersion, otherVersion) {
706 var $showVersionDiff = this.$showVersionDiff;
706 var $showVersionDiff = this.$showVersionDiff;
707
707
708 if (state) {
708 if (state) {
709 $showVersionDiff.attr('disabled', 'disabled');
709 $showVersionDiff.attr('disabled', 'disabled');
710 $showVersionDiff.addClass('disabled');
710 $showVersionDiff.addClass('disabled');
711 $showVersionDiff.html($showVersionDiff.data('labelTextLocked'));
711 $showVersionDiff.html($showVersionDiff.data('labelTextLocked'));
712 }
712 }
713 else {
713 else {
714 $showVersionDiff.removeAttr('disabled');
714 $showVersionDiff.removeAttr('disabled');
715 $showVersionDiff.removeClass('disabled');
715 $showVersionDiff.removeClass('disabled');
716
716
717 if (selectedVersion == otherVersion) {
717 if (selectedVersion == otherVersion) {
718 $showVersionDiff.html($showVersionDiff.data('labelTextShow'));
718 $showVersionDiff.html($showVersionDiff.data('labelTextShow'));
719 } else {
719 } else {
720 $showVersionDiff.html($showVersionDiff.data('labelTextDiff'));
720 $showVersionDiff.html($showVersionDiff.data('labelTextDiff'));
721 }
721 }
722 }
722 }
723
723
724 };
724 };
725
725
726 this.showVersionDiff = function () {
726 this.showVersionDiff = function () {
727 var target = self.$verTarget.filter(':checked');
727 var target = self.$verTarget.filter(':checked');
728 var source = self.$verSource.filter(':checked');
728 var source = self.$verSource.filter(':checked');
729
729
730 if (target.val() && source.val()) {
730 if (target.val() && source.val()) {
731 var params = {
731 var params = {
732 'pull_request_id': templateContext.pull_request_data.pull_request_id,
732 'pull_request_id': templateContext.pull_request_data.pull_request_id,
733 'repo_name': templateContext.repo_name,
733 'repo_name': templateContext.repo_name,
734 'version': target.val(),
734 'version': target.val(),
735 'from_version': source.val()
735 'from_version': source.val()
736 };
736 };
737 window.location = pyroutes.url('pullrequest_show', params)
737 window.location = pyroutes.url('pullrequest_show', params)
738 }
738 }
739
739
740 return false;
740 return false;
741 };
741 };
742
742
743 this.toggleVersionView = function (elem) {
743 this.toggleVersionView = function (elem) {
744
744
745 if (this.$showVersionDiff.is(':visible')) {
745 if (this.$showVersionDiff.is(':visible')) {
746 $('.version-pr').hide();
746 $('.version-pr').hide();
747 this.$showVersionDiff.hide();
747 this.$showVersionDiff.hide();
748 $(elem).html($(elem).data('toggleOn'))
748 $(elem).html($(elem).data('toggleOn'))
749 } else {
749 } else {
750 $('.version-pr').show();
750 $('.version-pr').show();
751 this.$showVersionDiff.show();
751 this.$showVersionDiff.show();
752 $(elem).html($(elem).data('toggleOff'))
752 $(elem).html($(elem).data('toggleOff'))
753 }
753 }
754
754
755 return false
755 return false
756 };
756 };
757
757
758 };
758 };
759
759
760
760
761 window.UpdatePrController = function () {
761 window.UpdatePrController = function () {
762 var self = this;
762 var self = this;
763 this.$updateCommits = $('#update_commits');
763 this.$updateCommits = $('#update_commits');
764 this.$updateCommitsSwitcher = $('#update_commits_switcher');
764 this.$updateCommitsSwitcher = $('#update_commits_switcher');
765
765
766 this.lockUpdateButton = function (label) {
766 this.lockUpdateButton = function (label) {
767 self.$updateCommits.attr('disabled', 'disabled');
767 self.$updateCommits.attr('disabled', 'disabled');
768 self.$updateCommitsSwitcher.attr('disabled', 'disabled');
768 self.$updateCommitsSwitcher.attr('disabled', 'disabled');
769
769
770 self.$updateCommits.addClass('disabled');
770 self.$updateCommits.addClass('disabled');
771 self.$updateCommitsSwitcher.addClass('disabled');
771 self.$updateCommitsSwitcher.addClass('disabled');
772
772
773 self.$updateCommits.removeClass('btn-primary');
773 self.$updateCommits.removeClass('btn-primary');
774 self.$updateCommitsSwitcher.removeClass('btn-primary');
774 self.$updateCommitsSwitcher.removeClass('btn-primary');
775
775
776 self.$updateCommits.text(_gettext(label));
776 self.$updateCommits.text(_gettext(label));
777 };
777 };
778
778
779 this.isUpdateLocked = function () {
779 this.isUpdateLocked = function () {
780 return self.$updateCommits.attr('disabled') !== undefined;
780 return self.$updateCommits.attr('disabled') !== undefined;
781 };
781 };
782
782
783 this.updateCommits = function (curNode) {
783 this.updateCommits = function (curNode) {
784 if (self.isUpdateLocked()) {
784 if (self.isUpdateLocked()) {
785 return
785 return
786 }
786 }
787 self.lockUpdateButton(_gettext('Updating...'));
787 self.lockUpdateButton(_gettext('Updating...'));
788 updateCommits(
788 updateCommits(
789 templateContext.repo_name,
789 templateContext.repo_name,
790 templateContext.pull_request_data.pull_request_id);
790 templateContext.pull_request_data.pull_request_id);
791 };
791 };
792
792
793 this.forceUpdateCommits = function () {
793 this.forceUpdateCommits = function () {
794 if (self.isUpdateLocked()) {
794 if (self.isUpdateLocked()) {
795 return
795 return
796 }
796 }
797 self.lockUpdateButton(_gettext('Force updating...'));
797 self.lockUpdateButton(_gettext('Force updating...'));
798 var force = true;
798 var force = true;
799 updateCommits(
799 updateCommits(
800 templateContext.repo_name,
800 templateContext.repo_name,
801 templateContext.pull_request_data.pull_request_id, force);
801 templateContext.pull_request_data.pull_request_id, force);
802 };
802 };
803 };
803 };
804
804
805
805
806 /**
806 /**
807 * Reviewer display panel
807 * Reviewer display panel
808 */
808 */
809 window.ReviewersPanel = {
809 window.ReviewersPanel = {
810 editButton: null,
810 editButton: null,
811 closeButton: null,
811 closeButton: null,
812 addButton: null,
812 addButton: null,
813 removeButtons: null,
813 removeButtons: null,
814 reviewRules: null,
814 reviewRules: null,
815 setReviewers: null,
815 setReviewers: null,
816 controller: null,
816 controller: null,
817
817
818 setSelectors: function () {
818 setSelectors: function () {
819 var self = this;
819 var self = this;
820 self.editButton = $('#open_edit_reviewers');
820 self.editButton = $('#open_edit_reviewers');
821 self.closeButton =$('#close_edit_reviewers');
821 self.closeButton =$('#close_edit_reviewers');
822 self.addButton = $('#add_reviewer');
822 self.addButton = $('#add_reviewer');
823 self.removeButtons = $('.reviewer_member_remove,.reviewer_member_mandatory_remove');
823 self.removeButtons = $('.reviewer_member_remove,.reviewer_member_mandatory_remove');
824 },
824 },
825
825
826 init: function (controller, reviewRules, setReviewers) {
826 init: function (controller, reviewRules, setReviewers) {
827 var self = this;
827 var self = this;
828 self.setSelectors();
828 self.setSelectors();
829
829
830 self.controller = controller;
830 self.controller = controller;
831 self.reviewRules = reviewRules;
831 self.reviewRules = reviewRules;
832 self.setReviewers = setReviewers;
832 self.setReviewers = setReviewers;
833
833
834 self.editButton.on('click', function (e) {
834 self.editButton.on('click', function (e) {
835 self.edit();
835 self.edit();
836 });
836 });
837 self.closeButton.on('click', function (e) {
837 self.closeButton.on('click', function (e) {
838 self.close();
838 self.close();
839 self.renderReviewers();
839 self.renderReviewers();
840 });
840 });
841
841
842 self.renderReviewers();
842 self.renderReviewers();
843
843
844 },
844 },
845
845
846 renderReviewers: function () {
846 renderReviewers: function () {
847 var self = this;
847 var self = this;
848
848
849 if (self.setReviewers.reviewers === undefined) {
849 if (self.setReviewers.reviewers === undefined) {
850 return
850 return
851 }
851 }
852 if (self.setReviewers.reviewers.length === 0) {
852 if (self.setReviewers.reviewers.length === 0) {
853 self.controller.emptyReviewersTable('<tr id="reviewer-empty-msg"><td colspan="6">No reviewers</td></tr>');
853 self.controller.emptyReviewersTable('<tr id="reviewer-empty-msg"><td colspan="6">No reviewers</td></tr>');
854 return
854 return
855 }
855 }
856
856
857 self.controller.emptyReviewersTable();
857 self.controller.emptyReviewersTable();
858
858
859 $.each(self.setReviewers.reviewers, function (key, val) {
859 $.each(self.setReviewers.reviewers, function (key, val) {
860
860
861 var member = val;
861 var member = val;
862 if (member.role === self.controller.ROLE_REVIEWER) {
862 if (member.role === self.controller.ROLE_REVIEWER) {
863 var entry = renderTemplate('reviewMemberEntry', {
863 var entry = renderTemplate('reviewMemberEntry', {
864 'member': member,
864 'member': member,
865 'mandatory': member.mandatory,
865 'mandatory': member.mandatory,
866 'role': member.role,
866 'role': member.role,
867 'reasons': member.reasons,
867 'reasons': member.reasons,
868 'allowed_to_update': member.allowed_to_update,
868 'allowed_to_update': member.allowed_to_update,
869 'review_status': member.review_status,
869 'review_status': member.review_status,
870 'review_status_label': member.review_status_label,
870 'review_status_label': member.review_status_label,
871 'user_group': member.user_group,
871 'user_group': member.user_group,
872 'create': false
872 'create': false
873 });
873 });
874
874
875 $(self.controller.$reviewMembers.selector).append(entry)
875 $(self.controller.$reviewMembers.selector).append(entry)
876 }
876 }
877 });
877 });
878
878
879 tooltipActivate();
879 tooltipActivate();
880 },
880 },
881
881
882 edit: function (event) {
882 edit: function (event) {
883 var self = this;
883 var self = this;
884 self.editButton.hide();
884 self.editButton.hide();
885 self.closeButton.show();
885 self.closeButton.show();
886 self.addButton.show();
886 self.addButton.show();
887 $(self.removeButtons.selector).css('visibility', 'visible');
887 $(self.removeButtons.selector).css('visibility', 'visible');
888 // review rules
888 // review rules
889 self.controller.loadReviewRules(this.reviewRules);
889 self.controller.loadReviewRules(this.reviewRules);
890 },
890 },
891
891
892 close: function (event) {
892 close: function (event) {
893 var self = this;
893 var self = this;
894 this.editButton.show();
894 this.editButton.show();
895 this.closeButton.hide();
895 this.closeButton.hide();
896 this.addButton.hide();
896 this.addButton.hide();
897 $(this.removeButtons.selector).css('visibility', 'hidden');
897 $(this.removeButtons.selector).css('visibility', 'hidden');
898 // hide review rules
898 // hide review rules
899 self.controller.hideReviewRules();
899 self.controller.hideReviewRules();
900 }
900 }
901 };
901 };
902
902
903 /**
903 /**
904 * Reviewer display panel
904 * Reviewer display panel
905 */
905 */
906 window.ObserversPanel = {
906 window.ObserversPanel = {
907 editButton: null,
907 editButton: null,
908 closeButton: null,
908 closeButton: null,
909 addButton: null,
909 addButton: null,
910 removeButtons: null,
910 removeButtons: null,
911 reviewRules: null,
911 reviewRules: null,
912 setReviewers: null,
912 setReviewers: null,
913 controller: null,
913 controller: null,
914
914
915 setSelectors: function () {
915 setSelectors: function () {
916 var self = this;
916 var self = this;
917 self.editButton = $('#open_edit_observers');
917 self.editButton = $('#open_edit_observers');
918 self.closeButton =$('#close_edit_observers');
918 self.closeButton =$('#close_edit_observers');
919 self.addButton = $('#add_observer');
919 self.addButton = $('#add_observer');
920 self.removeButtons = $('.observer_member_remove,.observer_member_mandatory_remove');
920 self.removeButtons = $('.observer_member_remove,.observer_member_mandatory_remove');
921 },
921 },
922
922
923 init: function (controller, reviewRules, setReviewers) {
923 init: function (controller, reviewRules, setReviewers) {
924 var self = this;
924 var self = this;
925 self.setSelectors();
925 self.setSelectors();
926
926
927 self.controller = controller;
927 self.controller = controller;
928 self.reviewRules = reviewRules;
928 self.reviewRules = reviewRules;
929 self.setReviewers = setReviewers;
929 self.setReviewers = setReviewers;
930
930
931 self.editButton.on('click', function (e) {
931 self.editButton.on('click', function (e) {
932 self.edit();
932 self.edit();
933 });
933 });
934 self.closeButton.on('click', function (e) {
934 self.closeButton.on('click', function (e) {
935 self.close();
935 self.close();
936 self.renderObservers();
936 self.renderObservers();
937 });
937 });
938
938
939 self.renderObservers();
939 self.renderObservers();
940
940
941 },
941 },
942
942
943 renderObservers: function () {
943 renderObservers: function () {
944 var self = this;
944 var self = this;
945 if (self.setReviewers.observers === undefined) {
945 if (self.setReviewers.observers === undefined) {
946 return
946 return
947 }
947 }
948 if (self.setReviewers.observers.length === 0) {
948 if (self.setReviewers.observers.length === 0) {
949 self.controller.emptyObserversTable('<tr id="observer-empty-msg"><td colspan="6">No observers</td></tr>');
949 self.controller.emptyObserversTable('<tr id="observer-empty-msg"><td colspan="6">No observers</td></tr>');
950 return
950 return
951 }
951 }
952
952
953 self.controller.emptyObserversTable();
953 self.controller.emptyObserversTable();
954
954
955 $.each(self.setReviewers.observers, function (key, val) {
955 $.each(self.setReviewers.observers, function (key, val) {
956 var member = val;
956 var member = val;
957 if (member.role === self.controller.ROLE_OBSERVER) {
957 if (member.role === self.controller.ROLE_OBSERVER) {
958 var entry = renderTemplate('reviewMemberEntry', {
958 var entry = renderTemplate('reviewMemberEntry', {
959 'member': member,
959 'member': member,
960 'mandatory': member.mandatory,
960 'mandatory': member.mandatory,
961 'role': member.role,
961 'role': member.role,
962 'reasons': member.reasons,
962 'reasons': member.reasons,
963 'allowed_to_update': member.allowed_to_update,
963 'allowed_to_update': member.allowed_to_update,
964 'review_status': member.review_status,
964 'review_status': member.review_status,
965 'review_status_label': member.review_status_label,
965 'review_status_label': member.review_status_label,
966 'user_group': member.user_group,
966 'user_group': member.user_group,
967 'create': false
967 'create': false
968 });
968 });
969
969
970 $(self.controller.$observerMembers.selector).append(entry)
970 $(self.controller.$observerMembers.selector).append(entry)
971 }
971 }
972 });
972 });
973
973
974 tooltipActivate();
974 tooltipActivate();
975 },
975 },
976
976
977 edit: function (event) {
977 edit: function (event) {
978 this.editButton.hide();
978 this.editButton.hide();
979 this.closeButton.show();
979 this.closeButton.show();
980 this.addButton.show();
980 this.addButton.show();
981 $(this.removeButtons.selector).css('visibility', 'visible');
981 $(this.removeButtons.selector).css('visibility', 'visible');
982 },
982 },
983
983
984 close: function (event) {
984 close: function (event) {
985 this.editButton.show();
985 this.editButton.show();
986 this.closeButton.hide();
986 this.closeButton.hide();
987 this.addButton.hide();
987 this.addButton.hide();
988 $(this.removeButtons.selector).css('visibility', 'hidden');
988 $(this.removeButtons.selector).css('visibility', 'hidden');
989 }
989 }
990
990
991 };
991 };
992
992
993 window.PRDetails = {
993 window.PRDetails = {
994 editButton: null,
994 editButton: null,
995 closeButton: null,
995 closeButton: null,
996 deleteButton: null,
996 deleteButton: null,
997 viewFields: null,
997 viewFields: null,
998 editFields: null,
998 editFields: null,
999
999
1000 setSelectors: function () {
1000 setSelectors: function () {
1001 var self = this;
1001 var self = this;
1002 self.editButton = $('#open_edit_pullrequest')
1002 self.editButton = $('#open_edit_pullrequest')
1003 self.closeButton = $('#close_edit_pullrequest')
1003 self.closeButton = $('#close_edit_pullrequest')
1004 self.deleteButton = $('#delete_pullrequest')
1004 self.deleteButton = $('#delete_pullrequest')
1005 self.viewFields = $('#pr-desc, #pr-title')
1005 self.viewFields = $('#pr-desc, #pr-title')
1006 self.editFields = $('#pr-desc-edit, #pr-title-edit, .pr-save')
1006 self.editFields = $('#pr-desc-edit, #pr-title-edit, .pr-save')
1007 },
1007 },
1008
1008
1009 init: function () {
1009 init: function () {
1010 var self = this;
1010 var self = this;
1011 self.setSelectors();
1011 self.setSelectors();
1012 self.editButton.on('click', function (e) {
1012 self.editButton.on('click', function (e) {
1013 self.edit();
1013 self.edit();
1014 });
1014 });
1015 self.closeButton.on('click', function (e) {
1015 self.closeButton.on('click', function (e) {
1016 self.view();
1016 self.view();
1017 });
1017 });
1018 },
1018 },
1019
1019
1020 edit: function (event) {
1020 edit: function (event) {
1021 var cmInstance = $('#pr-description-input').get(0).MarkupForm.cm;
1021 var cmInstance = $('#pr-description-input').get(0).MarkupForm.cm;
1022 this.viewFields.hide();
1022 this.viewFields.hide();
1023 this.editButton.hide();
1023 this.editButton.hide();
1024 this.deleteButton.hide();
1024 this.deleteButton.hide();
1025 this.closeButton.show();
1025 this.closeButton.show();
1026 this.editFields.show();
1026 this.editFields.show();
1027 cmInstance.refresh();
1027 cmInstance.refresh();
1028 },
1028 },
1029
1029
1030 view: function (event) {
1030 view: function (event) {
1031 this.editButton.show();
1031 this.editButton.show();
1032 this.deleteButton.show();
1032 this.deleteButton.show();
1033 this.editFields.hide();
1033 this.editFields.hide();
1034 this.closeButton.hide();
1034 this.closeButton.hide();
1035 this.viewFields.show();
1035 this.viewFields.show();
1036 }
1036 }
1037 };
1037 };
1038
1038
1039 /**
1039 /**
1040 * OnLine presence using channelstream
1040 * OnLine presence using channelstream
1041 */
1041 */
1042 window.ReviewerPresenceController = function (channel) {
1042 window.ReviewerPresenceController = function (channel) {
1043 var self = this;
1043 var self = this;
1044 this.channel = channel;
1044 this.channel = channel;
1045 this.users = {};
1045 this.users = {};
1046
1046
1047 this.storeUsers = function (users) {
1047 this.storeUsers = function (users) {
1048 self.users = {}
1048 self.users = {}
1049 $.each(users, function (index, value) {
1049 $.each(users, function (index, value) {
1050 var userId = value.state.id;
1050 var userId = value.state.id;
1051 self.users[userId] = value.state;
1051 self.users[userId] = value.state;
1052 })
1052 })
1053 }
1053 }
1054
1054
1055 this.render = function () {
1055 this.render = function () {
1056 $.each($('.reviewer_entry'), function (index, value) {
1056 $.each($('.reviewer_entry'), function (index, value) {
1057 var userData = $(value).data();
1057 var userData = $(value).data();
1058 if (self.users[userData.reviewerUserId] !== undefined) {
1058 if (self.users[userData.reviewerUserId] !== undefined) {
1059 $(value).find('.presence-state').show();
1059 $(value).find('.presence-state').show();
1060 } else {
1060 } else {
1061 $(value).find('.presence-state').hide();
1061 $(value).find('.presence-state').hide();
1062 }
1062 }
1063 })
1063 })
1064 };
1064 };
1065
1065
1066 this.handlePresence = function (data) {
1066 this.handlePresence = function (data) {
1067 if (data.type == 'presence' && data.channel === self.channel) {
1067 if (data.type == 'presence' && data.channel === self.channel) {
1068 this.storeUsers(data.users);
1068 this.storeUsers(data.users);
1069 this.render()
1069 this.render();
1070 }
1070 }
1071 };
1071 };
1072
1072
1073 this.handleChannelUpdate = function (data) {
1073 this.handleChannelUpdate = function (data) {
1074 if (data.channel === this.channel) {
1074 if (data.channel === this.channel) {
1075 this.storeUsers(data.state.users);
1075 this.storeUsers(data.state.users);
1076 this.render()
1076 this.render();
1077 }
1077 }
1078
1078
1079 };
1079 };
1080
1080
1081 /* subscribe to the current presence */
1081 /* subscribe to the current presence */
1082 $.Topic('/connection_controller/presence').subscribe(this.handlePresence.bind(this));
1082 $.Topic('/connection_controller/presence').subscribe(this.handlePresence.bind(this));
1083 /* subscribe to updates e.g connect/disconnect */
1083 /* subscribe to updates e.g connect/disconnect */
1084 $.Topic('/connection_controller/channel_update').subscribe(this.handleChannelUpdate.bind(this));
1084 $.Topic('/connection_controller/channel_update').subscribe(this.handleChannelUpdate.bind(this));
1085
1085
1086 };
1086 };
1087
1087
1088 window.refreshComments = function (version) {
1088 window.refreshComments = function (version) {
1089 version = version || templateContext.pull_request_data.pull_request_version || '';
1089 version = version || templateContext.pull_request_data.pull_request_version || '';
1090
1090
1091 // Pull request case
1091 // Pull request case
1092 if (templateContext.pull_request_data.pull_request_id !== null) {
1092 if (templateContext.pull_request_data.pull_request_id !== null) {
1093 var params = {
1093 var params = {
1094 'pull_request_id': templateContext.pull_request_data.pull_request_id,
1094 'pull_request_id': templateContext.pull_request_data.pull_request_id,
1095 'repo_name': templateContext.repo_name,
1095 'repo_name': templateContext.repo_name,
1096 'version': version,
1096 'version': version,
1097 };
1097 };
1098 var loadUrl = pyroutes.url('pullrequest_comments', params);
1098 var loadUrl = pyroutes.url('pullrequest_comments', params);
1099 } // commit case
1099 } // commit case
1100 else {
1100 else {
1101 return
1101 return
1102 }
1102 }
1103
1103
1104 var currentIDs = []
1104 var currentIDs = []
1105 $.each($('.comment'), function (idx, element) {
1105 $.each($('.comment'), function (idx, element) {
1106 currentIDs.push($(element).data('commentId'));
1106 currentIDs.push($(element).data('commentId'));
1107 });
1107 });
1108 var data = {"comments": currentIDs};
1108 var data = {"comments": currentIDs};
1109
1109
1110 var $targetElem = $('.comments-content-table');
1110 var $targetElem = $('.comments-content-table');
1111 $targetElem.css('opacity', 0.3);
1111 $targetElem.css('opacity', 0.3);
1112
1112
1113 var success = function (data) {
1113 var success = function (data) {
1114 var $counterElem = $('#comments-count');
1114 var $counterElem = $('#comments-count');
1115 var newCount = $(data).data('counter');
1115 var newCount = $(data).data('counter');
1116 if (newCount !== undefined) {
1116 if (newCount !== undefined) {
1117 var callback = function () {
1117 var callback = function () {
1118 $counterElem.animate({'opacity': 1.00}, 200)
1118 $counterElem.animate({'opacity': 1.00}, 200)
1119 $counterElem.html(newCount);
1119 $counterElem.html(newCount);
1120 };
1120 };
1121 $counterElem.animate({'opacity': 0.15}, 200, callback);
1121 $counterElem.animate({'opacity': 0.15}, 200, callback);
1122 }
1122 }
1123
1123
1124 $targetElem.css('opacity', 1);
1124 $targetElem.css('opacity', 1);
1125 $targetElem.html(data);
1125 $targetElem.html(data);
1126 tooltipActivate();
1126 tooltipActivate();
1127 }
1127 }
1128
1128
1129 ajaxPOST(loadUrl, data, success, null, {})
1129 ajaxPOST(loadUrl, data, success, null, {})
1130
1130
1131 }
1131 }
1132
1132
1133 window.refreshTODOs = function (version) {
1133 window.refreshTODOs = function (version) {
1134 version = version || templateContext.pull_request_data.pull_request_version || '';
1134 version = version || templateContext.pull_request_data.pull_request_version || '';
1135 // Pull request case
1135 // Pull request case
1136 if (templateContext.pull_request_data.pull_request_id !== null) {
1136 if (templateContext.pull_request_data.pull_request_id !== null) {
1137 var params = {
1137 var params = {
1138 'pull_request_id': templateContext.pull_request_data.pull_request_id,
1138 'pull_request_id': templateContext.pull_request_data.pull_request_id,
1139 'repo_name': templateContext.repo_name,
1139 'repo_name': templateContext.repo_name,
1140 'version': version,
1140 'version': version,
1141 };
1141 };
1142 var loadUrl = pyroutes.url('pullrequest_todos', params);
1142 var loadUrl = pyroutes.url('pullrequest_todos', params);
1143 } // commit case
1143 } // commit case
1144 else {
1144 else {
1145 return
1145 return
1146 }
1146 }
1147
1147
1148 var currentIDs = []
1148 var currentIDs = []
1149 $.each($('.comment'), function (idx, element) {
1149 $.each($('.comment'), function (idx, element) {
1150 currentIDs.push($(element).data('commentId'));
1150 currentIDs.push($(element).data('commentId'));
1151 });
1151 });
1152
1152
1153 var data = {"comments": currentIDs};
1153 var data = {"comments": currentIDs};
1154 var $targetElem = $('.todos-content-table');
1154 var $targetElem = $('.todos-content-table');
1155 $targetElem.css('opacity', 0.3);
1155 $targetElem.css('opacity', 0.3);
1156
1156
1157 var success = function (data) {
1157 var success = function (data) {
1158 var $counterElem = $('#todos-count')
1158 var $counterElem = $('#todos-count')
1159 var newCount = $(data).data('counter');
1159 var newCount = $(data).data('counter');
1160 if (newCount !== undefined) {
1160 if (newCount !== undefined) {
1161 var callback = function () {
1161 var callback = function () {
1162 $counterElem.animate({'opacity': 1.00}, 200)
1162 $counterElem.animate({'opacity': 1.00}, 200)
1163 $counterElem.html(newCount);
1163 $counterElem.html(newCount);
1164 };
1164 };
1165 $counterElem.animate({'opacity': 0.15}, 200, callback);
1165 $counterElem.animate({'opacity': 0.15}, 200, callback);
1166 }
1166 }
1167
1167
1168 $targetElem.css('opacity', 1);
1168 $targetElem.css('opacity', 1);
1169 $targetElem.html(data);
1169 $targetElem.html(data);
1170 tooltipActivate();
1170 tooltipActivate();
1171 }
1171 }
1172
1172
1173 ajaxPOST(loadUrl, data, success, null, {})
1173 ajaxPOST(loadUrl, data, success, null, {})
1174
1174
1175 }
1175 }
1176
1176
1177 window.refreshAllComments = function (version) {
1177 window.refreshAllComments = function (version) {
1178 version = version || templateContext.pull_request_data.pull_request_version || '';
1178 version = version || templateContext.pull_request_data.pull_request_version || '';
1179
1179
1180 refreshComments(version);
1180 refreshComments(version);
1181 refreshTODOs(version);
1181 refreshTODOs(version);
1182 };
1182 };
1183
1183
1184 window.refreshDraftComments = function () {
1185 alert('TODO: refresh Draft Comments needs implementation')
1186 };
1187
1184 window.sidebarComment = function (commentId) {
1188 window.sidebarComment = function (commentId) {
1185 var jsonData = $('#commentHovercard{0}'.format(commentId)).data('commentJsonB64');
1189 var jsonData = $('#commentHovercard{0}'.format(commentId)).data('commentJsonB64');
1186 if (!jsonData) {
1190 if (!jsonData) {
1187 return 'Failed to load comment {0}'.format(commentId)
1191 return 'Failed to load comment {0}'.format(commentId)
1188 }
1192 }
1189 var funcData = JSON.parse(atob(jsonData));
1193 var funcData = JSON.parse(atob(jsonData));
1190 return renderTemplate('sideBarCommentHovercard', funcData)
1194 return renderTemplate('sideBarCommentHovercard', funcData)
1191 };
1195 };
@@ -1,560 +1,558 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 ## usage:
2 ## usage:
3 ## <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
3 ## <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
4 ## ${comment.comment_block(comment)}
4 ## ${comment.comment_block(comment)}
5 ##
5 ##
6 <%namespace name="base" file="/base/base.mako"/>
6 <%namespace name="base" file="/base/base.mako"/>
7
7
8 <%!
8 <%!
9 from rhodecode.lib import html_filters
9 from rhodecode.lib import html_filters
10 %>
10 %>
11
11
12
12
13 <%def name="comment_block(comment, inline=False, active_pattern_entries=None, is_new=False)">
13 <%def name="comment_block(comment, inline=False, active_pattern_entries=None, is_new=False)">
14
14
15 <%
15 <%
16 from rhodecode.model.comment import CommentsModel
16 from rhodecode.model.comment import CommentsModel
17 comment_model = CommentsModel()
17 comment_model = CommentsModel()
18
18
19 comment_ver = comment.get_index_version(getattr(c, 'versions', []))
19 comment_ver = comment.get_index_version(getattr(c, 'versions', []))
20 latest_ver = len(getattr(c, 'versions', []))
20 latest_ver = len(getattr(c, 'versions', []))
21 visible_for_user = True
21 visible_for_user = True
22 if comment.draft:
22 if comment.draft:
23 visible_for_user = comment.user_id == c.rhodecode_user.user_id
23 visible_for_user = comment.user_id == c.rhodecode_user.user_id
24 %>
24 %>
25
25
26 % if inline:
26 % if inline:
27 <% outdated_at_ver = comment.outdated_at_version(c.at_version_num) %>
27 <% outdated_at_ver = comment.outdated_at_version(c.at_version_num) %>
28 % else:
28 % else:
29 <% outdated_at_ver = comment.older_than_version(c.at_version_num) %>
29 <% outdated_at_ver = comment.older_than_version(c.at_version_num) %>
30 % endif
30 % endif
31
31
32 % if visible_for_user:
32 % if visible_for_user:
33 <div class="comment
33 <div class="comment
34 ${'comment-inline' if inline else 'comment-general'}
34 ${'comment-inline' if inline else 'comment-general'}
35 ${'comment-outdated' if outdated_at_ver else 'comment-current'}"
35 ${'comment-outdated' if outdated_at_ver else 'comment-current'}"
36 id="comment-${comment.comment_id}"
36 id="comment-${comment.comment_id}"
37 line="${comment.line_no}"
37 line="${comment.line_no}"
38 data-comment-id="${comment.comment_id}"
38 data-comment-id="${comment.comment_id}"
39 data-comment-type="${comment.comment_type}"
39 data-comment-type="${comment.comment_type}"
40 data-comment-draft=${h.json.dumps(comment.draft)}
40 data-comment-draft=${h.json.dumps(comment.draft)}
41 data-comment-renderer="${comment.renderer}"
41 data-comment-renderer="${comment.renderer}"
42 data-comment-text="${comment.text | html_filters.base64,n}"
42 data-comment-text="${comment.text | html_filters.base64,n}"
43 data-comment-f-path="${comment.f_path}"
43 data-comment-f-path="${comment.f_path}"
44 data-comment-line-no="${comment.line_no}"
44 data-comment-line-no="${comment.line_no}"
45 data-comment-inline=${h.json.dumps(inline)}
45 data-comment-inline=${h.json.dumps(inline)}
46 style="${'display: none;' if outdated_at_ver else ''}">
46 style="${'display: none;' if outdated_at_ver else ''}">
47
47
48 <div class="meta">
48 <div class="meta">
49 <div class="comment-type-label">
49 <div class="comment-type-label">
50 % if comment.draft:
50 % if comment.draft:
51 <div class="tooltip comment-draft" title="${_('Draft comments are only visible to the author until submitted')}.">
51 <div class="tooltip comment-draft" title="${_('Draft comments are only visible to the author until submitted')}.">
52 DRAFT
52 DRAFT
53 </div>
53 </div>
54 % elif is_new:
55 <div class="tooltip comment-new" title="${_('This comment was added while you browsed this page')}.">
56 NEW
57 </div>
54 % endif
58 % endif
55
59
56 % if is_new:
57 <div class="tooltip comment-new" title="${_('This comment was added while you browsed this page')}.">
58 NEW
59 </div>
60 % endif
61
62 <div class="comment-label ${comment.comment_type or 'note'}" id="comment-label-${comment.comment_id}">
60 <div class="comment-label ${comment.comment_type or 'note'}" id="comment-label-${comment.comment_id}">
63
61
64 ## TODO COMMENT
62 ## TODO COMMENT
65 % if comment.comment_type == 'todo':
63 % if comment.comment_type == 'todo':
66 % if comment.resolved:
64 % if comment.resolved:
67 <div class="resolved tooltip" title="${_('Resolved by comment #{}').format(comment.resolved.comment_id)}">
65 <div class="resolved tooltip" title="${_('Resolved by comment #{}').format(comment.resolved.comment_id)}">
68 <i class="icon-flag-filled"></i>
66 <i class="icon-flag-filled"></i>
69 <a href="#comment-${comment.resolved.comment_id}">${comment.comment_type}</a>
67 <a href="#comment-${comment.resolved.comment_id}">${comment.comment_type}</a>
70 </div>
68 </div>
71 % else:
69 % else:
72 <div class="resolved tooltip" style="display: none">
70 <div class="resolved tooltip" style="display: none">
73 <span>${comment.comment_type}</span>
71 <span>${comment.comment_type}</span>
74 </div>
72 </div>
75 <div class="resolve tooltip" onclick="return Rhodecode.comments.createResolutionComment(${comment.comment_id});" title="${_('Click to create resolution comment.')}">
73 <div class="resolve tooltip" onclick="return Rhodecode.comments.createResolutionComment(${comment.comment_id});" title="${_('Click to create resolution comment.')}">
76 <i class="icon-flag-filled"></i>
74 <i class="icon-flag-filled"></i>
77 ${comment.comment_type}
75 ${comment.comment_type}
78 </div>
76 </div>
79 % endif
77 % endif
80 ## NOTE COMMENT
78 ## NOTE COMMENT
81 % else:
79 % else:
82 ## RESOLVED NOTE
80 ## RESOLVED NOTE
83 % if comment.resolved_comment:
81 % if comment.resolved_comment:
84 <div class="tooltip" title="${_('This comment resolves TODO #{}').format(comment.resolved_comment.comment_id)}">
82 <div class="tooltip" title="${_('This comment resolves TODO #{}').format(comment.resolved_comment.comment_id)}">
85 fix
83 fix
86 <a href="#comment-${comment.resolved_comment.comment_id}" onclick="Rhodecode.comments.scrollToComment($('#comment-${comment.resolved_comment.comment_id}'), 0, ${h.json.dumps(comment.resolved_comment.outdated)})">
84 <a href="#comment-${comment.resolved_comment.comment_id}" onclick="Rhodecode.comments.scrollToComment($('#comment-${comment.resolved_comment.comment_id}'), 0, ${h.json.dumps(comment.resolved_comment.outdated)})">
87 <span style="text-decoration: line-through">#${comment.resolved_comment.comment_id}</span>
85 <span style="text-decoration: line-through">#${comment.resolved_comment.comment_id}</span>
88 </a>
86 </a>
89 </div>
87 </div>
90 ## STATUS CHANGE NOTE
88 ## STATUS CHANGE NOTE
91 % elif not comment.is_inline and comment.status_change:
89 % elif not comment.is_inline and comment.status_change:
92 <%
90 <%
93 if comment.pull_request:
91 if comment.pull_request:
94 status_change_title = 'Status of review for pull request !{}'.format(comment.pull_request.pull_request_id)
92 status_change_title = 'Status of review for pull request !{}'.format(comment.pull_request.pull_request_id)
95 else:
93 else:
96 status_change_title = 'Status of review for commit {}'.format(h.short_id(comment.commit_id))
94 status_change_title = 'Status of review for commit {}'.format(h.short_id(comment.commit_id))
97 %>
95 %>
98
96
99 <i class="icon-circle review-status-${comment.review_status}"></i>
97 <i class="icon-circle review-status-${comment.review_status}"></i>
100 <div class="changeset-status-lbl tooltip" title="${status_change_title}">
98 <div class="changeset-status-lbl tooltip" title="${status_change_title}">
101 ${comment.review_status_lbl}
99 ${comment.review_status_lbl}
102 </div>
100 </div>
103 % else:
101 % else:
104 <div>
102 <div>
105 <i class="icon-comment"></i>
103 <i class="icon-comment"></i>
106 ${(comment.comment_type or 'note')}
104 ${(comment.comment_type or 'note')}
107 </div>
105 </div>
108 % endif
106 % endif
109 % endif
107 % endif
110
108
111 </div>
109 </div>
112 </div>
110 </div>
113 ## NOTE 0 and .. => because we disable it for now until UI ready
111 ## NOTE 0 and .. => because we disable it for now until UI ready
114 % if 0 and comment.status_change:
112 % if 0 and comment.status_change:
115 <div class="pull-left">
113 <div class="pull-left">
116 <span class="tag authortag tooltip" title="${_('Status from pull request.')}">
114 <span class="tag authortag tooltip" title="${_('Status from pull request.')}">
117 <a href="${h.route_path('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id)}">
115 <a href="${h.route_path('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id)}">
118 ${'!{}'.format(comment.pull_request.pull_request_id)}
116 ${'!{}'.format(comment.pull_request.pull_request_id)}
119 </a>
117 </a>
120 </span>
118 </span>
121 </div>
119 </div>
122 % endif
120 % endif
123 ## Since only author can see drafts, we don't show it
121 ## Since only author can see drafts, we don't show it
124 % if not comment.draft:
122 % if not comment.draft:
125 <div class="author ${'author-inline' if inline else 'author-general'}">
123 <div class="author ${'author-inline' if inline else 'author-general'}">
126 ${base.gravatar_with_user(comment.author.email, 16, tooltip=True)}
124 ${base.gravatar_with_user(comment.author.email, 16, tooltip=True)}
127 </div>
125 </div>
128 % endif
126 % endif
129
127
130 <div class="date">
128 <div class="date">
131 ${h.age_component(comment.modified_at, time_is_local=True)}
129 ${h.age_component(comment.modified_at, time_is_local=True)}
132 </div>
130 </div>
133
131
134 % if comment.pull_request and comment.pull_request.author.user_id == comment.author.user_id:
132 % if comment.pull_request and comment.pull_request.author.user_id == comment.author.user_id:
135 <span class="tag authortag tooltip" title="${_('Pull request author')}">
133 <span class="tag authortag tooltip" title="${_('Pull request author')}">
136 ${_('author')}
134 ${_('author')}
137 </span>
135 </span>
138 % endif
136 % endif
139
137
140 <%
138 <%
141 comment_version_selector = 'comment_versions_{}'.format(comment.comment_id)
139 comment_version_selector = 'comment_versions_{}'.format(comment.comment_id)
142 %>
140 %>
143
141
144 % if comment.history:
142 % if comment.history:
145 <div class="date">
143 <div class="date">
146
144
147 <input id="${comment_version_selector}" name="${comment_version_selector}"
145 <input id="${comment_version_selector}" name="${comment_version_selector}"
148 type="hidden"
146 type="hidden"
149 data-last-version="${comment.history[-1].version}">
147 data-last-version="${comment.history[-1].version}">
150
148
151 <script type="text/javascript">
149 <script type="text/javascript">
152
150
153 var preLoadVersionData = [
151 var preLoadVersionData = [
154 % for comment_history in comment.history:
152 % for comment_history in comment.history:
155 {
153 {
156 id: ${comment_history.comment_history_id},
154 id: ${comment_history.comment_history_id},
157 text: 'v${comment_history.version}',
155 text: 'v${comment_history.version}',
158 action: function () {
156 action: function () {
159 Rhodecode.comments.showVersion(
157 Rhodecode.comments.showVersion(
160 "${comment.comment_id}",
158 "${comment.comment_id}",
161 "${comment_history.comment_history_id}"
159 "${comment_history.comment_history_id}"
162 )
160 )
163 },
161 },
164 comment_version: "${comment_history.version}",
162 comment_version: "${comment_history.version}",
165 comment_author_username: "${comment_history.author.username}",
163 comment_author_username: "${comment_history.author.username}",
166 comment_author_gravatar: "${h.gravatar_url(comment_history.author.email, 16)}",
164 comment_author_gravatar: "${h.gravatar_url(comment_history.author.email, 16)}",
167 comment_created_on: '${h.age_component(comment_history.created_on, time_is_local=True)}',
165 comment_created_on: '${h.age_component(comment_history.created_on, time_is_local=True)}',
168 },
166 },
169 % endfor
167 % endfor
170 ]
168 ]
171 initVersionSelector("#${comment_version_selector}", {results: preLoadVersionData});
169 initVersionSelector("#${comment_version_selector}", {results: preLoadVersionData});
172
170
173 </script>
171 </script>
174
172
175 </div>
173 </div>
176 % else:
174 % else:
177 <div class="date" style="display: none">
175 <div class="date" style="display: none">
178 <input id="${comment_version_selector}" name="${comment_version_selector}"
176 <input id="${comment_version_selector}" name="${comment_version_selector}"
179 type="hidden"
177 type="hidden"
180 data-last-version="0">
178 data-last-version="0">
181 </div>
179 </div>
182 %endif
180 %endif
183
181
184 <div class="comment-links-block">
182 <div class="comment-links-block">
185
183
186 % if inline:
184 % if inline:
187 <a class="pr-version-inline" href="${request.current_route_path(_query=dict(version=comment.pull_request_version_id), _anchor='comment-{}'.format(comment.comment_id))}">
185 <a class="pr-version-inline" href="${request.current_route_path(_query=dict(version=comment.pull_request_version_id), _anchor='comment-{}'.format(comment.comment_id))}">
188 % if outdated_at_ver:
186 % if outdated_at_ver:
189 <strong class="comment-outdated-label">outdated</strong> <code class="tooltip pr-version-num" title="${_('Outdated comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}">${'v{}'.format(comment_ver)}</code>
187 <strong class="comment-outdated-label">outdated</strong> <code class="tooltip pr-version-num" title="${_('Outdated comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}">${'v{}'.format(comment_ver)}</code>
190 <code class="action-divider">|</code>
188 <code class="action-divider">|</code>
191 % elif comment_ver:
189 % elif comment_ver:
192 <code class="tooltip pr-version-num" title="${_('Comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}">${'v{}'.format(comment_ver)}</code>
190 <code class="tooltip pr-version-num" title="${_('Comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}">${'v{}'.format(comment_ver)}</code>
193 <code class="action-divider">|</code>
191 <code class="action-divider">|</code>
194 % endif
192 % endif
195 </a>
193 </a>
196 % else:
194 % else:
197 % if comment_ver:
195 % if comment_ver:
198
196
199 % if comment.outdated:
197 % if comment.outdated:
200 <a class="pr-version"
198 <a class="pr-version"
201 href="?version=${comment.pull_request_version_id}#comment-${comment.comment_id}"
199 href="?version=${comment.pull_request_version_id}#comment-${comment.comment_id}"
202 >
200 >
203 ${_('Outdated comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}
201 ${_('Outdated comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}
204 </a>
202 </a>
205 <code class="action-divider">|</code>
203 <code class="action-divider">|</code>
206 % else:
204 % else:
207 <a class="tooltip pr-version"
205 <a class="tooltip pr-version"
208 title="${_('Comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}"
206 title="${_('Comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}"
209 href="${h.route_path('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id, version=comment.pull_request_version_id)}"
207 href="${h.route_path('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id, version=comment.pull_request_version_id)}"
210 >
208 >
211 <code class="pr-version-num">${'v{}'.format(comment_ver)}</code>
209 <code class="pr-version-num">${'v{}'.format(comment_ver)}</code>
212 </a>
210 </a>
213 <code class="action-divider">|</code>
211 <code class="action-divider">|</code>
214 % endif
212 % endif
215
213
216 % endif
214 % endif
217 % endif
215 % endif
218
216
219 <details class="details-reset details-inline-block">
217 <details class="details-reset details-inline-block">
220 <summary class="noselect"><i class="icon-options cursor-pointer"></i></summary>
218 <summary class="noselect"><i class="icon-options cursor-pointer"></i></summary>
221 <details-menu class="details-dropdown">
219 <details-menu class="details-dropdown">
222
220
223 <div class="dropdown-item">
221 <div class="dropdown-item">
224 ${_('Comment')} #${comment.comment_id}
222 ${_('Comment')} #${comment.comment_id}
225 <span class="pull-right icon-clipboard clipboard-action" data-clipboard-text="${comment_model.get_url(comment,request, permalink=True, anchor='comment-{}'.format(comment.comment_id))}" title="${_('Copy permalink')}"></span>
223 <span class="pull-right icon-clipboard clipboard-action" data-clipboard-text="${comment_model.get_url(comment,request, permalink=True, anchor='comment-{}'.format(comment.comment_id))}" title="${_('Copy permalink')}"></span>
226 </div>
224 </div>
227
225
228 ## show delete comment if it's not a PR (regular comments) or it's PR that is not closed
226 ## show delete comment if it's not a PR (regular comments) or it's PR that is not closed
229 ## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated
227 ## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated
230 %if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())):
228 %if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())):
231 ## permissions to delete
229 ## permissions to delete
232 %if comment.immutable is False and (c.is_super_admin or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id):
230 %if comment.immutable is False and (c.is_super_admin or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id):
233 <div class="dropdown-divider"></div>
231 <div class="dropdown-divider"></div>
234 <div class="dropdown-item">
232 <div class="dropdown-item">
235 <a onclick="return Rhodecode.comments.editComment(this, '${comment.line_no}', '${comment.f_path}');" class="btn btn-link btn-sm edit-comment">${_('Edit')}</a>
233 <a onclick="return Rhodecode.comments.editComment(this, '${comment.line_no}', '${comment.f_path}');" class="btn btn-link btn-sm edit-comment">${_('Edit')}</a>
236 </div>
234 </div>
237 <div class="dropdown-item">
235 <div class="dropdown-item">
238 <a onclick="return Rhodecode.comments.deleteComment(this);" class="btn btn-link btn-sm btn-danger delete-comment">${_('Delete')}</a>
236 <a onclick="return Rhodecode.comments.deleteComment(this);" class="btn btn-link btn-sm btn-danger delete-comment">${_('Delete')}</a>
239 </div>
237 </div>
240 ## Only available in EE edition
238 ## Only available in EE edition
241 % if comment.draft and c.rhodecode_edition_id == 'EE':
239 % if comment.draft and c.rhodecode_edition_id == 'EE':
242 <div class="dropdown-item">
240 <div class="dropdown-item">
243 <a onclick="return Rhodecode.comments.finalizeDrafts([${comment.comment_id}]);" class="btn btn-link btn-sm finalize-draft-comment">${_('Submit draft')}</a>
241 <a onclick="return Rhodecode.comments.finalizeDrafts([${comment.comment_id}]);" class="btn btn-link btn-sm finalize-draft-comment">${_('Submit draft')}</a>
244 </div>
242 </div>
245 % endif
243 % endif
246 %else:
244 %else:
247 <div class="dropdown-divider"></div>
245 <div class="dropdown-divider"></div>
248 <div class="dropdown-item">
246 <div class="dropdown-item">
249 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Edit')}</a>
247 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Edit')}</a>
250 </div>
248 </div>
251 <div class="dropdown-item">
249 <div class="dropdown-item">
252 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Delete')}</a>
250 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Delete')}</a>
253 </div>
251 </div>
254 %endif
252 %endif
255 %else:
253 %else:
256 <div class="dropdown-divider"></div>
254 <div class="dropdown-divider"></div>
257 <div class="dropdown-item">
255 <div class="dropdown-item">
258 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Edit')}</a>
256 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Edit')}</a>
259 </div>
257 </div>
260 <div class="dropdown-item">
258 <div class="dropdown-item">
261 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Delete')}</a>
259 <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Delete')}</a>
262 </div>
260 </div>
263 %endif
261 %endif
264 </details-menu>
262 </details-menu>
265 </details>
263 </details>
266
264
267 <code class="action-divider">|</code>
265 <code class="action-divider">|</code>
268 % if outdated_at_ver:
266 % if outdated_at_ver:
269 <a onclick="return Rhodecode.comments.prevOutdatedComment(this);" class="tooltip prev-comment" title="${_('Jump to the previous outdated comment')}"> <i class="icon-angle-left"></i> </a>
267 <a onclick="return Rhodecode.comments.prevOutdatedComment(this);" class="tooltip prev-comment" title="${_('Jump to the previous outdated comment')}"> <i class="icon-angle-left"></i> </a>
270 <a onclick="return Rhodecode.comments.nextOutdatedComment(this);" class="tooltip next-comment" title="${_('Jump to the next outdated comment')}"> <i class="icon-angle-right"></i></a>
268 <a onclick="return Rhodecode.comments.nextOutdatedComment(this);" class="tooltip next-comment" title="${_('Jump to the next outdated comment')}"> <i class="icon-angle-right"></i></a>
271 % else:
269 % else:
272 <a onclick="return Rhodecode.comments.prevComment(this);" class="tooltip prev-comment" title="${_('Jump to the previous comment')}"> <i class="icon-angle-left"></i></a>
270 <a onclick="return Rhodecode.comments.prevComment(this);" class="tooltip prev-comment" title="${_('Jump to the previous comment')}"> <i class="icon-angle-left"></i></a>
273 <a onclick="return Rhodecode.comments.nextComment(this);" class="tooltip next-comment" title="${_('Jump to the next comment')}"> <i class="icon-angle-right"></i></a>
271 <a onclick="return Rhodecode.comments.nextComment(this);" class="tooltip next-comment" title="${_('Jump to the next comment')}"> <i class="icon-angle-right"></i></a>
274 % endif
272 % endif
275
273
276 </div>
274 </div>
277 </div>
275 </div>
278 <div class="text">
276 <div class="text">
279 ${h.render(comment.text, renderer=comment.renderer, mentions=True, repo_name=getattr(c, 'repo_name', None), active_pattern_entries=active_pattern_entries)}
277 ${h.render(comment.text, renderer=comment.renderer, mentions=True, repo_name=getattr(c, 'repo_name', None), active_pattern_entries=active_pattern_entries)}
280 </div>
278 </div>
281
279
282 </div>
280 </div>
283 % endif
281 % endif
284 </%def>
282 </%def>
285
283
286 ## generate main comments
284 ## generate main comments
287 <%def name="generate_comments(comments, include_pull_request=False, is_pull_request=False)">
285 <%def name="generate_comments(comments, include_pull_request=False, is_pull_request=False)">
288 <%
286 <%
289 active_pattern_entries = h.get_active_pattern_entries(getattr(c, 'repo_name', None))
287 active_pattern_entries = h.get_active_pattern_entries(getattr(c, 'repo_name', None))
290 %>
288 %>
291
289
292 <div class="general-comments" id="comments">
290 <div class="general-comments" id="comments">
293 %for comment in comments:
291 %for comment in comments:
294 <div id="comment-tr-${comment.comment_id}">
292 <div id="comment-tr-${comment.comment_id}">
295 ## only render comments that are not from pull request, or from
293 ## only render comments that are not from pull request, or from
296 ## pull request and a status change
294 ## pull request and a status change
297 %if not comment.pull_request or (comment.pull_request and comment.status_change) or include_pull_request:
295 %if not comment.pull_request or (comment.pull_request and comment.status_change) or include_pull_request:
298 ${comment_block(comment, active_pattern_entries=active_pattern_entries)}
296 ${comment_block(comment, active_pattern_entries=active_pattern_entries)}
299 %endif
297 %endif
300 </div>
298 </div>
301 %endfor
299 %endfor
302 ## to anchor ajax comments
300 ## to anchor ajax comments
303 <div id="injected_page_comments"></div>
301 <div id="injected_page_comments"></div>
304 </div>
302 </div>
305 </%def>
303 </%def>
306
304
307
305
308 <%def name="comments(post_url, cur_status, is_pull_request=False, is_compare=False, change_status=True, form_extras=None)">
306 <%def name="comments(post_url, cur_status, is_pull_request=False, is_compare=False, change_status=True, form_extras=None)">
309
307
310 <div class="comments">
308 <div class="comments">
311 <%
309 <%
312 if is_pull_request:
310 if is_pull_request:
313 placeholder = _('Leave a comment on this Pull Request.')
311 placeholder = _('Leave a comment on this Pull Request.')
314 elif is_compare:
312 elif is_compare:
315 placeholder = _('Leave a comment on {} commits in this range.').format(len(form_extras))
313 placeholder = _('Leave a comment on {} commits in this range.').format(len(form_extras))
316 else:
314 else:
317 placeholder = _('Leave a comment on this Commit.')
315 placeholder = _('Leave a comment on this Commit.')
318 %>
316 %>
319
317
320 % if c.rhodecode_user.username != h.DEFAULT_USER:
318 % if c.rhodecode_user.username != h.DEFAULT_USER:
321 <div class="js-template" id="cb-comment-general-form-template">
319 <div class="js-template" id="cb-comment-general-form-template">
322 ## template generated for injection
320 ## template generated for injection
323 ${comment_form(form_type='general', review_statuses=c.commit_statuses, form_extras=form_extras)}
321 ${comment_form(form_type='general', review_statuses=c.commit_statuses, form_extras=form_extras)}
324 </div>
322 </div>
325
323
326 <div id="cb-comment-general-form-placeholder" class="comment-form ac">
324 <div id="cb-comment-general-form-placeholder" class="comment-form ac">
327 ## inject form here
325 ## inject form here
328 </div>
326 </div>
329 <script type="text/javascript">
327 <script type="text/javascript">
330 var lineNo = 'general';
328 var lineNo = 'general';
331 var resolvesCommentId = null;
329 var resolvesCommentId = null;
332 var generalCommentForm = Rhodecode.comments.createGeneralComment(
330 var generalCommentForm = Rhodecode.comments.createGeneralComment(
333 lineNo, "${placeholder}", resolvesCommentId);
331 lineNo, "${placeholder}", resolvesCommentId);
334
332
335 // set custom success callback on rangeCommit
333 // set custom success callback on rangeCommit
336 % if is_compare:
334 % if is_compare:
337 generalCommentForm.setHandleFormSubmit(function(o) {
335 generalCommentForm.setHandleFormSubmit(function(o) {
338 var self = generalCommentForm;
336 var self = generalCommentForm;
339
337
340 var text = self.cm.getValue();
338 var text = self.cm.getValue();
341 var status = self.getCommentStatus();
339 var status = self.getCommentStatus();
342 var commentType = self.getCommentType();
340 var commentType = self.getCommentType();
343 var isDraft = self.getDraftState();
341 var isDraft = self.getDraftState();
344
342
345 if (text === "" && !status) {
343 if (text === "" && !status) {
346 return;
344 return;
347 }
345 }
348
346
349 // we can pick which commits we want to make the comment by
347 // we can pick which commits we want to make the comment by
350 // selecting them via click on preview pane, this will alter the hidden inputs
348 // selecting them via click on preview pane, this will alter the hidden inputs
351 var cherryPicked = $('#changeset_compare_view_content .compare_select.hl').length > 0;
349 var cherryPicked = $('#changeset_compare_view_content .compare_select.hl').length > 0;
352
350
353 var commitIds = [];
351 var commitIds = [];
354 $('#changeset_compare_view_content .compare_select').each(function(el) {
352 $('#changeset_compare_view_content .compare_select').each(function(el) {
355 var commitId = this.id.replace('row-', '');
353 var commitId = this.id.replace('row-', '');
356 if ($(this).hasClass('hl') || !cherryPicked) {
354 if ($(this).hasClass('hl') || !cherryPicked) {
357 $("input[data-commit-id='{0}']".format(commitId)).val(commitId);
355 $("input[data-commit-id='{0}']".format(commitId)).val(commitId);
358 commitIds.push(commitId);
356 commitIds.push(commitId);
359 } else {
357 } else {
360 $("input[data-commit-id='{0}']".format(commitId)).val('')
358 $("input[data-commit-id='{0}']".format(commitId)).val('')
361 }
359 }
362 });
360 });
363
361
364 self.setActionButtonsDisabled(true);
362 self.setActionButtonsDisabled(true);
365 self.cm.setOption("readOnly", true);
363 self.cm.setOption("readOnly", true);
366 var postData = {
364 var postData = {
367 'text': text,
365 'text': text,
368 'changeset_status': status,
366 'changeset_status': status,
369 'comment_type': commentType,
367 'comment_type': commentType,
370 'draft': isDraft,
368 'draft': isDraft,
371 'commit_ids': commitIds,
369 'commit_ids': commitIds,
372 'csrf_token': CSRF_TOKEN
370 'csrf_token': CSRF_TOKEN
373 };
371 };
374
372
375 var submitSuccessCallback = function(o) {
373 var submitSuccessCallback = function(o) {
376 location.reload(true);
374 location.reload(true);
377 };
375 };
378 var submitFailCallback = function(){
376 var submitFailCallback = function(){
379 self.resetCommentFormState(text)
377 self.resetCommentFormState(text)
380 };
378 };
381 self.submitAjaxPOST(
379 self.submitAjaxPOST(
382 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
380 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
383 });
381 });
384 % endif
382 % endif
385
383
386 </script>
384 </script>
387 % else:
385 % else:
388 ## form state when not logged in
386 ## form state when not logged in
389 <div class="comment-form ac">
387 <div class="comment-form ac">
390
388
391 <div class="comment-area">
389 <div class="comment-area">
392 <div class="comment-area-header">
390 <div class="comment-area-header">
393 <ul class="nav-links clearfix">
391 <ul class="nav-links clearfix">
394 <li class="active">
392 <li class="active">
395 <a class="disabled" href="#edit-btn" disabled="disabled" onclick="return false">${_('Write')}</a>
393 <a class="disabled" href="#edit-btn" disabled="disabled" onclick="return false">${_('Write')}</a>
396 </li>
394 </li>
397 <li class="">
395 <li class="">
398 <a class="disabled" href="#preview-btn" disabled="disabled" onclick="return false">${_('Preview')}</a>
396 <a class="disabled" href="#preview-btn" disabled="disabled" onclick="return false">${_('Preview')}</a>
399 </li>
397 </li>
400 </ul>
398 </ul>
401 </div>
399 </div>
402
400
403 <div class="comment-area-write" style="display: block;">
401 <div class="comment-area-write" style="display: block;">
404 <div id="edit-container">
402 <div id="edit-container">
405 <div style="padding: 20px 0px 0px 0;">
403 <div style="padding: 20px 0px 0px 0;">
406 ${_('You need to be logged in to leave comments.')}
404 ${_('You need to be logged in to leave comments.')}
407 <a href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">${_('Login now')}</a>
405 <a href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">${_('Login now')}</a>
408 </div>
406 </div>
409 </div>
407 </div>
410 <div id="preview-container" class="clearfix" style="display: none;">
408 <div id="preview-container" class="clearfix" style="display: none;">
411 <div id="preview-box" class="preview-box"></div>
409 <div id="preview-box" class="preview-box"></div>
412 </div>
410 </div>
413 </div>
411 </div>
414
412
415 <div class="comment-area-footer">
413 <div class="comment-area-footer">
416 <div class="toolbar">
414 <div class="toolbar">
417 <div class="toolbar-text">
415 <div class="toolbar-text">
418 </div>
416 </div>
419 </div>
417 </div>
420 </div>
418 </div>
421 </div>
419 </div>
422
420
423 <div class="comment-footer">
421 <div class="comment-footer">
424 </div>
422 </div>
425
423
426 </div>
424 </div>
427 % endif
425 % endif
428
426
429 <script type="text/javascript">
427 <script type="text/javascript">
430 bindToggleButtons();
428 bindToggleButtons();
431 </script>
429 </script>
432 </div>
430 </div>
433 </%def>
431 </%def>
434
432
435
433
436 <%def name="comment_form(form_type, form_id='', lineno_id='{1}', review_statuses=None, form_extras=None)">
434 <%def name="comment_form(form_type, form_id='', lineno_id='{1}', review_statuses=None, form_extras=None)">
437
435
438 ## comment injected based on assumption that user is logged in
436 ## comment injected based on assumption that user is logged in
439 <form ${('id="{}"'.format(form_id) if form_id else '') |n} action="#" method="GET">
437 <form ${('id="{}"'.format(form_id) if form_id else '') |n} action="#" method="GET">
440
438
441 <div class="comment-area">
439 <div class="comment-area">
442 <div class="comment-area-header">
440 <div class="comment-area-header">
443 <div class="pull-left">
441 <div class="pull-left">
444 <ul class="nav-links clearfix">
442 <ul class="nav-links clearfix">
445 <li class="active">
443 <li class="active">
446 <a href="#edit-btn" tabindex="-1" id="edit-btn_${lineno_id}">${_('Write')}</a>
444 <a href="#edit-btn" tabindex="-1" id="edit-btn_${lineno_id}">${_('Write')}</a>
447 </li>
445 </li>
448 <li class="">
446 <li class="">
449 <a href="#preview-btn" tabindex="-1" id="preview-btn_${lineno_id}">${_('Preview')}</a>
447 <a href="#preview-btn" tabindex="-1" id="preview-btn_${lineno_id}">${_('Preview')}</a>
450 </li>
448 </li>
451 </ul>
449 </ul>
452 </div>
450 </div>
453 <div class="pull-right">
451 <div class="pull-right">
454 <span class="comment-area-text">${_('Mark as')}:</span>
452 <span class="comment-area-text">${_('Mark as')}:</span>
455 <select class="comment-type" id="comment_type_${lineno_id}" name="comment_type">
453 <select class="comment-type" id="comment_type_${lineno_id}" name="comment_type">
456 % for val in c.visual.comment_types:
454 % for val in c.visual.comment_types:
457 <option value="${val}">${val.upper()}</option>
455 <option value="${val}">${val.upper()}</option>
458 % endfor
456 % endfor
459 </select>
457 </select>
460 </div>
458 </div>
461 </div>
459 </div>
462
460
463 <div class="comment-area-write" style="display: block;">
461 <div class="comment-area-write" style="display: block;">
464 <div id="edit-container_${lineno_id}" style="margin-top: -1px">
462 <div id="edit-container_${lineno_id}" style="margin-top: -1px">
465 <textarea id="text_${lineno_id}" name="text" class="comment-block-ta ac-input"></textarea>
463 <textarea id="text_${lineno_id}" name="text" class="comment-block-ta ac-input"></textarea>
466 </div>
464 </div>
467 <div id="preview-container_${lineno_id}" class="clearfix" style="display: none;">
465 <div id="preview-container_${lineno_id}" class="clearfix" style="display: none;">
468 <div id="preview-box_${lineno_id}" class="preview-box"></div>
466 <div id="preview-box_${lineno_id}" class="preview-box"></div>
469 </div>
467 </div>
470 </div>
468 </div>
471
469
472 <div class="comment-area-footer comment-attachment-uploader">
470 <div class="comment-area-footer comment-attachment-uploader">
473 <div class="toolbar">
471 <div class="toolbar">
474
472
475 <div class="comment-attachment-text">
473 <div class="comment-attachment-text">
476 <div class="dropzone-text">
474 <div class="dropzone-text">
477 ${_("Drag'n Drop files here or")} <span class="link pick-attachment">${_('Choose your files')}</span>.<br>
475 ${_("Drag'n Drop files here or")} <span class="link pick-attachment">${_('Choose your files')}</span>.<br>
478 </div>
476 </div>
479 <div class="dropzone-upload" style="display:none">
477 <div class="dropzone-upload" style="display:none">
480 <i class="icon-spin animate-spin"></i> ${_('uploading...')}
478 <i class="icon-spin animate-spin"></i> ${_('uploading...')}
481 </div>
479 </div>
482 </div>
480 </div>
483
481
484 ## comments dropzone template, empty on purpose
482 ## comments dropzone template, empty on purpose
485 <div style="display: none" class="comment-attachment-uploader-template">
483 <div style="display: none" class="comment-attachment-uploader-template">
486 <div class="dz-file-preview" style="margin: 0">
484 <div class="dz-file-preview" style="margin: 0">
487 <div class="dz-error-message"></div>
485 <div class="dz-error-message"></div>
488 </div>
486 </div>
489 </div>
487 </div>
490
488
491 </div>
489 </div>
492 </div>
490 </div>
493 </div>
491 </div>
494
492
495 <div class="comment-footer">
493 <div class="comment-footer">
496
494
497 ## inject extra inputs into the form
495 ## inject extra inputs into the form
498 % if form_extras and isinstance(form_extras, (list, tuple)):
496 % if form_extras and isinstance(form_extras, (list, tuple)):
499 <div id="comment_form_extras">
497 <div id="comment_form_extras">
500 % for form_ex_el in form_extras:
498 % for form_ex_el in form_extras:
501 ${form_ex_el|n}
499 ${form_ex_el|n}
502 % endfor
500 % endfor
503 </div>
501 </div>
504 % endif
502 % endif
505
503
506 <div class="action-buttons">
504 <div class="action-buttons">
507 % if form_type != 'inline':
505 % if form_type != 'inline':
508 <div class="action-buttons-extra"></div>
506 <div class="action-buttons-extra"></div>
509 % endif
507 % endif
510
508
511 <input class="btn btn-success comment-button-input submit-comment-action" id="save_${lineno_id}" name="save" type="submit" value="${_('Add comment')}" data-is-draft=false onclick="$(this).addClass('submitter')">
509 <input class="btn btn-success comment-button-input submit-comment-action" id="save_${lineno_id}" name="save" type="submit" value="${_('Add comment')}" data-is-draft=false onclick="$(this).addClass('submitter')">
512
510
513 % if form_type == 'inline':
511 % if form_type == 'inline':
514 % if c.rhodecode_edition_id == 'EE':
512 % if c.rhodecode_edition_id == 'EE':
515 ## Disable the button for CE, the "real" validation is in the backend code anyway
513 ## Disable the button for CE, the "real" validation is in the backend code anyway
516 <input class="btn btn-warning comment-button-input submit-draft-action" id="save_draft_${lineno_id}" name="save_draft" type="submit" value="${_('Add draft')}" data-is-draft=true onclick="$(this).addClass('submitter')">
514 <input class="btn btn-warning comment-button-input submit-draft-action" id="save_draft_${lineno_id}" name="save_draft" type="submit" value="${_('Add draft')}" data-is-draft=true onclick="$(this).addClass('submitter')">
517 % else:
515 % else:
518 <input class="tooltip btn btn-warning comment-button-input submit-draft-action disabled" type="submit" disabled="disabled" value="${_('Add draft')}" onclick="return false;" title="Draft comments only available in EE edition of RhodeCode">
516 <input class="btn btn-warning comment-button-input submit-draft-action disabled" disabled="disabled" type="submit" value="${_('Add draft')}" onclick="return false;" title="Draft comments only available in EE edition of RhodeCode">
519 % endif
517 % endif
520 % endif
518 % endif
521
519
522 % if review_statuses:
520 % if review_statuses:
523 <div class="comment-status-box">
521 <div class="comment-status-box">
524 <select id="change_status_${lineno_id}" name="changeset_status">
522 <select id="change_status_${lineno_id}" name="changeset_status">
525 <option></option> ## Placeholder
523 <option></option> ## Placeholder
526 % for status, lbl in review_statuses:
524 % for status, lbl in review_statuses:
527 <option value="${status}" data-status="${status}">${lbl}</option>
525 <option value="${status}" data-status="${status}">${lbl}</option>
528 %if is_pull_request and change_status and status in ('approved', 'rejected'):
526 %if is_pull_request and change_status and status in ('approved', 'rejected'):
529 <option value="${status}_closed" data-status="${status}">${lbl} & ${_('Closed')}</option>
527 <option value="${status}_closed" data-status="${status}">${lbl} & ${_('Closed')}</option>
530 %endif
528 %endif
531 % endfor
529 % endfor
532 </select>
530 </select>
533 </div>
531 </div>
534 % endif
532 % endif
535
533
536 ## inline for has a file, and line-number together with cancel hide button.
534 ## inline for has a file, and line-number together with cancel hide button.
537 % if form_type == 'inline':
535 % if form_type == 'inline':
538 <input type="hidden" name="f_path" value="{0}">
536 <input type="hidden" name="f_path" value="{0}">
539 <input type="hidden" name="line" value="${lineno_id}">
537 <input type="hidden" name="line" value="${lineno_id}">
540 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
538 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
541 <i class="icon-cancel-circled2"></i>
539 <i class="icon-cancel-circled2"></i>
542 </button>
540 </button>
543 % endif
541 % endif
544 </div>
542 </div>
545
543
546 <div class="toolbar-text">
544 <div class="toolbar-text">
547 <% renderer_url = '<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper()) %>
545 <% renderer_url = '<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper()) %>
548 <p>${_('Styling with {} is supported.').format(renderer_url)|n}
546 <p>${_('Styling with {} is supported.').format(renderer_url)|n}
549
547
550 <i class="icon-info-circled tooltip-hovercard"
548 <i class="icon-info-circled tooltip-hovercard"
551 data-hovercard-alt="ALT"
549 data-hovercard-alt="ALT"
552 data-hovercard-url="javascript:commentHelp('${c.visual.default_renderer.upper()}')"
550 data-hovercard-url="javascript:commentHelp('${c.visual.default_renderer.upper()}')"
553 data-comment-json-b64='${h.b64(h.json.dumps({}))}'></i>
551 data-comment-json-b64='${h.b64(h.json.dumps({}))}'></i>
554 </p>
552 </p>
555 </div>
553 </div>
556 </div>
554 </div>
557
555
558 </form>
556 </form>
559
557
560 </%def> No newline at end of file
558 </%def>
@@ -1,1012 +1,1035 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 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
4 <%namespace name="sidebar" file="/base/sidebar.mako"/>
4 <%namespace name="sidebar" file="/base/sidebar.mako"/>
5
5
6
6
7 <%def name="title()">
7 <%def name="title()">
8 ${_('{} Pull Request !{}').format(c.repo_name, c.pull_request.pull_request_id)}
8 ${_('{} Pull Request !{}').format(c.repo_name, c.pull_request.pull_request_id)}
9 %if c.rhodecode_name:
9 %if c.rhodecode_name:
10 &middot; ${h.branding(c.rhodecode_name)}
10 &middot; ${h.branding(c.rhodecode_name)}
11 %endif
11 %endif
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15
15
16 </%def>
16 </%def>
17
17
18 <%def name="menu_bar_nav()">
18 <%def name="menu_bar_nav()">
19 ${self.menu_items(active='repositories')}
19 ${self.menu_items(active='repositories')}
20 </%def>
20 </%def>
21
21
22 <%def name="menu_bar_subnav()">
22 <%def name="menu_bar_subnav()">
23 ${self.repo_menu(active='showpullrequest')}
23 ${self.repo_menu(active='showpullrequest')}
24 </%def>
24 </%def>
25
25
26
26
27 <%def name="main()">
27 <%def name="main()">
28 ## Container to gather extracted Tickets
28 ## Container to gather extracted Tickets
29 <%
29 <%
30 c.referenced_commit_issues = []
30 c.referenced_commit_issues = []
31 c.referenced_desc_issues = []
31 c.referenced_desc_issues = []
32 %>
32 %>
33
33
34 <script type="text/javascript">
34 <script type="text/javascript">
35 // TODO: marcink switch this to pyroutes
35 // TODO: marcink switch this to pyroutes
36 AJAX_COMMENT_DELETE_URL = "${h.route_path('pullrequest_comment_delete',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id,comment_id='__COMMENT_ID__')}";
36 AJAX_COMMENT_DELETE_URL = "${h.route_path('pullrequest_comment_delete',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id,comment_id='__COMMENT_ID__')}";
37 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
37 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
38 templateContext.pull_request_data.pull_request_version = '${request.GET.get('version', '')}';
38 templateContext.pull_request_data.pull_request_version = '${request.GET.get('version', '')}';
39 </script>
39 </script>
40
40
41 <div class="box">
41 <div class="box">
42
42
43 <div class="box pr-summary">
43 <div class="box pr-summary">
44
44
45 <div class="summary-details block-left">
45 <div class="summary-details block-left">
46 <div id="pr-title">
46 <div id="pr-title">
47 % if c.pull_request.is_closed():
47 % if c.pull_request.is_closed():
48 <span class="pr-title-closed-tag tag">${_('Closed')}</span>
48 <span class="pr-title-closed-tag tag">${_('Closed')}</span>
49 % endif
49 % endif
50 <input class="pr-title-input large disabled" disabled="disabled" name="pullrequest_title" type="text" value="${c.pull_request.title}">
50 <input class="pr-title-input large disabled" disabled="disabled" name="pullrequest_title" type="text" value="${c.pull_request.title}">
51 </div>
51 </div>
52 <div id="pr-title-edit" class="input" style="display: none;">
52 <div id="pr-title-edit" class="input" style="display: none;">
53 <input class="pr-title-input large" id="pr-title-input" name="pullrequest_title" type="text" value="${c.pull_request.title}">
53 <input class="pr-title-input large" id="pr-title-input" name="pullrequest_title" type="text" value="${c.pull_request.title}">
54 </div>
54 </div>
55
55
56 <% summary = lambda n:{False:'summary-short'}.get(n) %>
56 <% summary = lambda n:{False:'summary-short'}.get(n) %>
57 <div class="pr-details-title">
57 <div class="pr-details-title">
58 <div class="pull-left">
58 <div class="pull-left">
59 <a href="${h.route_path('pull_requests_global', pull_request_id=c.pull_request.pull_request_id)}">${_('Pull request !{}').format(c.pull_request.pull_request_id)}</a>
59 <a href="${h.route_path('pull_requests_global', pull_request_id=c.pull_request.pull_request_id)}">${_('Pull request !{}').format(c.pull_request.pull_request_id)}</a>
60 ${_('Created on')}
60 ${_('Created on')}
61 <span class="tooltip" title="${_('Last updated on')} ${h.format_date(c.pull_request.updated_on)}">${h.format_date(c.pull_request.created_on)},</span>
61 <span class="tooltip" title="${_('Last updated on')} ${h.format_date(c.pull_request.updated_on)}">${h.format_date(c.pull_request.created_on)},</span>
62 <span class="pr-details-title-author-pref">${_('by')}</span>
62 <span class="pr-details-title-author-pref">${_('by')}</span>
63 </div>
63 </div>
64
64
65 <div class="pull-left">
65 <div class="pull-left">
66 ${self.gravatar_with_user(c.pull_request.author.email, 16, tooltip=True)}
66 ${self.gravatar_with_user(c.pull_request.author.email, 16, tooltip=True)}
67 </div>
67 </div>
68
68
69 %if c.allowed_to_update:
69 %if c.allowed_to_update:
70 <div class="pull-right">
70 <div class="pull-right">
71 <div id="edit_pull_request" class="action_button pr-save" style="display: none;">${_('Update title & description')}</div>
71 <div id="edit_pull_request" class="action_button pr-save" style="display: none;">${_('Update title & description')}</div>
72 <div id="delete_pullrequest" class="action_button pr-save ${('' if c.allowed_to_delete else 'disabled' )}" style="display: none;">
72 <div id="delete_pullrequest" class="action_button pr-save ${('' if c.allowed_to_delete else 'disabled' )}" style="display: none;">
73 % if c.allowed_to_delete:
73 % if c.allowed_to_delete:
74 ${h.secure_form(h.route_path('pullrequest_delete', repo_name=c.pull_request.target_repo.repo_name, pull_request_id=c.pull_request.pull_request_id), request=request)}
74 ${h.secure_form(h.route_path('pullrequest_delete', repo_name=c.pull_request.target_repo.repo_name, pull_request_id=c.pull_request.pull_request_id), request=request)}
75 <input class="btn btn-link btn-danger no-margin" id="remove_${c.pull_request.pull_request_id}" name="remove_${c.pull_request.pull_request_id}"
75 <input class="btn btn-link btn-danger no-margin" id="remove_${c.pull_request.pull_request_id}" name="remove_${c.pull_request.pull_request_id}"
76 onclick="submitConfirm(event, this, _gettext('Confirm to delete this pull request'), _gettext('Delete'), '${'!{}'.format(c.pull_request.pull_request_id)}')"
76 onclick="submitConfirm(event, this, _gettext('Confirm to delete this pull request'), _gettext('Delete'), '${'!{}'.format(c.pull_request.pull_request_id)}')"
77 type="submit" value="${_('Delete pull request')}">
77 type="submit" value="${_('Delete pull request')}">
78 ${h.end_form()}
78 ${h.end_form()}
79 % else:
79 % else:
80 <span class="tooltip" title="${_('Not allowed to delete this pull request')}">${_('Delete pull request')}</span>
80 <span class="tooltip" title="${_('Not allowed to delete this pull request')}">${_('Delete pull request')}</span>
81 % endif
81 % endif
82 </div>
82 </div>
83 <div id="open_edit_pullrequest" class="action_button">${_('Edit')}</div>
83 <div id="open_edit_pullrequest" class="action_button">${_('Edit')}</div>
84 <div id="close_edit_pullrequest" class="action_button" style="display: none;">${_('Cancel')}</div>
84 <div id="close_edit_pullrequest" class="action_button" style="display: none;">${_('Cancel')}</div>
85 </div>
85 </div>
86
86
87 %endif
87 %endif
88 </div>
88 </div>
89
89
90 <div id="pr-desc" class="input" title="${_('Rendered using {} renderer').format(c.renderer)}">
90 <div id="pr-desc" class="input" title="${_('Rendered using {} renderer').format(c.renderer)}">
91 ${h.render(c.pull_request.description, renderer=c.renderer, repo_name=c.repo_name, issues_container=c.referenced_desc_issues)}
91 ${h.render(c.pull_request.description, renderer=c.renderer, repo_name=c.repo_name, issues_container=c.referenced_desc_issues)}
92 </div>
92 </div>
93
93
94 <div id="pr-desc-edit" class="input textarea" style="display: none;">
94 <div id="pr-desc-edit" class="input textarea" style="display: none;">
95 <input id="pr-renderer-input" type="hidden" name="description_renderer" value="${c.visual.default_renderer}">
95 <input id="pr-renderer-input" type="hidden" name="description_renderer" value="${c.visual.default_renderer}">
96 ${dt.markup_form('pr-description-input', form_text=c.pull_request.description)}
96 ${dt.markup_form('pr-description-input', form_text=c.pull_request.description)}
97 </div>
97 </div>
98
98
99 <div id="summary" class="fields pr-details-content">
99 <div id="summary" class="fields pr-details-content">
100
100
101 ## source
101 ## source
102 <div class="field">
102 <div class="field">
103 <div class="label-pr-detail">
103 <div class="label-pr-detail">
104 <label>${_('Commit flow')}:</label>
104 <label>${_('Commit flow')}:</label>
105 </div>
105 </div>
106 <div class="input">
106 <div class="input">
107 <div class="pr-commit-flow">
107 <div class="pr-commit-flow">
108 ## Source
108 ## Source
109 %if c.pull_request.source_ref_parts.type == 'branch':
109 %if c.pull_request.source_ref_parts.type == 'branch':
110 <a href="${h.route_path('repo_commits', repo_name=c.pull_request.source_repo.repo_name, _query=dict(branch=c.pull_request.source_ref_parts.name))}"><code class="pr-source-info">${c.pull_request.source_ref_parts.type}:${c.pull_request.source_ref_parts.name}</code></a>
110 <a href="${h.route_path('repo_commits', repo_name=c.pull_request.source_repo.repo_name, _query=dict(branch=c.pull_request.source_ref_parts.name))}"><code class="pr-source-info">${c.pull_request.source_ref_parts.type}:${c.pull_request.source_ref_parts.name}</code></a>
111 %else:
111 %else:
112 <code class="pr-source-info">${'{}:{}'.format(c.pull_request.source_ref_parts.type, c.pull_request.source_ref_parts.name)}</code>
112 <code class="pr-source-info">${'{}:{}'.format(c.pull_request.source_ref_parts.type, c.pull_request.source_ref_parts.name)}</code>
113 %endif
113 %endif
114 ${_('of')} <a href="${h.route_path('repo_summary', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.repo_name}</a>
114 ${_('of')} <a href="${h.route_path('repo_summary', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.repo_name}</a>
115 &rarr;
115 &rarr;
116 ## Target
116 ## Target
117 %if c.pull_request.target_ref_parts.type == 'branch':
117 %if c.pull_request.target_ref_parts.type == 'branch':
118 <a href="${h.route_path('repo_commits', repo_name=c.pull_request.target_repo.repo_name, _query=dict(branch=c.pull_request.target_ref_parts.name))}"><code class="pr-target-info">${c.pull_request.target_ref_parts.type}:${c.pull_request.target_ref_parts.name}</code></a>
118 <a href="${h.route_path('repo_commits', repo_name=c.pull_request.target_repo.repo_name, _query=dict(branch=c.pull_request.target_ref_parts.name))}"><code class="pr-target-info">${c.pull_request.target_ref_parts.type}:${c.pull_request.target_ref_parts.name}</code></a>
119 %else:
119 %else:
120 <code class="pr-target-info">${'{}:{}'.format(c.pull_request.target_ref_parts.type, c.pull_request.target_ref_parts.name)}</code>
120 <code class="pr-target-info">${'{}:{}'.format(c.pull_request.target_ref_parts.type, c.pull_request.target_ref_parts.name)}</code>
121 %endif
121 %endif
122
122
123 ${_('of')} <a href="${h.route_path('repo_summary', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.repo_name}</a>
123 ${_('of')} <a href="${h.route_path('repo_summary', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.repo_name}</a>
124
124
125 <a class="source-details-action" href="#expand-source-details" onclick="return toggleElement(this, '.source-details')" data-toggle-on='<i class="icon-angle-down">more details</i>' data-toggle-off='<i class="icon-angle-up">less details</i>'>
125 <a class="source-details-action" href="#expand-source-details" onclick="return toggleElement(this, '.source-details')" data-toggle-on='<i class="icon-angle-down">more details</i>' data-toggle-off='<i class="icon-angle-up">less details</i>'>
126 <i class="icon-angle-down">more details</i>
126 <i class="icon-angle-down">more details</i>
127 </a>
127 </a>
128
128
129 </div>
129 </div>
130
130
131 <div class="source-details" style="display: none">
131 <div class="source-details" style="display: none">
132
132
133 <ul>
133 <ul>
134
134
135 ## common ancestor
135 ## common ancestor
136 <li>
136 <li>
137 ${_('Common ancestor')}:
137 ${_('Common ancestor')}:
138 % if c.ancestor_commit:
138 % if c.ancestor_commit:
139 <a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=c.ancestor_commit.raw_id)}">${h.show_id(c.ancestor_commit)}</a>
139 <a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=c.ancestor_commit.raw_id)}">${h.show_id(c.ancestor_commit)}</a>
140 % else:
140 % else:
141 ${_('not available')}
141 ${_('not available')}
142 % endif
142 % endif
143 </li>
143 </li>
144
144
145 ## pull url
145 ## pull url
146 <li>
146 <li>
147 %if h.is_hg(c.pull_request.source_repo):
147 %if h.is_hg(c.pull_request.source_repo):
148 <% clone_url = 'hg pull -r {} {}'.format(h.short_id(c.source_ref), c.pull_request.source_repo.clone_url()) %>
148 <% clone_url = 'hg pull -r {} {}'.format(h.short_id(c.source_ref), c.pull_request.source_repo.clone_url()) %>
149 %elif h.is_git(c.pull_request.source_repo):
149 %elif h.is_git(c.pull_request.source_repo):
150 <% clone_url = 'git pull {} {}'.format(c.pull_request.source_repo.clone_url(), c.pull_request.source_ref_parts.name) %>
150 <% clone_url = 'git pull {} {}'.format(c.pull_request.source_repo.clone_url(), c.pull_request.source_ref_parts.name) %>
151 %endif
151 %endif
152
152
153 <span>${_('Pull changes from source')}</span>: <input type="text" class="input-monospace pr-pullinfo" value="${clone_url}" readonly="readonly">
153 <span>${_('Pull changes from source')}</span>: <input type="text" class="input-monospace pr-pullinfo" value="${clone_url}" readonly="readonly">
154 <i class="tooltip icon-clipboard clipboard-action pull-right pr-pullinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the pull url')}"></i>
154 <i class="tooltip icon-clipboard clipboard-action pull-right pr-pullinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the pull url')}"></i>
155 </li>
155 </li>
156
156
157 ## Shadow repo
157 ## Shadow repo
158 <li>
158 <li>
159 % if not c.pull_request.is_closed() and c.pull_request.shadow_merge_ref:
159 % if not c.pull_request.is_closed() and c.pull_request.shadow_merge_ref:
160 %if h.is_hg(c.pull_request.target_repo):
160 %if h.is_hg(c.pull_request.target_repo):
161 <% clone_url = 'hg clone --update {} {} pull-request-{}'.format(c.pull_request.shadow_merge_ref.name, c.shadow_clone_url, c.pull_request.pull_request_id) %>
161 <% clone_url = 'hg clone --update {} {} pull-request-{}'.format(c.pull_request.shadow_merge_ref.name, c.shadow_clone_url, c.pull_request.pull_request_id) %>
162 %elif h.is_git(c.pull_request.target_repo):
162 %elif h.is_git(c.pull_request.target_repo):
163 <% clone_url = 'git clone --branch {} {} pull-request-{}'.format(c.pull_request.shadow_merge_ref.name, c.shadow_clone_url, c.pull_request.pull_request_id) %>
163 <% clone_url = 'git clone --branch {} {} pull-request-{}'.format(c.pull_request.shadow_merge_ref.name, c.shadow_clone_url, c.pull_request.pull_request_id) %>
164 %endif
164 %endif
165
165
166 <span class="tooltip" title="${_('Clone repository in its merged state using shadow repository')}">${_('Clone from shadow repository')}</span>: <input type="text" class="input-monospace pr-mergeinfo" value="${clone_url}" readonly="readonly">
166 <span class="tooltip" title="${_('Clone repository in its merged state using shadow repository')}">${_('Clone from shadow repository')}</span>: <input type="text" class="input-monospace pr-mergeinfo" value="${clone_url}" readonly="readonly">
167 <i class="tooltip icon-clipboard clipboard-action pull-right pr-mergeinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the clone url')}"></i>
167 <i class="tooltip icon-clipboard clipboard-action pull-right pr-mergeinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the clone url')}"></i>
168
168
169 % else:
169 % else:
170 <div class="">
170 <div class="">
171 ${_('Shadow repository data not available')}.
171 ${_('Shadow repository data not available')}.
172 </div>
172 </div>
173 % endif
173 % endif
174 </li>
174 </li>
175
175
176 </ul>
176 </ul>
177
177
178 </div>
178 </div>
179
179
180 </div>
180 </div>
181
181
182 </div>
182 </div>
183
183
184 ## versions
184 ## versions
185 <div class="field">
185 <div class="field">
186 <div class="label-pr-detail">
186 <div class="label-pr-detail">
187 <label>${_('Versions')}:</label>
187 <label>${_('Versions')}:</label>
188 </div>
188 </div>
189
189
190 <% outdated_comm_count_ver = len(c.inline_versions[None]['outdated']) %>
190 <% outdated_comm_count_ver = len(c.inline_versions[None]['outdated']) %>
191 <% general_outdated_comm_count_ver = len(c.comment_versions[None]['outdated']) %>
191 <% general_outdated_comm_count_ver = len(c.comment_versions[None]['outdated']) %>
192
192
193 <div class="pr-versions">
193 <div class="pr-versions">
194 % if c.show_version_changes:
194 % if c.show_version_changes:
195 <% outdated_comm_count_ver = len(c.inline_versions[c.at_version_num]['outdated']) %>
195 <% outdated_comm_count_ver = len(c.inline_versions[c.at_version_num]['outdated']) %>
196 <% general_outdated_comm_count_ver = len(c.comment_versions[c.at_version_num]['outdated']) %>
196 <% general_outdated_comm_count_ver = len(c.comment_versions[c.at_version_num]['outdated']) %>
197 ${_ungettext('{} version available for this pull request, ', '{} versions available for this pull request, ', len(c.versions)).format(len(c.versions))}
197 ${_ungettext('{} version available for this pull request, ', '{} versions available for this pull request, ', len(c.versions)).format(len(c.versions))}
198 <a id="show-pr-versions" onclick="return versionController.toggleVersionView(this)" href="#show-pr-versions"
198 <a id="show-pr-versions" onclick="return versionController.toggleVersionView(this)" href="#show-pr-versions"
199 data-toggle-on="${_('show versions')}."
199 data-toggle-on="${_('show versions')}."
200 data-toggle-off="${_('hide versions')}.">
200 data-toggle-off="${_('hide versions')}.">
201 ${_('show versions')}.
201 ${_('show versions')}.
202 </a>
202 </a>
203 <table>
203 <table>
204 ## SHOW ALL VERSIONS OF PR
204 ## SHOW ALL VERSIONS OF PR
205 <% ver_pr = None %>
205 <% ver_pr = None %>
206
206
207 % for data in reversed(list(enumerate(c.versions, 1))):
207 % for data in reversed(list(enumerate(c.versions, 1))):
208 <% ver_pos = data[0] %>
208 <% ver_pos = data[0] %>
209 <% ver = data[1] %>
209 <% ver = data[1] %>
210 <% ver_pr = ver.pull_request_version_id %>
210 <% ver_pr = ver.pull_request_version_id %>
211 <% display_row = '' if c.at_version and (c.at_version_num == ver_pr or c.from_version_num == ver_pr) else 'none' %>
211 <% display_row = '' if c.at_version and (c.at_version_num == ver_pr or c.from_version_num == ver_pr) else 'none' %>
212
212
213 <tr class="version-pr" style="display: ${display_row}">
213 <tr class="version-pr" style="display: ${display_row}">
214 <td>
214 <td>
215 <code>
215 <code>
216 <a href="${request.current_route_path(_query=dict(version=ver_pr or 'latest'))}">v${ver_pos}</a>
216 <a href="${request.current_route_path(_query=dict(version=ver_pr or 'latest'))}">v${ver_pos}</a>
217 </code>
217 </code>
218 </td>
218 </td>
219 <td>
219 <td>
220 <input ${('checked="checked"' if c.from_version_index == ver_pr else '')} class="compare-radio-button" type="radio" name="ver_source" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
220 <input ${('checked="checked"' if c.from_version_index == ver_pr else '')} class="compare-radio-button" type="radio" name="ver_source" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
221 <input ${('checked="checked"' if c.at_version_num == ver_pr else '')} class="compare-radio-button" type="radio" name="ver_target" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
221 <input ${('checked="checked"' if c.at_version_num == ver_pr else '')} class="compare-radio-button" type="radio" name="ver_target" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
222 </td>
222 </td>
223 <td>
223 <td>
224 <% review_status = c.review_versions[ver_pr].status if ver_pr in c.review_versions else 'not_reviewed' %>
224 <% review_status = c.review_versions[ver_pr].status if ver_pr in c.review_versions else 'not_reviewed' %>
225 <i class="tooltip icon-circle review-status-${review_status}" title="${_('Your review status at this version')}"></i>
225 <i class="tooltip icon-circle review-status-${review_status}" title="${_('Your review status at this version')}"></i>
226
226
227 </td>
227 </td>
228 <td>
228 <td>
229 % if c.at_version_num != ver_pr:
229 % if c.at_version_num != ver_pr:
230 <i class="tooltip icon-comment" title="${_('Comments from pull request version v{0}').format(ver_pos)}"></i>
230 <i class="tooltip icon-comment" title="${_('Comments from pull request version v{0}').format(ver_pos)}"></i>
231 <code>
231 <code>
232 General:${len(c.comment_versions[ver_pr]['at'])} / Inline:${len(c.inline_versions[ver_pr]['at'])}
232 General:${len(c.comment_versions[ver_pr]['at'])} / Inline:${len(c.inline_versions[ver_pr]['at'])}
233 </code>
233 </code>
234 % endif
234 % endif
235 </td>
235 </td>
236 <td>
236 <td>
237 ##<code>${ver.source_ref_parts.commit_id[:6]}</code>
237 ##<code>${ver.source_ref_parts.commit_id[:6]}</code>
238 </td>
238 </td>
239 <td>
239 <td>
240 <code>${h.age_component(ver.updated_on, time_is_local=True, tooltip=False)}</code>
240 <code>${h.age_component(ver.updated_on, time_is_local=True, tooltip=False)}</code>
241 </td>
241 </td>
242 </tr>
242 </tr>
243 % endfor
243 % endfor
244
244
245 <tr>
245 <tr>
246 <td colspan="6">
246 <td colspan="6">
247 <button id="show-version-diff" onclick="return versionController.showVersionDiff()" class="btn btn-sm" style="display: none"
247 <button id="show-version-diff" onclick="return versionController.showVersionDiff()" class="btn btn-sm" style="display: none"
248 data-label-text-locked="${_('select versions to show changes')}"
248 data-label-text-locked="${_('select versions to show changes')}"
249 data-label-text-diff="${_('show changes between versions')}"
249 data-label-text-diff="${_('show changes between versions')}"
250 data-label-text-show="${_('show pull request for this version')}"
250 data-label-text-show="${_('show pull request for this version')}"
251 >
251 >
252 ${_('select versions to show changes')}
252 ${_('select versions to show changes')}
253 </button>
253 </button>
254 </td>
254 </td>
255 </tr>
255 </tr>
256 </table>
256 </table>
257 % else:
257 % else:
258 <div>
258 <div>
259 ${_('Pull request versions not available')}.
259 ${_('Pull request versions not available')}.
260 </div>
260 </div>
261 % endif
261 % endif
262 </div>
262 </div>
263 </div>
263 </div>
264
264
265 </div>
265 </div>
266
266
267 </div>
267 </div>
268
268
269
269
270 </div>
270 </div>
271
271
272 </div>
272 </div>
273
273
274 <div class="box">
274 <div class="box">
275
275
276 % if c.state_progressing:
276 % if c.state_progressing:
277
277
278 <h2 style="text-align: center">
278 <h2 style="text-align: center">
279 ${_('Cannot show diff when pull request state is changing. Current progress state')}: <span class="tag tag-merge-state-${c.pull_request.state}">${c.pull_request.state}</span>
279 ${_('Cannot show diff when pull request state is changing. Current progress state')}: <span class="tag tag-merge-state-${c.pull_request.state}">${c.pull_request.state}</span>
280
280
281 % if c.is_super_admin:
281 % if c.is_super_admin:
282 <br/>
282 <br/>
283 If you think this is an error try <a href="${h.current_route_path(request, force_state='created')}">forced state reset</a> to <span class="tag tag-merge-state-created">created</span> state.
283 If you think this is an error try <a href="${h.current_route_path(request, force_state='created')}">forced state reset</a> to <span class="tag tag-merge-state-created">created</span> state.
284 % endif
284 % endif
285 </h2>
285 </h2>
286
286
287 % else:
287 % else:
288
288
289 ## Diffs rendered here
289 ## Diffs rendered here
290 <div class="table" >
290 <div class="table" >
291 <div id="changeset_compare_view_content">
291 <div id="changeset_compare_view_content">
292 ##CS
292 ##CS
293 % if c.missing_requirements:
293 % if c.missing_requirements:
294 <div class="box">
294 <div class="box">
295 <div class="alert alert-warning">
295 <div class="alert alert-warning">
296 <div>
296 <div>
297 <strong>${_('Missing requirements:')}</strong>
297 <strong>${_('Missing requirements:')}</strong>
298 ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')}
298 ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')}
299 </div>
299 </div>
300 </div>
300 </div>
301 </div>
301 </div>
302 % elif c.missing_commits:
302 % elif c.missing_commits:
303 <div class="box">
303 <div class="box">
304 <div class="alert alert-warning">
304 <div class="alert alert-warning">
305 <div>
305 <div>
306 <strong>${_('Missing commits')}:</strong>
306 <strong>${_('Missing commits')}:</strong>
307 ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}<br/>
307 ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}<br/>
308 ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}<br/>
308 ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}<br/>
309 ${_('Consider doing a `force update commits` in case you think this is an error.')}
309 ${_('Consider doing a `force update commits` in case you think this is an error.')}
310 </div>
310 </div>
311 </div>
311 </div>
312 </div>
312 </div>
313 % elif c.pr_merge_source_commit.changed and not c.pull_request.is_closed():
313 % elif c.pr_merge_source_commit.changed and not c.pull_request.is_closed():
314 <div class="box">
314 <div class="box">
315 <div class="alert alert-info">
315 <div class="alert alert-info">
316 <div>
316 <div>
317 <strong>${_('There are new changes for `{}:{}` in source repository, please consider updating this pull request.').format(c.pr_merge_source_commit.ref_spec.type, c.pr_merge_source_commit.ref_spec.name)}</strong>
317 <strong>${_('There are new changes for `{}:{}` in source repository, please consider updating this pull request.').format(c.pr_merge_source_commit.ref_spec.type, c.pr_merge_source_commit.ref_spec.name)}</strong>
318 </div>
318 </div>
319 </div>
319 </div>
320 </div>
320 </div>
321 % endif
321 % endif
322
322
323 <div class="compare_view_commits_title">
323 <div class="compare_view_commits_title">
324 % if not c.compare_mode:
324 % if not c.compare_mode:
325
325
326 % if c.at_version_index:
326 % if c.at_version_index:
327 <h4>
327 <h4>
328 ${_('Showing changes at v{}, commenting is disabled.').format(c.at_version_index)}
328 ${_('Showing changes at v{}, commenting is disabled.').format(c.at_version_index)}
329 </h4>
329 </h4>
330 % endif
330 % endif
331
331
332 <div class="pull-left">
332 <div class="pull-left">
333 <div class="btn-group">
333 <div class="btn-group">
334 <a class="${('collapsed' if c.collapse_all_commits else '')}" href="#expand-commits" onclick="toggleCommitExpand(this); return false" data-toggle-commits-cnt=${len(c.commit_ranges)} >
334 <a class="${('collapsed' if c.collapse_all_commits else '')}" href="#expand-commits" onclick="toggleCommitExpand(this); return false" data-toggle-commits-cnt=${len(c.commit_ranges)} >
335 % if c.collapse_all_commits:
335 % if c.collapse_all_commits:
336 <i class="icon-plus-squared-alt icon-no-margin"></i>
336 <i class="icon-plus-squared-alt icon-no-margin"></i>
337 ${_ungettext('Expand {} commit', 'Expand {} commits', len(c.commit_ranges)).format(len(c.commit_ranges))}
337 ${_ungettext('Expand {} commit', 'Expand {} commits', len(c.commit_ranges)).format(len(c.commit_ranges))}
338 % else:
338 % else:
339 <i class="icon-minus-squared-alt icon-no-margin"></i>
339 <i class="icon-minus-squared-alt icon-no-margin"></i>
340 ${_ungettext('Collapse {} commit', 'Collapse {} commits', len(c.commit_ranges)).format(len(c.commit_ranges))}
340 ${_ungettext('Collapse {} commit', 'Collapse {} commits', len(c.commit_ranges)).format(len(c.commit_ranges))}
341 % endif
341 % endif
342 </a>
342 </a>
343 </div>
343 </div>
344 </div>
344 </div>
345
345
346 <div class="pull-right">
346 <div class="pull-right">
347 % if c.allowed_to_update and not c.pull_request.is_closed():
347 % if c.allowed_to_update and not c.pull_request.is_closed():
348
348
349 <div class="btn-group btn-group-actions">
349 <div class="btn-group btn-group-actions">
350 <a id="update_commits" class="btn btn-primary no-margin" onclick="updateController.updateCommits(this); return false">
350 <a id="update_commits" class="btn btn-primary no-margin" onclick="updateController.updateCommits(this); return false">
351 ${_('Update commits')}
351 ${_('Update commits')}
352 </a>
352 </a>
353
353
354 <a id="update_commits_switcher" class="tooltip btn btn-primary btn-more-option" data-toggle="dropdown" aria-pressed="false" role="button" title="${_('more update options')}">
354 <a id="update_commits_switcher" class="tooltip btn btn-primary btn-more-option" data-toggle="dropdown" aria-pressed="false" role="button" title="${_('more update options')}">
355 <i class="icon-down"></i>
355 <i class="icon-down"></i>
356 </a>
356 </a>
357
357
358 <div class="btn-action-switcher-container right-align" id="update-commits-switcher">
358 <div class="btn-action-switcher-container right-align" id="update-commits-switcher">
359 <ul class="btn-action-switcher" role="menu" style="min-width: 300px;">
359 <ul class="btn-action-switcher" role="menu" style="min-width: 300px;">
360 <li>
360 <li>
361 <a href="#forceUpdate" onclick="updateController.forceUpdateCommits(this); return false">
361 <a href="#forceUpdate" onclick="updateController.forceUpdateCommits(this); return false">
362 ${_('Force update commits')}
362 ${_('Force update commits')}
363 </a>
363 </a>
364 <div class="action-help-block">
364 <div class="action-help-block">
365 ${_('Update commits and force refresh this pull request.')}
365 ${_('Update commits and force refresh this pull request.')}
366 </div>
366 </div>
367 </li>
367 </li>
368 </ul>
368 </ul>
369 </div>
369 </div>
370 </div>
370 </div>
371
371
372 % else:
372 % else:
373 <a class="tooltip btn disabled pull-right" disabled="disabled" title="${_('Update is disabled for current view')}">${_('Update commits')}</a>
373 <a class="tooltip btn disabled pull-right" disabled="disabled" title="${_('Update is disabled for current view')}">${_('Update commits')}</a>
374 % endif
374 % endif
375
375
376 </div>
376 </div>
377 % endif
377 % endif
378 </div>
378 </div>
379
379
380 % if not c.missing_commits:
380 % if not c.missing_commits:
381 ## COMPARE RANGE DIFF MODE
381 ## COMPARE RANGE DIFF MODE
382 % if c.compare_mode:
382 % if c.compare_mode:
383 % if c.at_version:
383 % if c.at_version:
384 <h4>
384 <h4>
385 ${_('Commits and changes between v{ver_from} and {ver_to} of this pull request, commenting is disabled').format(ver_from=c.from_version_index, ver_to=c.at_version_index if c.at_version_index else 'latest')}:
385 ${_('Commits and changes between v{ver_from} and {ver_to} of this pull request, commenting is disabled').format(ver_from=c.from_version_index, ver_to=c.at_version_index if c.at_version_index else 'latest')}:
386 </h4>
386 </h4>
387
387
388 <div class="subtitle-compare">
388 <div class="subtitle-compare">
389 ${_('commits added: {}, removed: {}').format(len(c.commit_changes_summary.added), len(c.commit_changes_summary.removed))}
389 ${_('commits added: {}, removed: {}').format(len(c.commit_changes_summary.added), len(c.commit_changes_summary.removed))}
390 </div>
390 </div>
391
391
392 <div class="container">
392 <div class="container">
393 <table class="rctable compare_view_commits">
393 <table class="rctable compare_view_commits">
394 <tr>
394 <tr>
395 <th></th>
395 <th></th>
396 <th>${_('Time')}</th>
396 <th>${_('Time')}</th>
397 <th>${_('Author')}</th>
397 <th>${_('Author')}</th>
398 <th>${_('Commit')}</th>
398 <th>${_('Commit')}</th>
399 <th></th>
399 <th></th>
400 <th>${_('Description')}</th>
400 <th>${_('Description')}</th>
401 </tr>
401 </tr>
402
402
403 % for c_type, commit in c.commit_changes:
403 % for c_type, commit in c.commit_changes:
404 % if c_type in ['a', 'r']:
404 % if c_type in ['a', 'r']:
405 <%
405 <%
406 if c_type == 'a':
406 if c_type == 'a':
407 cc_title = _('Commit added in displayed changes')
407 cc_title = _('Commit added in displayed changes')
408 elif c_type == 'r':
408 elif c_type == 'r':
409 cc_title = _('Commit removed in displayed changes')
409 cc_title = _('Commit removed in displayed changes')
410 else:
410 else:
411 cc_title = ''
411 cc_title = ''
412 %>
412 %>
413 <tr id="row-${commit.raw_id}" commit_id="${commit.raw_id}" class="compare_select">
413 <tr id="row-${commit.raw_id}" commit_id="${commit.raw_id}" class="compare_select">
414 <td>
414 <td>
415 <div class="commit-change-indicator color-${c_type}-border">
415 <div class="commit-change-indicator color-${c_type}-border">
416 <div class="commit-change-content color-${c_type} tooltip" title="${h.tooltip(cc_title)}">
416 <div class="commit-change-content color-${c_type} tooltip" title="${h.tooltip(cc_title)}">
417 ${c_type.upper()}
417 ${c_type.upper()}
418 </div>
418 </div>
419 </div>
419 </div>
420 </td>
420 </td>
421 <td class="td-time">
421 <td class="td-time">
422 ${h.age_component(commit.date)}
422 ${h.age_component(commit.date)}
423 </td>
423 </td>
424 <td class="td-user">
424 <td class="td-user">
425 ${base.gravatar_with_user(commit.author, 16, tooltip=True)}
425 ${base.gravatar_with_user(commit.author, 16, tooltip=True)}
426 </td>
426 </td>
427 <td class="td-hash">
427 <td class="td-hash">
428 <code>
428 <code>
429 <a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=commit.raw_id)}">
429 <a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=commit.raw_id)}">
430 r${commit.idx}:${h.short_id(commit.raw_id)}
430 r${commit.idx}:${h.short_id(commit.raw_id)}
431 </a>
431 </a>
432 ${h.hidden('revisions', commit.raw_id)}
432 ${h.hidden('revisions', commit.raw_id)}
433 </code>
433 </code>
434 </td>
434 </td>
435 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_( 'Expand commit message')}" onclick="commitsController.expandCommit(this); return false">
435 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_( 'Expand commit message')}" onclick="commitsController.expandCommit(this); return false">
436 <i class="icon-expand-linked"></i>
436 <i class="icon-expand-linked"></i>
437 </td>
437 </td>
438 <td class="mid td-description">
438 <td class="mid td-description">
439 <div class="log-container truncate-wrap">
439 <div class="log-container truncate-wrap">
440 <div class="message truncate" id="c-${commit.raw_id}" data-message-raw="${commit.message}">${h.urlify_commit_message(commit.message, c.repo_name, issues_container=c.referenced_commit_issues)}</div>
440 <div class="message truncate" id="c-${commit.raw_id}" data-message-raw="${commit.message}">${h.urlify_commit_message(commit.message, c.repo_name, issues_container=c.referenced_commit_issues)}</div>
441 </div>
441 </div>
442 </td>
442 </td>
443 </tr>
443 </tr>
444 % endif
444 % endif
445 % endfor
445 % endfor
446 </table>
446 </table>
447 </div>
447 </div>
448
448
449 % endif
449 % endif
450
450
451 ## Regular DIFF
451 ## Regular DIFF
452 % else:
452 % else:
453 <%include file="/compare/compare_commits.mako" />
453 <%include file="/compare/compare_commits.mako" />
454 % endif
454 % endif
455
455
456 <div class="cs_files">
456 <div class="cs_files">
457 <%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/>
457 <%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/>
458
458
459 <%
459 <%
460 pr_menu_data = {
460 pr_menu_data = {
461 'outdated_comm_count_ver': outdated_comm_count_ver,
461 'outdated_comm_count_ver': outdated_comm_count_ver,
462 'pull_request': c.pull_request
462 'pull_request': c.pull_request
463 }
463 }
464 %>
464 %>
465
465
466 ${cbdiffs.render_diffset_menu(c.diffset, range_diff_on=c.range_diff_on, pull_request_menu=pr_menu_data)}
466 ${cbdiffs.render_diffset_menu(c.diffset, range_diff_on=c.range_diff_on, pull_request_menu=pr_menu_data)}
467
467
468 % if c.range_diff_on:
468 % if c.range_diff_on:
469 % for commit in c.commit_ranges:
469 % for commit in c.commit_ranges:
470 ${cbdiffs.render_diffset(
470 ${cbdiffs.render_diffset(
471 c.changes[commit.raw_id],
471 c.changes[commit.raw_id],
472 commit=commit, use_comments=True,
472 commit=commit, use_comments=True,
473 collapse_when_files_over=5,
473 collapse_when_files_over=5,
474 disable_new_comments=True,
474 disable_new_comments=True,
475 deleted_files_comments=c.deleted_files_comments,
475 deleted_files_comments=c.deleted_files_comments,
476 inline_comments=c.inline_comments,
476 inline_comments=c.inline_comments,
477 pull_request_menu=pr_menu_data, show_todos=False)}
477 pull_request_menu=pr_menu_data, show_todos=False)}
478 % endfor
478 % endfor
479 % else:
479 % else:
480 ${cbdiffs.render_diffset(
480 ${cbdiffs.render_diffset(
481 c.diffset, use_comments=True,
481 c.diffset, use_comments=True,
482 collapse_when_files_over=30,
482 collapse_when_files_over=30,
483 disable_new_comments=not c.allowed_to_comment,
483 disable_new_comments=not c.allowed_to_comment,
484 deleted_files_comments=c.deleted_files_comments,
484 deleted_files_comments=c.deleted_files_comments,
485 inline_comments=c.inline_comments,
485 inline_comments=c.inline_comments,
486 pull_request_menu=pr_menu_data, show_todos=False)}
486 pull_request_menu=pr_menu_data, show_todos=False)}
487 % endif
487 % endif
488
488
489 </div>
489 </div>
490 % else:
490 % else:
491 ## skipping commits we need to clear the view for missing commits
491 ## skipping commits we need to clear the view for missing commits
492 <div style="clear:both;"></div>
492 <div style="clear:both;"></div>
493 % endif
493 % endif
494
494
495 </div>
495 </div>
496 </div>
496 </div>
497
497
498 ## template for inline comment form
498 ## template for inline comment form
499 <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
499 <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
500
500
501 ## comments heading with count
501 ## comments heading with count
502 <div class="comments-heading">
502 <div class="comments-heading">
503 <i class="icon-comment"></i>
503 <i class="icon-comment"></i>
504 ${_('General Comments')} ${len(c.comments)}
504 ${_('General Comments')} ${len(c.comments)}
505 </div>
505 </div>
506
506
507 ## render general comments
507 ## render general comments
508 <div id="comment-tr-show">
508 <div id="comment-tr-show">
509 % if general_outdated_comm_count_ver:
509 % if general_outdated_comm_count_ver:
510 <div class="info-box">
510 <div class="info-box">
511 % if general_outdated_comm_count_ver == 1:
511 % if general_outdated_comm_count_ver == 1:
512 ${_('there is {num} general comment from older versions').format(num=general_outdated_comm_count_ver)},
512 ${_('there is {num} general comment from older versions').format(num=general_outdated_comm_count_ver)},
513 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show it')}</a>
513 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show it')}</a>
514 % else:
514 % else:
515 ${_('there are {num} general comments from older versions').format(num=general_outdated_comm_count_ver)},
515 ${_('there are {num} general comments from older versions').format(num=general_outdated_comm_count_ver)},
516 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show them')}</a>
516 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show them')}</a>
517 % endif
517 % endif
518 </div>
518 </div>
519 % endif
519 % endif
520 </div>
520 </div>
521
521
522 ${comment.generate_comments(c.comments, include_pull_request=True, is_pull_request=True)}
522 ${comment.generate_comments(c.comments, include_pull_request=True, is_pull_request=True)}
523
523
524 % if not c.pull_request.is_closed():
524 % if not c.pull_request.is_closed():
525 ## main comment form and it status
525 ## main comment form and it status
526 ${comment.comments(h.route_path('pullrequest_comment_create', repo_name=c.repo_name,
526 ${comment.comments(h.route_path('pullrequest_comment_create', repo_name=c.repo_name,
527 pull_request_id=c.pull_request.pull_request_id),
527 pull_request_id=c.pull_request.pull_request_id),
528 c.pull_request_review_status,
528 c.pull_request_review_status,
529 is_pull_request=True, change_status=c.allowed_to_change_status)}
529 is_pull_request=True, change_status=c.allowed_to_change_status)}
530
530
531 ## merge status, and merge action
531 ## merge status, and merge action
532 <div class="pull-request-merge">
532 <div class="pull-request-merge">
533 <%include file="/pullrequests/pullrequest_merge_checks.mako"/>
533 <%include file="/pullrequests/pullrequest_merge_checks.mako"/>
534 </div>
534 </div>
535
535
536 %endif
536 %endif
537
537
538 % endif
538 % endif
539 </div>
539 </div>
540
540
541
541
542 ### NAV SIDEBAR
542 ### NAV SIDEBAR
543 <aside class="right-sidebar right-sidebar-expanded" id="pr-nav-sticky" style="display: none">
543 <aside class="right-sidebar right-sidebar-expanded" id="pr-nav-sticky" style="display: none">
544 <div class="sidenav navbar__inner" >
544 <div class="sidenav navbar__inner" >
545 ## TOGGLE
545 ## TOGGLE
546 <div class="sidebar-toggle" onclick="toggleSidebar(); return false">
546 <div class="sidebar-toggle" onclick="toggleSidebar(); return false">
547 <a href="#toggleSidebar" class="grey-link-action">
547 <a href="#toggleSidebar" class="grey-link-action">
548
548
549 </a>
549 </a>
550 </div>
550 </div>
551
551
552 ## CONTENT
552 ## CONTENT
553 <div class="sidebar-content">
553 <div class="sidebar-content">
554
554
555 ## Drafts
556 % if c.rhodecode_edition_id == 'EE':
557 <div class="sidebar-element clear-both">
558 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Drafts')}">
559 <i class="icon-comment icon-draft"></i>
560 <span id="comments-count">${0}</span>
561 </div>
562
563 <div class="right-sidebar-expanded-state pr-details-title">
564 <span class="sidebar-heading noselect">
565 <i class="icon-comment icon-draft"></i>
566 ${_('Drafts')}
567 </span>
568 </div>
569
570 <div id="drafts" class="right-sidebar-expanded-state pr-details-content reviewers">
571 ## members redering block
572
573
574 ???
575
576
577 ## end members redering block
578
579 </div>
580
581 </div>
582 % endif
583
555 ## RULES SUMMARY/RULES
584 ## RULES SUMMARY/RULES
556 <div class="sidebar-element clear-both">
585 <div class="sidebar-element clear-both">
557 <% vote_title = _ungettext(
586 <% vote_title = _ungettext(
558 'Status calculated based on votes from {} reviewer',
587 'Status calculated based on votes from {} reviewer',
559 'Status calculated based on votes from {} reviewers', c.reviewers_count).format(c.reviewers_count)
588 'Status calculated based on votes from {} reviewers', c.reviewers_count).format(c.reviewers_count)
560 %>
589 %>
561
590
562 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${vote_title}">
591 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${vote_title}">
563 <i class="icon-circle review-status-${c.pull_request_review_status}"></i>
592 <i class="icon-circle review-status-${c.pull_request_review_status}"></i>
564 ${c.reviewers_count}
593 ${c.reviewers_count}
565 </div>
594 </div>
566
595
567 ## REVIEW RULES
596 ## REVIEW RULES
568 <div id="review_rules" style="display: none" class="">
597 <div id="review_rules" style="display: none" class="">
569 <div class="right-sidebar-expanded-state pr-details-title">
598 <div class="right-sidebar-expanded-state pr-details-title">
570 <span class="sidebar-heading">
599 <span class="sidebar-heading">
571 ${_('Reviewer rules')}
600 ${_('Reviewer rules')}
572 </span>
601 </span>
573
602
574 </div>
603 </div>
575 <div class="pr-reviewer-rules">
604 <div class="pr-reviewer-rules">
576 ## review rules will be appended here, by default reviewers logic
605 ## review rules will be appended here, by default reviewers logic
577 </div>
606 </div>
578 <input id="review_data" type="hidden" name="review_data" value="">
607 <input id="review_data" type="hidden" name="review_data" value="">
579 </div>
608 </div>
580
609
581 ## REVIEWERS
610 ## REVIEWERS
582 <div class="right-sidebar-expanded-state pr-details-title">
611 <div class="right-sidebar-expanded-state pr-details-title">
583 <span class="tooltip sidebar-heading" title="${vote_title}">
612 <span class="tooltip sidebar-heading" title="${vote_title}">
584 <i class="icon-circle review-status-${c.pull_request_review_status}"></i>
613 <i class="icon-circle review-status-${c.pull_request_review_status}"></i>
585 ${_('Reviewers')}
614 ${_('Reviewers')}
586 </span>
615 </span>
587 %if c.allowed_to_update:
616 %if c.allowed_to_update:
588 <span id="open_edit_reviewers" class="block-right action_button last-item">${_('Edit')}</span>
617 <span id="open_edit_reviewers" class="block-right action_button last-item">${_('Edit')}</span>
589 <span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
618 <span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
590 %else:
619 %else:
591 <span id="open_edit_reviewers" class="block-right action_button last-item">${_('Show rules')}</span>
620 <span id="open_edit_reviewers" class="block-right action_button last-item">${_('Show rules')}</span>
592 <span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
621 <span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
593 %endif
622 %endif
594 </div>
623 </div>
595
624
596 <div id="reviewers" class="right-sidebar-expanded-state pr-details-content reviewers">
625 <div id="reviewers" class="right-sidebar-expanded-state pr-details-content reviewers">
597
626
598 ## members redering block
627 ## members redering block
599 <input type="hidden" name="__start__" value="review_members:sequence">
628 <input type="hidden" name="__start__" value="review_members:sequence">
600
629
601 <table id="review_members" class="group_members">
630 <table id="review_members" class="group_members">
602 ## This content is loaded via JS and ReviewersPanel
631 ## This content is loaded via JS and ReviewersPanel
603 </table>
632 </table>
604
633
605 <input type="hidden" name="__end__" value="review_members:sequence">
634 <input type="hidden" name="__end__" value="review_members:sequence">
606 ## end members redering block
635 ## end members redering block
607
636
608 %if not c.pull_request.is_closed():
637 %if not c.pull_request.is_closed():
609 <div id="add_reviewer" class="ac" style="display: none;">
638 <div id="add_reviewer" class="ac" style="display: none;">
610 %if c.allowed_to_update:
639 %if c.allowed_to_update:
611 % if not c.forbid_adding_reviewers:
640 % if not c.forbid_adding_reviewers:
612 <div id="add_reviewer_input" class="reviewer_ac" style="width: 240px">
641 <div id="add_reviewer_input" class="reviewer_ac" style="width: 240px">
613 <input class="ac-input" id="user" name="user" placeholder="${_('Add reviewer or reviewer group')}" type="text" autocomplete="off">
642 <input class="ac-input" id="user" name="user" placeholder="${_('Add reviewer or reviewer group')}" type="text" autocomplete="off">
614 <div id="reviewers_container"></div>
643 <div id="reviewers_container"></div>
615 </div>
644 </div>
616 % endif
645 % endif
617 <div class="pull-right" style="margin-bottom: 15px">
646 <div class="pull-right" style="margin-bottom: 15px">
618 <button data-role="reviewer" id="update_reviewers" class="btn btn-small no-margin">${_('Save Changes')}</button>
647 <button data-role="reviewer" id="update_reviewers" class="btn btn-small no-margin">${_('Save Changes')}</button>
619 </div>
648 </div>
620 %endif
649 %endif
621 </div>
650 </div>
622 %endif
651 %endif
623 </div>
652 </div>
624 </div>
653 </div>
625
654
626 ## OBSERVERS
655 ## OBSERVERS
627 % if c.rhodecode_edition_id == 'EE':
656 % if c.rhodecode_edition_id == 'EE':
628 <div class="sidebar-element clear-both">
657 <div class="sidebar-element clear-both">
629 <% vote_title = _ungettext(
658 <% vote_title = _ungettext(
630 '{} observer without voting right.',
659 '{} observer without voting right.',
631 '{} observers without voting right.', c.observers_count).format(c.observers_count)
660 '{} observers without voting right.', c.observers_count).format(c.observers_count)
632 %>
661 %>
633
662
634 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${vote_title}">
663 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${vote_title}">
635 <i class="icon-circle-thin"></i>
664 <i class="icon-circle-thin"></i>
636 ${c.observers_count}
665 ${c.observers_count}
637 </div>
666 </div>
638
667
639 <div class="right-sidebar-expanded-state pr-details-title">
668 <div class="right-sidebar-expanded-state pr-details-title">
640 <span class="tooltip sidebar-heading" title="${vote_title}">
669 <span class="tooltip sidebar-heading" title="${vote_title}">
641 <i class="icon-circle-thin"></i>
670 <i class="icon-circle-thin"></i>
642 ${_('Observers')}
671 ${_('Observers')}
643 </span>
672 </span>
644 %if c.allowed_to_update:
673 %if c.allowed_to_update:
645 <span id="open_edit_observers" class="block-right action_button last-item">${_('Edit')}</span>
674 <span id="open_edit_observers" class="block-right action_button last-item">${_('Edit')}</span>
646 <span id="close_edit_observers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
675 <span id="close_edit_observers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
647 %endif
676 %endif
648 </div>
677 </div>
649
678
650 <div id="observers" class="right-sidebar-expanded-state pr-details-content reviewers">
679 <div id="observers" class="right-sidebar-expanded-state pr-details-content reviewers">
651 ## members redering block
680 ## members redering block
652 <input type="hidden" name="__start__" value="observer_members:sequence">
681 <input type="hidden" name="__start__" value="observer_members:sequence">
653
682
654 <table id="observer_members" class="group_members">
683 <table id="observer_members" class="group_members">
655 ## This content is loaded via JS and ReviewersPanel
684 ## This content is loaded via JS and ReviewersPanel
656 </table>
685 </table>
657
686
658 <input type="hidden" name="__end__" value="observer_members:sequence">
687 <input type="hidden" name="__end__" value="observer_members:sequence">
659 ## end members redering block
688 ## end members redering block
660
689
661 %if not c.pull_request.is_closed():
690 %if not c.pull_request.is_closed():
662 <div id="add_observer" class="ac" style="display: none;">
691 <div id="add_observer" class="ac" style="display: none;">
663 %if c.allowed_to_update:
692 %if c.allowed_to_update:
664 % if not c.forbid_adding_reviewers or 1:
693 % if not c.forbid_adding_reviewers or 1:
665 <div id="add_reviewer_input" class="reviewer_ac" style="width: 240px" >
694 <div id="add_reviewer_input" class="reviewer_ac" style="width: 240px" >
666 <input class="ac-input" id="observer" name="observer" placeholder="${_('Add observer or observer group')}" type="text" autocomplete="off">
695 <input class="ac-input" id="observer" name="observer" placeholder="${_('Add observer or observer group')}" type="text" autocomplete="off">
667 <div id="observers_container"></div>
696 <div id="observers_container"></div>
668 </div>
697 </div>
669 % endif
698 % endif
670 <div class="pull-right" style="margin-bottom: 15px">
699 <div class="pull-right" style="margin-bottom: 15px">
671 <button data-role="observer" id="update_observers" class="btn btn-small no-margin">${_('Save Changes')}</button>
700 <button data-role="observer" id="update_observers" class="btn btn-small no-margin">${_('Save Changes')}</button>
672 </div>
701 </div>
673 %endif
702 %endif
674 </div>
703 </div>
675 %endif
704 %endif
676 </div>
705 </div>
677 </div>
706 </div>
678 % endif
707 % endif
679
708
680 ## TODOs
709 ## TODOs
681 <div class="sidebar-element clear-both">
710 <div class="sidebar-element clear-both">
682 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="TODOs">
711 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="TODOs">
683 <i class="icon-flag-filled"></i>
712 <i class="icon-flag-filled"></i>
684 <span id="todos-count">${len(c.unresolved_comments)}</span>
713 <span id="todos-count">${len(c.unresolved_comments)}</span>
685 </div>
714 </div>
686
715
687 <div class="right-sidebar-expanded-state pr-details-title">
716 <div class="right-sidebar-expanded-state pr-details-title">
688 ## Only show unresolved, that is only what matters
717 ## Only show unresolved, that is only what matters
689 <span class="sidebar-heading noselect" onclick="refreshTODOs(); return false">
718 <span class="sidebar-heading noselect" onclick="refreshTODOs(); return false">
690 <i class="icon-flag-filled"></i>
719 <i class="icon-flag-filled"></i>
691 TODOs
720 TODOs
692 </span>
721 </span>
693
722
694 % if not c.at_version:
723 % if not c.at_version:
695 % if c.resolved_comments:
724 % if c.resolved_comments:
696 <span class="block-right action_button last-item noselect" onclick="$('.unresolved-todo-text').toggle(); return toggleElement(this, '.resolved-todo');" data-toggle-on="Show resolved" data-toggle-off="Hide resolved">Show resolved</span>
725 <span class="block-right action_button last-item noselect" onclick="$('.unresolved-todo-text').toggle(); return toggleElement(this, '.resolved-todo');" data-toggle-on="Show resolved" data-toggle-off="Hide resolved">Show resolved</span>
697 % else:
726 % else:
698 <span class="block-right last-item noselect">Show resolved</span>
727 <span class="block-right last-item noselect">Show resolved</span>
699 % endif
728 % endif
700 % endif
729 % endif
701 </div>
730 </div>
702
731
703 <div class="right-sidebar-expanded-state pr-details-content">
732 <div class="right-sidebar-expanded-state pr-details-content">
704
733
705 % if c.at_version:
734 % if c.at_version:
706 <table>
735 <table>
707 <tr>
736 <tr>
708 <td class="unresolved-todo-text">${_('TODOs unavailable when browsing versions')}.</td>
737 <td class="unresolved-todo-text">${_('TODOs unavailable when browsing versions')}.</td>
709 </tr>
738 </tr>
710 </table>
739 </table>
711 % else:
740 % else:
712 % if c.unresolved_comments + c.resolved_comments:
741 % if c.unresolved_comments + c.resolved_comments:
713 ${sidebar.comments_table(c.unresolved_comments + c.resolved_comments, len(c.unresolved_comments), todo_comments=True)}
742 ${sidebar.comments_table(c.unresolved_comments + c.resolved_comments, len(c.unresolved_comments), todo_comments=True)}
714 % else:
743 % else:
715 <table>
744 <table>
716 <tr>
745 <tr>
717 <td>
746 <td>
718 ${_('No TODOs yet')}
747 ${_('No TODOs yet')}
719 </td>
748 </td>
720 </tr>
749 </tr>
721 </table>
750 </table>
722 % endif
751 % endif
723 % endif
752 % endif
724 </div>
753 </div>
725 </div>
754 </div>
726
755
727 ## COMMENTS
756 ## COMMENTS
728 <div class="sidebar-element clear-both">
757 <div class="sidebar-element clear-both">
729 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Comments')}">
758 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Comments')}">
730 <i class="icon-comment" style="color: #949494"></i>
759 <i class="icon-comment" style="color: #949494"></i>
731 <span id="comments-count">${len(c.inline_comments_flat+c.comments)}</span>
760 <span id="comments-count">${len(c.inline_comments_flat+c.comments)}</span>
732 <span class="display-none" id="general-comments-count">${len(c.comments)}</span>
761 <span class="display-none" id="general-comments-count">${len(c.comments)}</span>
733 <span class="display-none" id="inline-comments-count">${len(c.inline_comments_flat)}</span>
762 <span class="display-none" id="inline-comments-count">${len(c.inline_comments_flat)}</span>
734 </div>
763 </div>
735
764
736 <div class="right-sidebar-expanded-state pr-details-title">
765 <div class="right-sidebar-expanded-state pr-details-title">
737 <span class="sidebar-heading noselect" onclick="refreshComments(); return false">
766 <span class="sidebar-heading noselect" onclick="refreshComments(); return false">
738 <i class="icon-comment" style="color: #949494"></i>
767 <i class="icon-comment" style="color: #949494"></i>
739 ${_('Comments')}
768 ${_('Comments')}
740
769
741 ## % if outdated_comm_count_ver:
770 ## % if outdated_comm_count_ver:
742 ## <a href="#" onclick="showOutdated(); Rhodecode.comments.nextOutdatedComment(); return false;">
771 ## <a href="#" onclick="showOutdated(); Rhodecode.comments.nextOutdatedComment(); return false;">
743 ## (${_("{} Outdated").format(outdated_comm_count_ver)})
772 ## (${_("{} Outdated").format(outdated_comm_count_ver)})
744 ## </a>
773 ## </a>
745 ## <a href="#" class="showOutdatedComments" onclick="showOutdated(this); return false;"> | ${_('show outdated')}</a>
774 ## <a href="#" class="showOutdatedComments" onclick="showOutdated(this); return false;"> | ${_('show outdated')}</a>
746 ## <a href="#" class="hideOutdatedComments" style="display: none" onclick="hideOutdated(this); return false;"> | ${_('hide outdated')}</a>
775 ## <a href="#" class="hideOutdatedComments" style="display: none" onclick="hideOutdated(this); return false;"> | ${_('hide outdated')}</a>
747
776
748 ## % else:
777 ## % else:
749 ## (${_("{} Outdated").format(outdated_comm_count_ver)})
778 ## (${_("{} Outdated").format(outdated_comm_count_ver)})
750 ## % endif
779 ## % endif
751
780
752 </span>
781 </span>
753
782
754 % if outdated_comm_count_ver:
783 % if outdated_comm_count_ver:
755 <span class="block-right action_button last-item noselect" onclick="return toggleElement(this, '.hidden-comment');" data-toggle-on="Show outdated" data-toggle-off="Hide outdated">Show outdated</span>
784 <span class="block-right action_button last-item noselect" onclick="return toggleElement(this, '.hidden-comment');" data-toggle-on="Show outdated" data-toggle-off="Hide outdated">Show outdated</span>
756 % else:
785 % else:
757 <span class="block-right last-item noselect">Show hidden</span>
786 <span class="block-right last-item noselect">Show hidden</span>
758 % endif
787 % endif
759
788
760 </div>
789 </div>
761
790
762 <div class="right-sidebar-expanded-state pr-details-content">
791 <div class="right-sidebar-expanded-state pr-details-content">
763 % if c.inline_comments_flat + c.comments:
792 % if c.inline_comments_flat + c.comments:
764 ${sidebar.comments_table(c.inline_comments_flat + c.comments, len(c.inline_comments_flat+c.comments))}
793 ${sidebar.comments_table(c.inline_comments_flat + c.comments, len(c.inline_comments_flat+c.comments))}
765 % else:
794 % else:
766 <table>
795 <table>
767 <tr>
796 <tr>
768 <td>
797 <td>
769 ${_('No Comments yet')}
798 ${_('No Comments yet')}
770 </td>
799 </td>
771 </tr>
800 </tr>
772 </table>
801 </table>
773 % endif
802 % endif
774 </div>
803 </div>
775
804
776 </div>
805 </div>
777
806
778 ## Referenced Tickets
807 ## Referenced Tickets
779 <div class="sidebar-element clear-both">
808 <div class="sidebar-element clear-both">
780 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Referenced Tickets')}">
809 <div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Referenced Tickets')}">
781 <i class="icon-info-circled"></i>
810 <i class="icon-info-circled"></i>
782 ${(len(c.referenced_desc_issues) + len(c.referenced_commit_issues))}
811 ${(len(c.referenced_desc_issues) + len(c.referenced_commit_issues))}
783 </div>
812 </div>
784
813
785 <div class="right-sidebar-expanded-state pr-details-title">
814 <div class="right-sidebar-expanded-state pr-details-title">
786 <span class="sidebar-heading">
815 <span class="sidebar-heading">
787 <i class="icon-info-circled"></i>
816 <i class="icon-info-circled"></i>
788 ${_('Referenced Tickets')}
817 ${_('Referenced Tickets')}
789 </span>
818 </span>
790 </div>
819 </div>
791 <div class="right-sidebar-expanded-state pr-details-content">
820 <div class="right-sidebar-expanded-state pr-details-content">
792 <table>
821 <table>
793
822
794 <tr><td><code>${_('In pull request description')}:</code></td></tr>
823 <tr><td><code>${_('In pull request description')}:</code></td></tr>
795 % if c.referenced_desc_issues:
824 % if c.referenced_desc_issues:
796 % for ticket_dict in sorted(c.referenced_desc_issues):
825 % for ticket_dict in sorted(c.referenced_desc_issues):
797 <tr>
826 <tr>
798 <td>
827 <td>
799 <a href="${ticket_dict.get('url')}">
828 <a href="${ticket_dict.get('url')}">
800 ${ticket_dict.get('id')}
829 ${ticket_dict.get('id')}
801 </a>
830 </a>
802 </td>
831 </td>
803 </tr>
832 </tr>
804 % endfor
833 % endfor
805 % else:
834 % else:
806 <tr>
835 <tr>
807 <td>
836 <td>
808 ${_('No Ticket data found.')}
837 ${_('No Ticket data found.')}
809 </td>
838 </td>
810 </tr>
839 </tr>
811 % endif
840 % endif
812
841
813 <tr><td style="padding-top: 10px"><code>${_('In commit messages')}:</code></td></tr>
842 <tr><td style="padding-top: 10px"><code>${_('In commit messages')}:</code></td></tr>
814 % if c.referenced_commit_issues:
843 % if c.referenced_commit_issues:
815 % for ticket_dict in sorted(c.referenced_commit_issues):
844 % for ticket_dict in sorted(c.referenced_commit_issues):
816 <tr>
845 <tr>
817 <td>
846 <td>
818 <a href="${ticket_dict.get('url')}">
847 <a href="${ticket_dict.get('url')}">
819 ${ticket_dict.get('id')}
848 ${ticket_dict.get('id')}
820 </a>
849 </a>
821 </td>
850 </td>
822 </tr>
851 </tr>
823 % endfor
852 % endfor
824 % else:
853 % else:
825 <tr>
854 <tr>
826 <td>
855 <td>
827 ${_('No Ticket data found.')}
856 ${_('No Ticket data found.')}
828 </td>
857 </td>
829 </tr>
858 </tr>
830 % endif
859 % endif
831 </table>
860 </table>
832
861
833 </div>
862 </div>
834 </div>
863 </div>
835
864
836 </div>
865 </div>
837
866
838 </div>
867 </div>
839 </aside>
868 </aside>
840
869
841 ## This JS needs to be at the end
870 ## This JS needs to be at the end
842 <script type="text/javascript">
871 <script type="text/javascript">
843
872
844 versionController = new VersionController();
873 versionController = new VersionController();
845 versionController.init();
874 versionController.init();
846
875
847 reviewersController = new ReviewersController();
876 reviewersController = new ReviewersController();
848 commitsController = new CommitsController();
877 commitsController = new CommitsController();
849 commentsController = new CommentsController();
878 commentsController = new CommentsController();
850
879
851 updateController = new UpdatePrController();
880 updateController = new UpdatePrController();
852
881
853 window.reviewerRulesData = ${c.pull_request_default_reviewers_data_json | n};
882 window.reviewerRulesData = ${c.pull_request_default_reviewers_data_json | n};
854 window.setReviewersData = ${c.pull_request_set_reviewers_data_json | n};
883 window.setReviewersData = ${c.pull_request_set_reviewers_data_json | n};
855 window.setObserversData = ${c.pull_request_set_observers_data_json | n};
884 window.setObserversData = ${c.pull_request_set_observers_data_json | n};
856
885
857 (function () {
886 (function () {
858 "use strict";
887 "use strict";
859
888
860 // custom code mirror
889 // custom code mirror
861 var codeMirrorInstance = $('#pr-description-input').get(0).MarkupForm.cm;
890 var codeMirrorInstance = $('#pr-description-input').get(0).MarkupForm.cm;
862
891
863 PRDetails.init();
892 PRDetails.init();
864 ReviewersPanel.init(reviewersController, reviewerRulesData, setReviewersData);
893 ReviewersPanel.init(reviewersController, reviewerRulesData, setReviewersData);
865 ObserversPanel.init(reviewersController, reviewerRulesData, setObserversData);
894 ObserversPanel.init(reviewersController, reviewerRulesData, setObserversData);
866
895
867 window.showOutdated = function (self) {
896 window.showOutdated = function (self) {
868 $('.comment-inline.comment-outdated').show();
897 $('.comment-inline.comment-outdated').show();
869 $('.filediff-outdated').show();
898 $('.filediff-outdated').show();
870 $('.showOutdatedComments').hide();
899 $('.showOutdatedComments').hide();
871 $('.hideOutdatedComments').show();
900 $('.hideOutdatedComments').show();
872 };
901 };
873
902
874 window.hideOutdated = function (self) {
903 window.hideOutdated = function (self) {
875 $('.comment-inline.comment-outdated').hide();
904 $('.comment-inline.comment-outdated').hide();
876 $('.filediff-outdated').hide();
905 $('.filediff-outdated').hide();
877 $('.hideOutdatedComments').hide();
906 $('.hideOutdatedComments').hide();
878 $('.showOutdatedComments').show();
907 $('.showOutdatedComments').show();
879 };
908 };
880
909
881 window.refreshMergeChecks = function () {
910 window.refreshMergeChecks = function () {
882 var loadUrl = "${request.current_route_path(_query=dict(merge_checks=1))}";
911 var loadUrl = "${request.current_route_path(_query=dict(merge_checks=1))}";
883 $('.pull-request-merge').css('opacity', 0.3);
912 $('.pull-request-merge').css('opacity', 0.3);
884 $('.action-buttons-extra').css('opacity', 0.3);
913 $('.action-buttons-extra').css('opacity', 0.3);
885
914
886 $('.pull-request-merge').load(
915 $('.pull-request-merge').load(
887 loadUrl, function () {
916 loadUrl, function () {
888 $('.pull-request-merge').css('opacity', 1);
917 $('.pull-request-merge').css('opacity', 1);
889
918
890 $('.action-buttons-extra').css('opacity', 1);
919 $('.action-buttons-extra').css('opacity', 1);
891 }
920 }
892 );
921 );
893 };
922 };
894
923
895 window.closePullRequest = function (status) {
924 window.closePullRequest = function (status) {
896 if (!confirm(_gettext('Are you sure to close this pull request without merging?'))) {
925 if (!confirm(_gettext('Are you sure to close this pull request without merging?'))) {
897 return false;
926 return false;
898 }
927 }
899 // inject closing flag
928 // inject closing flag
900 $('.action-buttons-extra').append('<input type="hidden" class="close-pr-input" id="close_pull_request" value="1">');
929 $('.action-buttons-extra').append('<input type="hidden" class="close-pr-input" id="close_pull_request" value="1">');
901 $(generalCommentForm.statusChange).select2("val", status).trigger('change');
930 $(generalCommentForm.statusChange).select2("val", status).trigger('change');
902 $(generalCommentForm.submitForm).submit();
931 $(generalCommentForm.submitForm).submit();
903 };
932 };
904
933
905 //TODO this functionality is now missing
934 //TODO this functionality is now missing
906 $('#show-outdated-comments').on('click', function (e) {
935 $('#show-outdated-comments').on('click', function (e) {
907 var button = $(this);
936 var button = $(this);
908 var outdated = $('.comment-outdated');
937 var outdated = $('.comment-outdated');
909
938
910 if (button.html() === "(Show)") {
939 if (button.html() === "(Show)") {
911 button.html("(Hide)");
940 button.html("(Hide)");
912 outdated.show();
941 outdated.show();
913 } else {
942 } else {
914 button.html("(Show)");
943 button.html("(Show)");
915 outdated.hide();
944 outdated.hide();
916 }
945 }
917 });
946 });
918
947
919 $('#merge_pull_request_form').submit(function () {
948 $('#merge_pull_request_form').submit(function () {
920 if (!$('#merge_pull_request').attr('disabled')) {
949 if (!$('#merge_pull_request').attr('disabled')) {
921 $('#merge_pull_request').attr('disabled', 'disabled');
950 $('#merge_pull_request').attr('disabled', 'disabled');
922 }
951 }
923 return true;
952 return true;
924 });
953 });
925
954
926 $('#edit_pull_request').on('click', function (e) {
955 $('#edit_pull_request').on('click', function (e) {
927 var title = $('#pr-title-input').val();
956 var title = $('#pr-title-input').val();
928 var description = codeMirrorInstance.getValue();
957 var description = codeMirrorInstance.getValue();
929 var renderer = $('#pr-renderer-input').val();
958 var renderer = $('#pr-renderer-input').val();
930 editPullRequest(
959 editPullRequest(
931 "${c.repo_name}", "${c.pull_request.pull_request_id}",
960 "${c.repo_name}", "${c.pull_request.pull_request_id}",
932 title, description, renderer);
961 title, description, renderer);
933 });
962 });
934
963
935 var $updateButtons = $('#update_reviewers,#update_observers');
964 var $updateButtons = $('#update_reviewers,#update_observers');
936 $updateButtons.on('click', function (e) {
965 $updateButtons.on('click', function (e) {
937 var role = $(this).data('role');
966 var role = $(this).data('role');
938 $updateButtons.attr('disabled', 'disabled');
967 $updateButtons.attr('disabled', 'disabled');
939 $updateButtons.addClass('disabled');
968 $updateButtons.addClass('disabled');
940 $updateButtons.html(_gettext('Saving...'));
969 $updateButtons.html(_gettext('Saving...'));
941 reviewersController.updateReviewers(
970 reviewersController.updateReviewers(
942 templateContext.repo_name,
971 templateContext.repo_name,
943 templateContext.pull_request_data.pull_request_id,
972 templateContext.pull_request_data.pull_request_id,
944 role
973 role
945 );
974 );
946 });
975 });
947
976
948 // fixing issue with caches on firefox
977 // fixing issue with caches on firefox
949 $('#update_commits').removeAttr("disabled");
978 $('#update_commits').removeAttr("disabled");
950
979
951 $('.show-inline-comments').on('click', function (e) {
980 $('.show-inline-comments').on('click', function (e) {
952 var boxid = $(this).attr('data-comment-id');
981 var boxid = $(this).attr('data-comment-id');
953 var button = $(this);
982 var button = $(this);
954
983
955 if (button.hasClass("comments-visible")) {
984 if (button.hasClass("comments-visible")) {
956 $('#{0} .inline-comments'.format(boxid)).each(function (index) {
985 $('#{0} .inline-comments'.format(boxid)).each(function (index) {
957 $(this).hide();
986 $(this).hide();
958 });
987 });
959 button.removeClass("comments-visible");
988 button.removeClass("comments-visible");
960 } else {
989 } else {
961 $('#{0} .inline-comments'.format(boxid)).each(function (index) {
990 $('#{0} .inline-comments'.format(boxid)).each(function (index) {
962 $(this).show();
991 $(this).show();
963 });
992 });
964 button.addClass("comments-visible");
993 button.addClass("comments-visible");
965 }
994 }
966 });
995 });
967
996
968 $('.show-inline-comments').on('change', function (e) {
997 $('.show-inline-comments').on('change', function (e) {
969 var show = 'none';
998 var show = 'none';
970 var target = e.currentTarget;
999 var target = e.currentTarget;
971 if (target.checked) {
1000 if (target.checked) {
972 show = ''
1001 show = ''
973 }
1002 }
974 var boxid = $(target).attr('id_for');
1003 var boxid = $(target).attr('id_for');
975 var comments = $('#{0} .inline-comments'.format(boxid));
1004 var comments = $('#{0} .inline-comments'.format(boxid));
976 var fn_display = function (idx) {
1005 var fn_display = function (idx) {
977 $(this).css('display', show);
1006 $(this).css('display', show);
978 };
1007 };
979 $(comments).each(fn_display);
1008 $(comments).each(fn_display);
980 var btns = $('#{0} .inline-comments-button'.format(boxid));
1009 var btns = $('#{0} .inline-comments-button'.format(boxid));
981 $(btns).each(fn_display);
1010 $(btns).each(fn_display);
982 });
1011 });
983
1012
984 // register submit callback on commentForm form to track TODOs, and refresh mergeChecks conditions
1013 // register submit callback on commentForm form to track TODOs, and refresh mergeChecks conditions
985 window.commentFormGlobalSubmitSuccessCallback = function (comment) {
1014 window.commentFormGlobalSubmitSuccessCallback = function (comment) {
986 if (!comment.draft) {
1015 if (!comment.draft) {
987 refreshMergeChecks();
1016 refreshMergeChecks();
988 }
1017 }
989 };
1018 };
990
1019
991 ReviewerAutoComplete('#user', reviewersController);
1020 ReviewerAutoComplete('#user', reviewersController);
992 ObserverAutoComplete('#observer', reviewersController);
1021 ObserverAutoComplete('#observer', reviewersController);
993
1022
994 })();
1023 })();
995
1024
996 $(document).ready(function () {
1025 $(document).ready(function () {
997
1026
998 var channel = '${c.pr_broadcast_channel}';
1027 var channel = '${c.pr_broadcast_channel}';
999 new ReviewerPresenceController(channel)
1028 new ReviewerPresenceController(channel)
1000
1001
1002 window.finalizeDrafts = function(commentIds) {
1003 alert('okok !' + commentIds)
1004
1005 }
1006 // register globally so inject comment logic can re-use it.
1029 // register globally so inject comment logic can re-use it.
1007 window.commentsController = commentsController;
1030 window.commentsController = commentsController;
1008
1031
1009 })
1032 })
1010 </script>
1033 </script>
1011
1034
1012 </%def>
1035 </%def>
General Comments 0
You need to be logged in to leave comments. Login now