##// END OF EJS Templates
pull-requests: expose TODO box in dedicated panel...
marcink -
r4140:eb578430 default
parent child Browse files
Show More
@@ -1,94 +1,104 b''
1 1 //--- RESETS ---//
2 2 :focus { outline: none; }
3 3 a { cursor: pointer; }
4 4
5 5 //--- clearfix --//
6 6 .clearfix {
7 7 &:before,
8 8 &:after {
9 9 content:"";
10 10 width: 100%;
11 11 clear: both;
12 12 float: left;
13 13 }
14 14 }
15 15
16 16 .clearinner:after { /* clears all floating divs inside a block */
17 17 content: "";
18 18 display: table;
19 19 clear: both;
20 20 }
21 21
22 22 .js-template { /* mark a template for javascript use */
23 23 display: none;
24 24 }
25 25
26 26 .linebreak {
27 27 display: block;
28 28 }
29 29
30 30 .clear-both {
31 31 clear: both;
32 32 }
33 33
34 34 .pull-right {
35 35 float: right !important;
36 36 }
37 37
38 38 .pull-left {
39 39 float: left !important;
40 40 }
41 41
42 42 .block-left {
43 43 float: left;
44 44 }
45 45
46 46 .block-right {
47 47 float: right;
48 48 clear: right;
49 49
50 50 li {
51 51 list-style-type: none;
52 52 }
53 53 }
54 54
55 .noselect {
56 -webkit-touch-callout: none; /* iOS Safari */
57 -webkit-user-select: none; /* Safari */
58 -khtml-user-select: none; /* Konqueror HTML */
59 -moz-user-select: none; /* Firefox */
60 -ms-user-select: none; /* Internet Explorer/Edge */
61 user-select: none; /* Non-prefixed version, currently
62 supported by Chrome and Opera */
63 }
64
55 65 //--- DEVICE-SPECIFIC CLASSES ---------------//
56 66 //regular tablet and up
57 67 @media (min-width:768px) {
58 68 .no-mobile {
59 69 display: block;
60 70 }
61 71 .mobile-only {
62 72 display: none;
63 73 }
64 74 }
65 75 //small tablet and phone
66 76 @media (max-width:767px) {
67 77 .mobile-only {
68 78 display: block;
69 79 }
70 80 .no-mobile {
71 81 display: none;
72 82 }
73 83 }
74 84
75 85 //--- STICKY FOOTER ---//
76 86 html, body {
77 87 height: 100%;
78 88 margin: 0;
79 89 }
80 90 .outerwrapper {
81 91 height: 100%;
82 92 min-height: 100%;
83 93 margin: 0;
84 94 padding-bottom: 3em; /* must be equal to footer height */
85 95 }
86 96 .outerwrapper:after{
87 97 content:" ";
88 98 }
89 99 #footer {
90 100 clear: both;
91 101 position: relative;
92 102 height: 3em; /* footer height */
93 103 margin: -3em 0 0; /* must be equal to footer height */
94 104 }
@@ -1,2985 +1,3037 b''
1 1 //Primary CSS
2 2
3 3 //--- IMPORTS ------------------//
4 4
5 5 @import 'helpers';
6 6 @import 'mixins';
7 7 @import 'rcicons';
8 8 @import 'variables';
9 9 @import 'bootstrap-variables';
10 10 @import 'form-bootstrap';
11 11 @import 'codemirror';
12 12 @import 'legacy_code_styles';
13 13 @import 'readme-box';
14 14 @import 'progress-bar';
15 15
16 16 @import 'type';
17 17 @import 'alerts';
18 18 @import 'buttons';
19 19 @import 'tags';
20 20 @import 'code-block';
21 21 @import 'examples';
22 22 @import 'login';
23 23 @import 'main-content';
24 24 @import 'select2';
25 25 @import 'comments';
26 26 @import 'panels-bootstrap';
27 27 @import 'panels';
28 28 @import 'deform';
29 29 @import 'tooltips';
30 30
31 31 //--- BASE ------------------//
32 32 .noscript-error {
33 33 top: 0;
34 34 left: 0;
35 35 width: 100%;
36 36 z-index: 101;
37 37 text-align: center;
38 38 font-size: 120%;
39 39 color: white;
40 40 background-color: @alert2;
41 41 padding: 5px 0 5px 0;
42 42 font-weight: @text-semibold-weight;
43 43 font-family: @text-semibold;
44 44 }
45 45
46 46 html {
47 47 display: table;
48 48 height: 100%;
49 49 width: 100%;
50 50 }
51 51
52 52 body {
53 53 display: table-cell;
54 54 width: 100%;
55 55 }
56 56
57 57 //--- LAYOUT ------------------//
58 58
59 59 .hidden{
60 60 display: none !important;
61 61 }
62 62
63 63 .box{
64 64 float: left;
65 65 width: 100%;
66 66 }
67 67
68 68 .browser-header {
69 69 clear: both;
70 70 }
71 71 .main {
72 72 clear: both;
73 73 padding:0 0 @pagepadding;
74 74 height: auto;
75 75
76 76 &:after { //clearfix
77 77 content:"";
78 78 clear:both;
79 79 width:100%;
80 80 display:block;
81 81 }
82 82 }
83 83
84 84 .action-link{
85 85 margin-left: @padding;
86 86 padding-left: @padding;
87 87 border-left: @border-thickness solid @border-default-color;
88 88 }
89 89
90 90 input + .action-link, .action-link.first{
91 91 border-left: none;
92 92 }
93 93
94 94 .action-link.last{
95 95 margin-right: @padding;
96 96 padding-right: @padding;
97 97 }
98 98
99 99 .action-link.active,
100 100 .action-link.active a{
101 101 color: @grey4;
102 102 }
103 103
104 104 .action-link.disabled {
105 105 color: @grey4;
106 106 cursor: inherit;
107 107 }
108 108
109 109
110 110 .clipboard-action {
111 111 cursor: pointer;
112 112 margin-left: 5px;
113 113
114 114 &:not(.no-grey) {
115 115
116 116 &:hover {
117 117 color: @grey2;
118 118 }
119 119 color: @grey4;
120 120 }
121 121 }
122 122
123 123 ul.simple-list{
124 124 list-style: none;
125 125 margin: 0;
126 126 padding: 0;
127 127 }
128 128
129 129 .main-content {
130 130 padding-bottom: @pagepadding;
131 131 }
132 132
133 133 .wide-mode-wrapper {
134 134 max-width:4000px !important;
135 135 }
136 136
137 137 .wrapper {
138 138 position: relative;
139 139 max-width: @wrapper-maxwidth;
140 140 margin: 0 auto;
141 141 }
142 142
143 143 #content {
144 144 clear: both;
145 145 padding: 0 @contentpadding;
146 146 }
147 147
148 148 .advanced-settings-fields{
149 149 input{
150 150 margin-left: @textmargin;
151 151 margin-right: @padding/2;
152 152 }
153 153 }
154 154
155 155 .cs_files_title {
156 156 margin: @pagepadding 0 0;
157 157 }
158 158
159 159 input.inline[type="file"] {
160 160 display: inline;
161 161 }
162 162
163 163 .error_page {
164 164 margin: 10% auto;
165 165
166 166 h1 {
167 167 color: @grey2;
168 168 }
169 169
170 170 .alert {
171 171 margin: @padding 0;
172 172 }
173 173
174 174 .error-branding {
175 175 color: @grey4;
176 176 font-weight: @text-semibold-weight;
177 177 font-family: @text-semibold;
178 178 }
179 179
180 180 .error_message {
181 181 font-family: @text-regular;
182 182 }
183 183
184 184 .sidebar {
185 185 min-height: 275px;
186 186 margin: 0;
187 187 padding: 0 0 @sidebarpadding @sidebarpadding;
188 188 border: none;
189 189 }
190 190
191 191 .main-content {
192 192 position: relative;
193 193 margin: 0 @sidebarpadding @sidebarpadding;
194 194 padding: 0 0 0 @sidebarpadding;
195 195 border-left: @border-thickness solid @grey5;
196 196
197 197 @media (max-width:767px) {
198 198 clear: both;
199 199 width: 100%;
200 200 margin: 0;
201 201 border: none;
202 202 }
203 203 }
204 204
205 205 .inner-column {
206 206 float: left;
207 207 width: 29.75%;
208 208 min-height: 150px;
209 209 margin: @sidebarpadding 2% 0 0;
210 210 padding: 0 2% 0 0;
211 211 border-right: @border-thickness solid @grey5;
212 212
213 213 @media (max-width:767px) {
214 214 clear: both;
215 215 width: 100%;
216 216 border: none;
217 217 }
218 218
219 219 ul {
220 220 padding-left: 1.25em;
221 221 }
222 222
223 223 &:last-child {
224 224 margin: @sidebarpadding 0 0;
225 225 border: none;
226 226 }
227 227
228 228 h4 {
229 229 margin: 0 0 @padding;
230 230 font-weight: @text-semibold-weight;
231 231 font-family: @text-semibold;
232 232 }
233 233 }
234 234 }
235 235 .error-page-logo {
236 236 width: 130px;
237 237 height: 160px;
238 238 }
239 239
240 240 // HEADER
241 241 .header {
242 242
243 243 // TODO: johbo: Fix login pages, so that they work without a min-height
244 244 // for the header and then remove the min-height. I chose a smaller value
245 245 // intentionally here to avoid rendering issues in the main navigation.
246 246 min-height: 49px;
247 247 min-width: 1024px;
248 248
249 249 position: relative;
250 250 vertical-align: bottom;
251 251 padding: 0 @header-padding;
252 252 background-color: @grey1;
253 253 color: @grey5;
254 254
255 255 .title {
256 256 overflow: visible;
257 257 }
258 258
259 259 &:before,
260 260 &:after {
261 261 content: "";
262 262 clear: both;
263 263 width: 100%;
264 264 }
265 265
266 266 // TODO: johbo: Avoids breaking "Repositories" chooser
267 267 .select2-container .select2-choice .select2-arrow {
268 268 display: none;
269 269 }
270 270 }
271 271
272 272 #header-inner {
273 273 &.title {
274 274 margin: 0;
275 275 }
276 276 &:before,
277 277 &:after {
278 278 content: "";
279 279 clear: both;
280 280 }
281 281 }
282 282
283 283 // Gists
284 284 #files_data {
285 285 clear: both; //for firefox
286 286 padding-top: 10px;
287 287 }
288 288
289 289 #gistid {
290 290 margin-right: @padding;
291 291 }
292 292
293 293 // Global Settings Editor
294 294 .textarea.editor {
295 295 float: left;
296 296 position: relative;
297 297 max-width: @texteditor-width;
298 298
299 299 select {
300 300 position: absolute;
301 301 top:10px;
302 302 right:0;
303 303 }
304 304
305 305 .CodeMirror {
306 306 margin: 0;
307 307 }
308 308
309 309 .help-block {
310 310 margin: 0 0 @padding;
311 311 padding:.5em;
312 312 background-color: @grey6;
313 313 &.pre-formatting {
314 314 white-space: pre;
315 315 }
316 316 }
317 317 }
318 318
319 319 ul.auth_plugins {
320 320 margin: @padding 0 @padding @legend-width;
321 321 padding: 0;
322 322
323 323 li {
324 324 margin-bottom: @padding;
325 325 line-height: 1em;
326 326 list-style-type: none;
327 327
328 328 .auth_buttons .btn {
329 329 margin-right: @padding;
330 330 }
331 331
332 332 }
333 333 }
334 334
335 335
336 336 // My Account PR list
337 337
338 338 #show_closed {
339 339 margin: 0 1em 0 0;
340 340 }
341 341
342 342 #pull_request_list_table {
343 343 .closed {
344 344 background-color: @grey6;
345 345 }
346 346
347 347 .state-creating,
348 348 .state-updating,
349 349 .state-merging
350 350 {
351 351 background-color: @grey6;
352 352 }
353 353
354 354 .td-status {
355 355 padding-left: .5em;
356 356 }
357 357 .log-container .truncate {
358 358 height: 2.75em;
359 359 white-space: pre-line;
360 360 }
361 361 table.rctable .user {
362 362 padding-left: 0;
363 363 }
364 364 table.rctable {
365 365 td.td-description,
366 366 .rc-user {
367 367 min-width: auto;
368 368 }
369 369 }
370 370 }
371 371
372 372 // Pull Requests
373 373
374 374 .pullrequests_section_head {
375 375 display: block;
376 376 clear: both;
377 377 margin: @padding 0;
378 378 font-weight: @text-bold-weight;
379 379 font-family: @text-bold;
380 380 }
381 381
382 382 .pr-commit-flow {
383 383 position: relative;
384 384 font-weight: 600;
385 385
386 386 .tag {
387 387 display: inline-block;
388 388 margin: 0 1em .5em 0;
389 389 }
390 390
391 391 .clone-url {
392 392 display: inline-block;
393 393 margin: 0 0 .5em 0;
394 394 padding: 0;
395 395 line-height: 1.2em;
396 396 }
397 397 }
398 398
399 399 .pr-mergeinfo {
400 400 min-width: 95% !important;
401 401 padding: 0 !important;
402 402 border: 0;
403 403 }
404 404 .pr-mergeinfo-copy {
405 405 padding: 0 0;
406 406 }
407 407
408 408 .pr-pullinfo {
409 409 min-width: 95% !important;
410 410 padding: 0 !important;
411 411 border: 0;
412 412 }
413 413 .pr-pullinfo-copy {
414 414 padding: 0 0;
415 415 }
416 416
417 417 .pr-title-input {
418 418 width: 80%;
419 419 font-size: 1em;
420 420 margin: 0 0 4px 0;
421 421 padding: 0;
422 422 line-height: 1.7em;
423 423 color: @text-color;
424 424 letter-spacing: .02em;
425 425 font-weight: @text-bold-weight;
426 426 font-family: @text-bold;
427 427
428 428 &:hover {
429 429 box-shadow: none;
430 430 }
431 431 }
432 432
433 433 #pr-title {
434 434 input {
435 435 border: 1px transparent;
436 436 color: black;
437 opacity: 1
437 opacity: 1;
438 background: #fff;
438 439 }
439 440 }
440 441
441 442 .pr-title-closed-tag {
442 443 font-size: 16px;
443 444 }
444 445
445 446 #pr-desc {
446 447 padding: 10px 0;
447 448
448 449 .markdown-block {
449 450 padding: 0;
450 451 margin-bottom: -30px;
451 452 }
452 453 }
453 454
454 455 #pullrequest_title {
455 456 width: 100%;
456 457 box-sizing: border-box;
457 458 }
458 459
459 460 #pr_open_message {
460 461 border: @border-thickness solid #fff;
461 462 border-radius: @border-radius;
462 463 padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0;
463 464 text-align: left;
464 465 overflow: hidden;
465 466 }
466 467
467 468 .pr-details-title {
468 469 height: 16px
469 470 }
470 471
471 472 .pr-details-title-author-pref {
472 473 padding-right: 10px
473 474 }
474 475
475 476 .label-pr-detail {
476 477 display: table-cell;
477 478 width: 120px;
478 479 padding-top: 7.5px;
479 480 padding-bottom: 7.5px;
480 481 padding-right: 7.5px;
481 482 }
482 483
483 484 .source-details ul {
484 485 padding: 10px 16px;
485 486 }
486 487
487 488 .source-details-action {
488 489 color: @grey4;
489 490 font-size: 11px
490 491 }
491 492
492 493 .pr-submit-button {
493 494 float: right;
494 495 margin: 0 0 0 5px;
495 496 }
496 497
497 498 .pr-spacing-container {
498 499 padding: 20px;
499 500 clear: both
500 501 }
501 502
502 503 #pr-description-input {
503 504 margin-bottom: 0;
504 505 }
505 506
506 507 .pr-description-label {
507 508 vertical-align: top;
508 509 }
509 510
511 #open_edit_pullrequest {
512 padding: 0;
513 }
514
510 515 #close_edit_pullrequest {
511 padding-left: 1em
516
512 517 }
513 518
514 519 #delete_pullrequest {
515 520 clear: inherit;
516 padding: 0
521
522 form {
523 display: inline;
524 }
525
517 526 }
518 527
519 528 .perms_section_head {
520 529 min-width: 625px;
521 530
522 531 h2 {
523 532 margin-bottom: 0;
524 533 }
525 534
526 535 .label-checkbox {
527 536 float: left;
528 537 }
529 538
530 539 &.field {
531 540 margin: @space 0 @padding;
532 541 }
533 542
534 543 &:first-child.field {
535 544 margin-top: 0;
536 545
537 546 .label {
538 547 margin-top: 0;
539 548 padding-top: 0;
540 549 }
541 550
542 551 .radios {
543 552 padding-top: 0;
544 553 }
545 554 }
546 555
547 556 .radios {
548 557 position: relative;
549 558 width: 505px;
550 559 }
551 560 }
552 561
553 562 //--- MODULES ------------------//
554 563
555 564
556 565 // Server Announcement
557 566 #server-announcement {
558 567 width: 95%;
559 568 margin: @padding auto;
560 569 padding: @padding;
561 570 border-width: 2px;
562 571 border-style: solid;
563 572 .border-radius(2px);
564 573 font-weight: @text-bold-weight;
565 574 font-family: @text-bold;
566 575
567 576 &.info { border-color: @alert4; background-color: @alert4-inner; }
568 577 &.warning { border-color: @alert3; background-color: @alert3-inner; }
569 578 &.error { border-color: @alert2; background-color: @alert2-inner; }
570 579 &.success { border-color: @alert1; background-color: @alert1-inner; }
571 580 &.neutral { border-color: @grey3; background-color: @grey6; }
572 581 }
573 582
574 583 // Fixed Sidebar Column
575 584 .sidebar-col-wrapper {
576 585 padding-left: @sidebar-all-width;
577 586
578 587 .sidebar {
579 588 width: @sidebar-width;
580 589 margin-left: -@sidebar-all-width;
581 590 }
582 591 }
583 592
584 593 .sidebar-col-wrapper.scw-small {
585 594 padding-left: @sidebar-small-all-width;
586 595
587 596 .sidebar {
588 597 width: @sidebar-small-width;
589 598 margin-left: -@sidebar-small-all-width;
590 599 }
591 600 }
592 601
593 602
594 603 // FOOTER
595 604 #footer {
596 605 padding: 0;
597 606 text-align: center;
598 607 vertical-align: middle;
599 608 color: @grey2;
600 609 font-size: 11px;
601 610
602 611 p {
603 612 margin: 0;
604 613 padding: 1em;
605 614 line-height: 1em;
606 615 }
607 616
608 617 .server-instance { //server instance
609 618 display: none;
610 619 }
611 620
612 621 .title {
613 622 float: none;
614 623 margin: 0 auto;
615 624 }
616 625 }
617 626
618 627 button.close {
619 628 padding: 0;
620 629 cursor: pointer;
621 630 background: transparent;
622 631 border: 0;
623 632 .box-shadow(none);
624 633 -webkit-appearance: none;
625 634 }
626 635
627 636 .close {
628 637 float: right;
629 638 font-size: 21px;
630 639 font-family: @text-bootstrap;
631 640 line-height: 1em;
632 641 font-weight: bold;
633 642 color: @grey2;
634 643
635 644 &:hover,
636 645 &:focus {
637 646 color: @grey1;
638 647 text-decoration: none;
639 648 cursor: pointer;
640 649 }
641 650 }
642 651
643 652 // GRID
644 653 .sorting,
645 654 .sorting_desc,
646 655 .sorting_asc {
647 656 cursor: pointer;
648 657 }
649 658 .sorting_desc:after {
650 659 content: "\00A0\25B2";
651 660 font-size: .75em;
652 661 }
653 662 .sorting_asc:after {
654 663 content: "\00A0\25BC";
655 664 font-size: .68em;
656 665 }
657 666
658 667
659 668 .user_auth_tokens {
660 669
661 670 &.truncate {
662 671 white-space: nowrap;
663 672 overflow: hidden;
664 673 text-overflow: ellipsis;
665 674 }
666 675
667 676 .fields .field .input {
668 677 margin: 0;
669 678 }
670 679
671 680 input#description {
672 681 width: 100px;
673 682 margin: 0;
674 683 }
675 684
676 685 .drop-menu {
677 686 // TODO: johbo: Remove this, should work out of the box when
678 687 // having multiple inputs inline
679 688 margin: 0 0 0 5px;
680 689 }
681 690 }
682 691 #user_list_table {
683 692 .closed {
684 693 background-color: @grey6;
685 694 }
686 695 }
687 696
688 697
689 698 input, textarea {
690 699 &.disabled {
691 700 opacity: .5;
692 701 }
693 702
694 703 &:hover {
695 704 border-color: @grey3;
696 705 box-shadow: @button-shadow;
697 706 }
698 707
699 708 &:focus {
700 709 border-color: @rcblue;
701 710 box-shadow: @button-shadow;
702 711 }
703 712 }
704 713
705 714 // remove extra padding in firefox
706 715 input::-moz-focus-inner { border:0; padding:0 }
707 716
708 717 .adjacent input {
709 718 margin-bottom: @padding;
710 719 }
711 720
712 721 .permissions_boxes {
713 722 display: block;
714 723 }
715 724
716 725 //FORMS
717 726
718 727 .medium-inline,
719 728 input#description.medium-inline {
720 729 display: inline;
721 730 width: @medium-inline-input-width;
722 731 min-width: 100px;
723 732 }
724 733
725 734 select {
726 735 //reset
727 736 -webkit-appearance: none;
728 737 -moz-appearance: none;
729 738
730 739 display: inline-block;
731 740 height: 28px;
732 741 width: auto;
733 742 margin: 0 @padding @padding 0;
734 743 padding: 0 18px 0 8px;
735 744 line-height:1em;
736 745 font-size: @basefontsize;
737 746 border: @border-thickness solid @grey5;
738 747 border-radius: @border-radius;
739 748 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
740 749 color: @grey4;
741 750 box-shadow: @button-shadow;
742 751
743 752 &:after {
744 753 content: "\00A0\25BE";
745 754 }
746 755
747 756 &:focus, &:hover {
748 757 outline: none;
749 758 border-color: @grey4;
750 759 color: @rcdarkblue;
751 760 }
752 761 }
753 762
754 763 option {
755 764 &:focus {
756 765 outline: none;
757 766 }
758 767 }
759 768
760 769 input,
761 770 textarea {
762 771 padding: @input-padding;
763 772 border: @input-border-thickness solid @border-highlight-color;
764 773 .border-radius (@border-radius);
765 774 font-family: @text-light;
766 775 font-size: @basefontsize;
767 776
768 777 &.input-sm {
769 778 padding: 5px;
770 779 }
771 780
772 781 &#description {
773 782 min-width: @input-description-minwidth;
774 783 min-height: 1em;
775 784 padding: 10px;
776 785 }
777 786 }
778 787
779 788 .field-sm {
780 789 input,
781 790 textarea {
782 791 padding: 5px;
783 792 }
784 793 }
785 794
786 795 textarea {
787 796 display: block;
788 797 clear: both;
789 798 width: 100%;
790 799 min-height: 100px;
791 800 margin-bottom: @padding;
792 801 .box-sizing(border-box);
793 802 overflow: auto;
794 803 }
795 804
796 805 label {
797 806 font-family: @text-light;
798 807 }
799 808
800 809 // GRAVATARS
801 810 // centers gravatar on username to the right
802 811
803 812 .gravatar {
804 813 display: inline;
805 814 min-width: 16px;
806 815 min-height: 16px;
807 816 margin: -5px 0;
808 817 padding: 0;
809 818 line-height: 1em;
810 819 box-sizing: content-box;
811 820 border-radius: 50%;
812 821
813 822 &.gravatar-large {
814 823 margin: -0.5em .25em -0.5em 0;
815 824 }
816 825
817 826 & + .user {
818 827 display: inline;
819 828 margin: 0;
820 829 padding: 0 0 0 .17em;
821 830 line-height: 1em;
822 831 }
832
833 & + .no-margin {
834 margin: 0
835 }
836
823 837 }
824 838
825 839 .user-inline-data {
826 840 display: inline-block;
827 841 float: left;
828 842 padding-left: .5em;
829 843 line-height: 1.3em;
830 844 }
831 845
832 846 .rc-user { // gravatar + user wrapper
833 847 float: left;
834 848 position: relative;
835 849 min-width: 100px;
836 850 max-width: 200px;
837 851 min-height: (@gravatar-size + @border-thickness * 2); // account for border
838 852 display: block;
839 padding: 0 0 0 (@gravatar-size + @basefontsize/2 + @border-thickness * 2);
853 padding: 0 0 0 (@gravatar-size + @basefontsize/4);
840 854
841 855
842 856 .gravatar {
843 857 display: block;
844 858 position: absolute;
845 859 top: 0;
846 860 left: 0;
847 861 min-width: @gravatar-size;
848 862 min-height: @gravatar-size;
849 863 margin: 0;
850 864 }
851 865
852 866 .user {
853 867 display: block;
854 868 max-width: 175px;
855 869 padding-top: 2px;
856 870 overflow: hidden;
857 871 text-overflow: ellipsis;
858 872 }
859 873 }
860 874
861 875 .gist-gravatar,
862 876 .journal_container {
863 877 .gravatar-large {
864 878 margin: 0 .5em -10px 0;
865 879 }
866 880 }
867 881
868 882 .gist-type-fields {
869 883 line-height: 30px;
870 884 height: 30px;
871 885
872 886 .gist-type-fields-wrapper {
873 887 vertical-align: middle;
874 888 display: inline-block;
875 889 line-height: 25px;
876 890 }
877 891 }
878 892
879 893 // ADMIN SETTINGS
880 894
881 895 // Tag Patterns
882 896 .tag_patterns {
883 897 .tag_input {
884 898 margin-bottom: @padding;
885 899 }
886 900 }
887 901
888 902 .locked_input {
889 903 position: relative;
890 904
891 905 input {
892 906 display: inline;
893 907 margin: 3px 5px 0px 0px;
894 908 }
895 909
896 910 br {
897 911 display: none;
898 912 }
899 913
900 914 .error-message {
901 915 float: left;
902 916 width: 100%;
903 917 }
904 918
905 919 .lock_input_button {
906 920 display: inline;
907 921 }
908 922
909 923 .help-block {
910 924 clear: both;
911 925 }
912 926 }
913 927
914 928 // Notifications
915 929
916 930 .notifications_buttons {
917 931 margin: 0 0 @space 0;
918 932 padding: 0;
919 933
920 934 .btn {
921 935 display: inline-block;
922 936 }
923 937 }
924 938
925 939 .notification-list {
926 940
927 941 div {
928 942 vertical-align: middle;
929 943 }
930 944
931 945 .container {
932 946 display: block;
933 947 margin: 0 0 @padding 0;
934 948 }
935 949
936 950 .delete-notifications {
937 951 margin-left: @padding;
938 952 text-align: right;
939 953 cursor: pointer;
940 954 }
941 955
942 956 .read-notifications {
943 957 margin-left: @padding/2;
944 958 text-align: right;
945 959 width: 35px;
946 960 cursor: pointer;
947 961 }
948 962
949 963 .icon-minus-sign {
950 964 color: @alert2;
951 965 }
952 966
953 967 .icon-ok-sign {
954 968 color: @alert1;
955 969 }
956 970 }
957 971
958 972 .user_settings {
959 973 float: left;
960 974 clear: both;
961 975 display: block;
962 976 width: 100%;
963 977
964 978 .gravatar_box {
965 979 margin-bottom: @padding;
966 980
967 981 &:after {
968 982 content: " ";
969 983 clear: both;
970 984 width: 100%;
971 985 }
972 986 }
973 987
974 988 .fields .field {
975 989 clear: both;
976 990 }
977 991 }
978 992
979 993 .advanced_settings {
980 994 margin-bottom: @space;
981 995
982 996 .help-block {
983 997 margin-left: 0;
984 998 }
985 999
986 1000 button + .help-block {
987 1001 margin-top: @padding;
988 1002 }
989 1003 }
990 1004
991 1005 // admin settings radio buttons and labels
992 1006 .label-2 {
993 1007 float: left;
994 1008 width: @label2-width;
995 1009
996 1010 label {
997 1011 color: @grey1;
998 1012 }
999 1013 }
1000 1014 .checkboxes {
1001 1015 float: left;
1002 1016 width: @checkboxes-width;
1003 1017 margin-bottom: @padding;
1004 1018
1005 1019 .checkbox {
1006 1020 width: 100%;
1007 1021
1008 1022 label {
1009 1023 margin: 0;
1010 1024 padding: 0;
1011 1025 }
1012 1026 }
1013 1027
1014 1028 .checkbox + .checkbox {
1015 1029 display: inline-block;
1016 1030 }
1017 1031
1018 1032 label {
1019 1033 margin-right: 1em;
1020 1034 }
1021 1035 }
1022 1036
1023 1037 // CHANGELOG
1024 1038 .container_header {
1025 1039 float: left;
1026 1040 display: block;
1027 1041 width: 100%;
1028 1042 margin: @padding 0 @padding;
1029 1043
1030 1044 #filter_changelog {
1031 1045 float: left;
1032 1046 margin-right: @padding;
1033 1047 }
1034 1048
1035 1049 .breadcrumbs_light {
1036 1050 display: inline-block;
1037 1051 }
1038 1052 }
1039 1053
1040 1054 .info_box {
1041 1055 float: right;
1042 1056 }
1043 1057
1044 1058
1045 1059
1046 1060 #graph_content{
1047 1061
1048 1062 // adjust for table headers so that graph renders properly
1049 1063 // #graph_nodes padding - table cell padding
1050 1064 padding-top: (@space - (@basefontsize * 2.4));
1051 1065
1052 1066 &.graph_full_width {
1053 1067 width: 100%;
1054 1068 max-width: 100%;
1055 1069 }
1056 1070 }
1057 1071
1058 1072 #graph {
1059 1073
1060 1074 .pagination-left {
1061 1075 float: left;
1062 1076 clear: both;
1063 1077 }
1064 1078
1065 1079 .log-container {
1066 1080 max-width: 345px;
1067 1081
1068 1082 .message{
1069 1083 max-width: 340px;
1070 1084 }
1071 1085 }
1072 1086
1073 1087 .graph-col-wrapper {
1074 1088
1075 1089 #graph_nodes {
1076 1090 width: 100px;
1077 1091 position: absolute;
1078 1092 left: 70px;
1079 1093 z-index: -1;
1080 1094 }
1081 1095 }
1082 1096
1083 1097 .load-more-commits {
1084 1098 text-align: center;
1085 1099 }
1086 1100 .load-more-commits:hover {
1087 1101 background-color: @grey7;
1088 1102 }
1089 1103 .load-more-commits {
1090 1104 a {
1091 1105 display: block;
1092 1106 }
1093 1107 }
1094 1108 }
1095 1109
1096 1110 .obsolete-toggle {
1097 1111 line-height: 30px;
1098 1112 margin-left: -15px;
1099 1113 }
1100 1114
1101 1115 #rev_range_container, #rev_range_clear, #rev_range_more {
1102 1116 margin-top: -5px;
1103 1117 margin-bottom: -5px;
1104 1118 }
1105 1119
1106 1120 #filter_changelog {
1107 1121 float: left;
1108 1122 }
1109 1123
1110 1124
1111 1125 //--- THEME ------------------//
1112 1126
1113 1127 #logo {
1114 1128 float: left;
1115 1129 margin: 9px 0 0 0;
1116 1130
1117 1131 .header {
1118 1132 background-color: transparent;
1119 1133 }
1120 1134
1121 1135 a {
1122 1136 display: inline-block;
1123 1137 }
1124 1138
1125 1139 img {
1126 1140 height:30px;
1127 1141 }
1128 1142 }
1129 1143
1130 1144 .logo-wrapper {
1131 1145 float:left;
1132 1146 }
1133 1147
1134 1148 .branding {
1135 1149 float: left;
1136 1150 padding: 9px 2px;
1137 1151 line-height: 1em;
1138 1152 font-size: @navigation-fontsize;
1139 1153
1140 1154 a {
1141 1155 color: @grey5
1142 1156 }
1143 1157 @media screen and (max-width: 1200px) {
1144 1158 display: none;
1145 1159 }
1146 1160 }
1147 1161
1148 1162 img {
1149 1163 border: none;
1150 1164 outline: none;
1151 1165 }
1152 1166 user-profile-header
1153 1167 label {
1154 1168
1155 1169 input[type="checkbox"] {
1156 1170 margin-right: 1em;
1157 1171 }
1158 1172 input[type="radio"] {
1159 1173 margin-right: 1em;
1160 1174 }
1161 1175 }
1162 1176
1163 1177 .review-status {
1164 1178 &.under_review {
1165 1179 color: @alert3;
1166 1180 }
1167 1181 &.approved {
1168 1182 color: @alert1;
1169 1183 }
1170 1184 &.rejected,
1171 1185 &.forced_closed{
1172 1186 color: @alert2;
1173 1187 }
1174 1188 &.not_reviewed {
1175 1189 color: @grey5;
1176 1190 }
1177 1191 }
1178 1192
1179 1193 .review-status-under_review {
1180 1194 color: @alert3;
1181 1195 }
1182 1196 .status-tag-under_review {
1183 1197 border-color: @alert3;
1184 1198 }
1185 1199
1186 1200 .review-status-approved {
1187 1201 color: @alert1;
1188 1202 }
1189 1203 .status-tag-approved {
1190 1204 border-color: @alert1;
1191 1205 }
1192 1206
1193 1207 .review-status-rejected,
1194 1208 .review-status-forced_closed {
1195 1209 color: @alert2;
1196 1210 }
1197 1211 .status-tag-rejected,
1198 1212 .status-tag-forced_closed {
1199 1213 border-color: @alert2;
1200 1214 }
1201 1215
1202 1216 .review-status-not_reviewed {
1203 1217 color: @grey5;
1204 1218 }
1205 1219 .status-tag-not_reviewed {
1206 1220 border-color: @grey5;
1207 1221 }
1208 1222
1209 1223 .test_pattern_preview {
1210 1224 margin: @space 0;
1211 1225
1212 1226 p {
1213 1227 margin-bottom: 0;
1214 1228 border-bottom: @border-thickness solid @border-default-color;
1215 1229 color: @grey3;
1216 1230 }
1217 1231
1218 1232 .btn {
1219 1233 margin-bottom: @padding;
1220 1234 }
1221 1235 }
1222 1236 #test_pattern_result {
1223 1237 display: none;
1224 1238 &:extend(pre);
1225 1239 padding: .9em;
1226 1240 color: @grey3;
1227 1241 background-color: @grey7;
1228 1242 border-right: @border-thickness solid @border-default-color;
1229 1243 border-bottom: @border-thickness solid @border-default-color;
1230 1244 border-left: @border-thickness solid @border-default-color;
1231 1245 }
1232 1246
1233 1247 #repo_vcs_settings {
1234 1248 #inherit_overlay_vcs_default {
1235 1249 display: none;
1236 1250 }
1237 1251 #inherit_overlay_vcs_custom {
1238 1252 display: custom;
1239 1253 }
1240 1254 &.inherited {
1241 1255 #inherit_overlay_vcs_default {
1242 1256 display: block;
1243 1257 }
1244 1258 #inherit_overlay_vcs_custom {
1245 1259 display: none;
1246 1260 }
1247 1261 }
1248 1262 }
1249 1263
1250 1264 .issue-tracker-link {
1251 1265 color: @rcblue;
1252 1266 }
1253 1267
1254 1268 // Issue Tracker Table Show/Hide
1255 1269 #repo_issue_tracker {
1256 1270 #inherit_overlay {
1257 1271 display: none;
1258 1272 }
1259 1273 #custom_overlay {
1260 1274 display: custom;
1261 1275 }
1262 1276 &.inherited {
1263 1277 #inherit_overlay {
1264 1278 display: block;
1265 1279 }
1266 1280 #custom_overlay {
1267 1281 display: none;
1268 1282 }
1269 1283 }
1270 1284 }
1271 1285 table.issuetracker {
1272 1286 &.readonly {
1273 1287 tr, td {
1274 1288 color: @grey3;
1275 1289 }
1276 1290 }
1277 1291 .edit {
1278 1292 display: none;
1279 1293 }
1280 1294 .editopen {
1281 1295 .edit {
1282 1296 display: inline;
1283 1297 }
1284 1298 .entry {
1285 1299 display: none;
1286 1300 }
1287 1301 }
1288 1302 tr td.td-action {
1289 1303 min-width: 117px;
1290 1304 }
1291 1305 td input {
1292 1306 max-width: none;
1293 1307 min-width: 30px;
1294 1308 width: 80%;
1295 1309 }
1296 1310 .issuetracker_pref input {
1297 1311 width: 40%;
1298 1312 }
1299 1313 input.edit_issuetracker_update {
1300 1314 margin-right: 0;
1301 1315 width: auto;
1302 1316 }
1303 1317 }
1304 1318
1305 1319 table.integrations {
1306 1320 .td-icon {
1307 1321 width: 20px;
1308 1322 .integration-icon {
1309 1323 height: 20px;
1310 1324 width: 20px;
1311 1325 }
1312 1326 }
1313 1327 }
1314 1328
1315 1329 .integrations {
1316 1330 a.integration-box {
1317 1331 color: @text-color;
1318 1332 &:hover {
1319 1333 .panel {
1320 1334 background: #fbfbfb;
1321 1335 }
1322 1336 }
1323 1337 .integration-icon {
1324 1338 width: 30px;
1325 1339 height: 30px;
1326 1340 margin-right: 20px;
1327 1341 float: left;
1328 1342 }
1329 1343
1330 1344 .panel-body {
1331 1345 padding: 10px;
1332 1346 }
1333 1347 .panel {
1334 1348 margin-bottom: 10px;
1335 1349 }
1336 1350 h2 {
1337 1351 display: inline-block;
1338 1352 margin: 0;
1339 1353 min-width: 140px;
1340 1354 }
1341 1355 }
1342 1356 a.integration-box.dummy-integration {
1343 1357 color: @grey4
1344 1358 }
1345 1359 }
1346 1360
1347 1361 //Permissions Settings
1348 1362 #add_perm {
1349 1363 margin: 0 0 @padding;
1350 1364 cursor: pointer;
1351 1365 }
1352 1366
1353 1367 .perm_ac {
1354 1368 input {
1355 1369 width: 95%;
1356 1370 }
1357 1371 }
1358 1372
1359 1373 .autocomplete-suggestions {
1360 1374 width: auto !important; // overrides autocomplete.js
1361 1375 min-width: 278px;
1362 1376 margin: 0;
1363 1377 border: @border-thickness solid @grey5;
1364 1378 border-radius: @border-radius;
1365 1379 color: @grey2;
1366 1380 background-color: white;
1367 1381 }
1368 1382
1369 1383 .autocomplete-qfilter-suggestions {
1370 1384 width: auto !important; // overrides autocomplete.js
1371 1385 max-height: 100% !important;
1372 1386 min-width: 376px;
1373 1387 margin: 0;
1374 1388 border: @border-thickness solid @grey5;
1375 1389 color: @grey2;
1376 1390 background-color: white;
1377 1391 }
1378 1392
1379 1393 .autocomplete-selected {
1380 1394 background: #F0F0F0;
1381 1395 }
1382 1396
1383 1397 .ac-container-wrap {
1384 1398 margin: 0;
1385 1399 padding: 8px;
1386 1400 border-bottom: @border-thickness solid @grey5;
1387 1401 list-style-type: none;
1388 1402 cursor: pointer;
1389 1403
1390 1404 &:hover {
1391 1405 background-color: @grey7;
1392 1406 }
1393 1407
1394 1408 img {
1395 1409 height: @gravatar-size;
1396 1410 width: @gravatar-size;
1397 1411 margin-right: 1em;
1398 1412 }
1399 1413
1400 1414 strong {
1401 1415 font-weight: normal;
1402 1416 }
1403 1417 }
1404 1418
1405 1419 // Settings Dropdown
1406 1420 .user-menu .container {
1407 1421 padding: 0 4px;
1408 1422 margin: 0;
1409 1423 }
1410 1424
1411 1425 .user-menu .gravatar {
1412 1426 cursor: pointer;
1413 1427 }
1414 1428
1415 1429 .codeblock {
1416 1430 margin-bottom: @padding;
1417 1431 clear: both;
1418 1432
1419 1433 .stats {
1420 1434 overflow: hidden;
1421 1435 }
1422 1436
1423 1437 .message{
1424 1438 textarea{
1425 1439 margin: 0;
1426 1440 }
1427 1441 }
1428 1442
1429 1443 .code-header {
1430 1444 .stats {
1431 1445 line-height: 2em;
1432 1446
1433 1447 .revision_id {
1434 1448 margin-left: 0;
1435 1449 }
1436 1450 .buttons {
1437 1451 padding-right: 0;
1438 1452 }
1439 1453 }
1440 1454
1441 1455 .item{
1442 1456 margin-right: 0.5em;
1443 1457 }
1444 1458 }
1445 1459
1446 1460 #editor_container {
1447 1461 position: relative;
1448 1462 margin: @padding 10px;
1449 1463 }
1450 1464 }
1451 1465
1452 1466 #file_history_container {
1453 1467 display: none;
1454 1468 }
1455 1469
1456 1470 .file-history-inner {
1457 1471 margin-bottom: 10px;
1458 1472 }
1459 1473
1460 1474 // Pull Requests
1461 1475 .summary-details {
1462 1476 width: 72%;
1463 1477 }
1464 1478 .pr-summary {
1465 1479 border-bottom: @border-thickness solid @grey5;
1466 1480 margin-bottom: @space;
1467 1481 }
1468 1482 .reviewers-title {
1469 1483 width: 25%;
1470 1484 min-width: 200px;
1471 1485 }
1472 1486 .reviewers {
1473 1487 width: 25%;
1474 1488 min-width: 200px;
1475 1489 }
1476 1490 .reviewers ul li {
1477 1491 position: relative;
1478 1492 width: 100%;
1479 1493 padding-bottom: 8px;
1480 1494 list-style-type: none;
1481 1495 }
1482 1496
1483 1497 .reviewer_entry {
1484 1498 min-height: 55px;
1485 1499 }
1486 1500
1487 1501 .reviewers_member {
1488 1502 width: 100%;
1489 1503 overflow: auto;
1490 1504 }
1491 1505 .reviewer_reason {
1492 1506 padding-left: 20px;
1493 1507 line-height: 1.5em;
1494 1508 }
1495 1509 .reviewer_status {
1496 1510 display: inline-block;
1497 1511 width: 25px;
1498 1512 min-width: 25px;
1499 1513 height: 1.2em;
1500 1514 line-height: 1em;
1501 1515 }
1502 1516
1503 1517 .reviewer_name {
1504 1518 display: inline-block;
1505 1519 max-width: 83%;
1506 1520 padding-right: 20px;
1507 1521 vertical-align: middle;
1508 1522 line-height: 1;
1509 1523
1510 1524 .rc-user {
1511 1525 min-width: 0;
1512 1526 margin: -2px 1em 0 0;
1513 1527 }
1514 1528
1515 1529 .reviewer {
1516 1530 float: left;
1517 1531 }
1518 1532 }
1519 1533
1520 1534 .reviewer_member_mandatory {
1521 1535 position: absolute;
1522 1536 left: 15px;
1523 1537 top: 8px;
1524 1538 width: 16px;
1525 1539 font-size: 11px;
1526 1540 margin: 0;
1527 1541 padding: 0;
1528 1542 color: black;
1529 1543 }
1530 1544
1531 1545 .reviewer_member_mandatory_remove,
1532 1546 .reviewer_member_remove {
1533 1547 position: absolute;
1534 1548 right: 0;
1535 1549 top: 0;
1536 1550 width: 16px;
1537 1551 margin-bottom: 10px;
1538 1552 padding: 0;
1539 1553 color: black;
1540 1554 }
1541 1555
1542 1556 .reviewer_member_mandatory_remove {
1543 1557 color: @grey4;
1544 1558 }
1545 1559
1546 1560 .reviewer_member_status {
1547 1561 margin-top: 5px;
1548 1562 }
1549 1563 .pr-summary #summary{
1550 1564 width: 100%;
1551 1565 }
1552 1566 .pr-summary .action_button:hover {
1553 1567 border: 0;
1554 1568 cursor: pointer;
1555 1569 }
1556 1570 .pr-details-title {
1557 1571 padding-bottom: 8px;
1558 1572 border-bottom: @border-thickness solid @grey5;
1559 1573
1560 1574 .action_button.disabled {
1561 1575 color: @grey4;
1562 1576 cursor: inherit;
1563 1577 }
1564 1578 .action_button {
1565 1579 color: @rcblue;
1566 1580 }
1567 1581 }
1568 1582 .pr-details-content {
1569 margin-top: @textmargin;
1570 margin-bottom: @textmargin;
1583 margin-top: @textmargin - 5;
1584 margin-bottom: @textmargin - 5;
1571 1585 }
1572 1586
1573 1587 .pr-reviewer-rules {
1574 1588 padding: 10px 0px 20px 0px;
1575 1589 }
1576 1590
1591 .todo-resolved {
1592 text-decoration: line-through;
1593 }
1594
1595 .todo-table {
1596 width: 100%;
1597
1598 td {
1599 padding: 5px 0px;
1600 }
1601
1602 .td-todo-number {
1603 text-align: left;
1604 white-space: nowrap;
1605 width: 15%;
1606 }
1607
1608 .td-todo-gravatar {
1609 width: 5%;
1610
1611 img {
1612 margin: -3px 0;
1613 }
1614 }
1615
1616 }
1617
1618 .todo-comment-text-wrapper {
1619 display: inline-grid;
1620 }
1621
1622 .todo-comment-text {
1623 margin-left: 5px;
1624 white-space: nowrap;
1625 overflow: hidden;
1626 text-overflow: ellipsis;
1627 }
1628
1577 1629 .group_members {
1578 1630 margin-top: 0;
1579 1631 padding: 0;
1580 1632 list-style: outside none none;
1581 1633
1582 1634 img {
1583 1635 height: @gravatar-size;
1584 1636 width: @gravatar-size;
1585 1637 margin-right: .5em;
1586 1638 margin-left: 3px;
1587 1639 }
1588 1640
1589 1641 .to-delete {
1590 1642 .user {
1591 1643 text-decoration: line-through;
1592 1644 }
1593 1645 }
1594 1646 }
1595 1647
1596 1648 .compare_view_commits_title {
1597 1649 .disabled {
1598 1650 cursor: inherit;
1599 1651 &:hover{
1600 1652 background-color: inherit;
1601 1653 color: inherit;
1602 1654 }
1603 1655 }
1604 1656 }
1605 1657
1606 1658 .subtitle-compare {
1607 1659 margin: -15px 0px 0px 0px;
1608 1660 }
1609 1661
1610 1662 // new entry in group_members
1611 1663 .td-author-new-entry {
1612 1664 background-color: rgba(red(@alert1), green(@alert1), blue(@alert1), 0.3);
1613 1665 }
1614 1666
1615 1667 .usergroup_member_remove {
1616 1668 width: 16px;
1617 1669 margin-bottom: 10px;
1618 1670 padding: 0;
1619 1671 color: black !important;
1620 1672 cursor: pointer;
1621 1673 }
1622 1674
1623 1675 .reviewer_ac .ac-input {
1624 1676 width: 92%;
1625 1677 margin-bottom: 1em;
1626 1678 }
1627 1679
1628 1680 .compare_view_commits tr{
1629 1681 height: 20px;
1630 1682 }
1631 1683 .compare_view_commits td {
1632 1684 vertical-align: top;
1633 1685 padding-top: 10px;
1634 1686 }
1635 1687 .compare_view_commits .author {
1636 1688 margin-left: 5px;
1637 1689 }
1638 1690
1639 1691 .compare_view_commits {
1640 1692 .color-a {
1641 1693 color: @alert1;
1642 1694 }
1643 1695
1644 1696 .color-c {
1645 1697 color: @color3;
1646 1698 }
1647 1699
1648 1700 .color-r {
1649 1701 color: @color5;
1650 1702 }
1651 1703
1652 1704 .color-a-bg {
1653 1705 background-color: @alert1;
1654 1706 }
1655 1707
1656 1708 .color-c-bg {
1657 1709 background-color: @alert3;
1658 1710 }
1659 1711
1660 1712 .color-r-bg {
1661 1713 background-color: @alert2;
1662 1714 }
1663 1715
1664 1716 .color-a-border {
1665 1717 border: 1px solid @alert1;
1666 1718 }
1667 1719
1668 1720 .color-c-border {
1669 1721 border: 1px solid @alert3;
1670 1722 }
1671 1723
1672 1724 .color-r-border {
1673 1725 border: 1px solid @alert2;
1674 1726 }
1675 1727
1676 1728 .commit-change-indicator {
1677 1729 width: 15px;
1678 1730 height: 15px;
1679 1731 position: relative;
1680 1732 left: 15px;
1681 1733 }
1682 1734
1683 1735 .commit-change-content {
1684 1736 text-align: center;
1685 1737 vertical-align: middle;
1686 1738 line-height: 15px;
1687 1739 }
1688 1740 }
1689 1741
1690 1742 .compare_view_filepath {
1691 1743 color: @grey1;
1692 1744 }
1693 1745
1694 1746 .show_more {
1695 1747 display: inline-block;
1696 1748 width: 0;
1697 1749 height: 0;
1698 1750 vertical-align: middle;
1699 1751 content: "";
1700 1752 border: 4px solid;
1701 1753 border-right-color: transparent;
1702 1754 border-bottom-color: transparent;
1703 1755 border-left-color: transparent;
1704 1756 font-size: 0;
1705 1757 }
1706 1758
1707 1759 .journal_more .show_more {
1708 1760 display: inline;
1709 1761
1710 1762 &:after {
1711 1763 content: none;
1712 1764 }
1713 1765 }
1714 1766
1715 1767 .compare_view_commits .collapse_commit:after {
1716 1768 cursor: pointer;
1717 1769 content: "\00A0\25B4";
1718 1770 margin-left: -3px;
1719 1771 font-size: 17px;
1720 1772 color: @grey4;
1721 1773 }
1722 1774
1723 1775 .diff_links {
1724 1776 margin-left: 8px;
1725 1777 }
1726 1778
1727 1779 #pull_request_overview {
1728 1780 div.ancestor {
1729 1781 margin: -33px 0;
1730 1782 }
1731 1783 }
1732 1784
1733 1785 div.ancestor {
1734 1786 line-height: 33px;
1735 1787 }
1736 1788
1737 1789 .cs_icon_td input[type="checkbox"] {
1738 1790 display: none;
1739 1791 }
1740 1792
1741 1793 .cs_icon_td .expand_file_icon:after {
1742 1794 cursor: pointer;
1743 1795 content: "\00A0\25B6";
1744 1796 font-size: 12px;
1745 1797 color: @grey4;
1746 1798 }
1747 1799
1748 1800 .cs_icon_td .collapse_file_icon:after {
1749 1801 cursor: pointer;
1750 1802 content: "\00A0\25BC";
1751 1803 font-size: 12px;
1752 1804 color: @grey4;
1753 1805 }
1754 1806
1755 1807 /*new binary
1756 1808 NEW_FILENODE = 1
1757 1809 DEL_FILENODE = 2
1758 1810 MOD_FILENODE = 3
1759 1811 RENAMED_FILENODE = 4
1760 1812 COPIED_FILENODE = 5
1761 1813 CHMOD_FILENODE = 6
1762 1814 BIN_FILENODE = 7
1763 1815 */
1764 1816 .cs_files_expand {
1765 1817 font-size: @basefontsize + 5px;
1766 1818 line-height: 1.8em;
1767 1819 float: right;
1768 1820 }
1769 1821
1770 1822 .cs_files_expand span{
1771 1823 color: @rcblue;
1772 1824 cursor: pointer;
1773 1825 }
1774 1826 .cs_files {
1775 1827 clear: both;
1776 1828 padding-bottom: @padding;
1777 1829
1778 1830 .cur_cs {
1779 1831 margin: 10px 2px;
1780 1832 font-weight: bold;
1781 1833 }
1782 1834
1783 1835 .node {
1784 1836 float: left;
1785 1837 }
1786 1838
1787 1839 .changes {
1788 1840 float: right;
1789 1841 color: white;
1790 1842 font-size: @basefontsize - 4px;
1791 1843 margin-top: 4px;
1792 1844 opacity: 0.6;
1793 1845 filter: Alpha(opacity=60); /* IE8 and earlier */
1794 1846
1795 1847 .added {
1796 1848 background-color: @alert1;
1797 1849 float: left;
1798 1850 text-align: center;
1799 1851 }
1800 1852
1801 1853 .deleted {
1802 1854 background-color: @alert2;
1803 1855 float: left;
1804 1856 text-align: center;
1805 1857 }
1806 1858
1807 1859 .bin {
1808 1860 background-color: @alert1;
1809 1861 text-align: center;
1810 1862 }
1811 1863
1812 1864 /*new binary*/
1813 1865 .bin.bin1 {
1814 1866 background-color: @alert1;
1815 1867 text-align: center;
1816 1868 }
1817 1869
1818 1870 /*deleted binary*/
1819 1871 .bin.bin2 {
1820 1872 background-color: @alert2;
1821 1873 text-align: center;
1822 1874 }
1823 1875
1824 1876 /*mod binary*/
1825 1877 .bin.bin3 {
1826 1878 background-color: @grey2;
1827 1879 text-align: center;
1828 1880 }
1829 1881
1830 1882 /*rename file*/
1831 1883 .bin.bin4 {
1832 1884 background-color: @alert4;
1833 1885 text-align: center;
1834 1886 }
1835 1887
1836 1888 /*copied file*/
1837 1889 .bin.bin5 {
1838 1890 background-color: @alert4;
1839 1891 text-align: center;
1840 1892 }
1841 1893
1842 1894 /*chmod file*/
1843 1895 .bin.bin6 {
1844 1896 background-color: @grey2;
1845 1897 text-align: center;
1846 1898 }
1847 1899 }
1848 1900 }
1849 1901
1850 1902 .cs_files .cs_added, .cs_files .cs_A,
1851 1903 .cs_files .cs_added, .cs_files .cs_M,
1852 1904 .cs_files .cs_added, .cs_files .cs_D {
1853 1905 height: 16px;
1854 1906 padding-right: 10px;
1855 1907 margin-top: 7px;
1856 1908 text-align: left;
1857 1909 }
1858 1910
1859 1911 .cs_icon_td {
1860 1912 min-width: 16px;
1861 1913 width: 16px;
1862 1914 }
1863 1915
1864 1916 .pull-request-merge {
1865 1917 border: 1px solid @grey5;
1866 1918 padding: 10px 0px 20px;
1867 1919 margin-top: 10px;
1868 1920 margin-bottom: 20px;
1869 1921 }
1870 1922
1871 1923 .pull-request-merge-refresh {
1872 1924 margin: 2px 7px;
1873 1925 a {
1874 1926 color: @grey3;
1875 1927 }
1876 1928 }
1877 1929
1878 1930 .pull-request-merge ul {
1879 1931 padding: 0px 0px;
1880 1932 }
1881 1933
1882 1934 .pull-request-merge li {
1883 1935 list-style-type: none;
1884 1936 }
1885 1937
1886 1938 .pull-request-merge .pull-request-wrap {
1887 1939 height: auto;
1888 1940 padding: 0px 0px;
1889 1941 text-align: right;
1890 1942 }
1891 1943
1892 1944 .pull-request-merge span {
1893 1945 margin-right: 5px;
1894 1946 }
1895 1947
1896 1948 .pull-request-merge-actions {
1897 1949 min-height: 30px;
1898 1950 padding: 0px 0px;
1899 1951 }
1900 1952
1901 1953 .pull-request-merge-info {
1902 1954 padding: 0px 5px 5px 0px;
1903 1955 }
1904 1956
1905 1957 .merge-status {
1906 1958 margin-right: 5px;
1907 1959 }
1908 1960
1909 1961 .merge-message {
1910 1962 font-size: 1.2em
1911 1963 }
1912 1964
1913 1965 .merge-message.success i,
1914 1966 .merge-icon.success i {
1915 1967 color:@alert1;
1916 1968 }
1917 1969
1918 1970 .merge-message.warning i,
1919 1971 .merge-icon.warning i {
1920 1972 color: @alert3;
1921 1973 }
1922 1974
1923 1975 .merge-message.error i,
1924 1976 .merge-icon.error i {
1925 1977 color:@alert2;
1926 1978 }
1927 1979
1928 1980 .pr-versions {
1929 1981 font-size: 1.1em;
1930 1982 padding: 7.5px;
1931 1983
1932 1984 table {
1933 1985
1934 1986 }
1935 1987
1936 1988 td {
1937 1989 line-height: 15px;
1938 1990 }
1939 1991
1940 1992 .compare-radio-button {
1941 1993 position: relative;
1942 1994 top: -3px;
1943 1995 }
1944 1996 }
1945 1997
1946 1998
1947 1999 #close_pull_request {
1948 2000 margin-right: 0px;
1949 2001 }
1950 2002
1951 2003 .empty_data {
1952 2004 color: @grey4;
1953 2005 }
1954 2006
1955 2007 #changeset_compare_view_content {
1956 2008 clear: both;
1957 2009 width: 100%;
1958 2010 box-sizing: border-box;
1959 2011 .border-radius(@border-radius);
1960 2012
1961 2013 .help-block {
1962 2014 margin: @padding 0;
1963 2015 color: @text-color;
1964 2016 &.pre-formatting {
1965 2017 white-space: pre;
1966 2018 }
1967 2019 }
1968 2020
1969 2021 .empty_data {
1970 2022 margin: @padding 0;
1971 2023 }
1972 2024
1973 2025 .alert {
1974 2026 margin-bottom: @space;
1975 2027 }
1976 2028 }
1977 2029
1978 2030 .table_disp {
1979 2031 .status {
1980 2032 width: auto;
1981 2033 }
1982 2034 }
1983 2035
1984 2036
1985 2037 .creation_in_progress {
1986 2038 color: @grey4
1987 2039 }
1988 2040
1989 2041 .status_box_menu {
1990 2042 margin: 0;
1991 2043 }
1992 2044
1993 2045 .notification-table{
1994 2046 margin-bottom: @space;
1995 2047 display: table;
1996 2048 width: 100%;
1997 2049
1998 2050 .container{
1999 2051 display: table-row;
2000 2052
2001 2053 .notification-header{
2002 2054 border-bottom: @border-thickness solid @border-default-color;
2003 2055 }
2004 2056
2005 2057 .notification-subject{
2006 2058 display: table-cell;
2007 2059 }
2008 2060 }
2009 2061 }
2010 2062
2011 2063 // Notifications
2012 2064 .notification-header{
2013 2065 display: table;
2014 2066 width: 100%;
2015 2067 padding: floor(@basefontsize/2) 0;
2016 2068 line-height: 1em;
2017 2069
2018 2070 .desc, .delete-notifications, .read-notifications{
2019 2071 display: table-cell;
2020 2072 text-align: left;
2021 2073 }
2022 2074
2023 2075 .delete-notifications, .read-notifications{
2024 2076 width: 35px;
2025 2077 min-width: 35px; //fixes when only one button is displayed
2026 2078 }
2027 2079 }
2028 2080
2029 2081 .notification-body {
2030 2082 .markdown-block,
2031 2083 .rst-block {
2032 2084 padding: @padding 0;
2033 2085 }
2034 2086
2035 2087 .notification-subject {
2036 2088 padding: @textmargin 0;
2037 2089 border-bottom: @border-thickness solid @border-default-color;
2038 2090 }
2039 2091 }
2040 2092
2041 2093
2042 2094 .notifications_buttons{
2043 2095 float: right;
2044 2096 }
2045 2097
2046 2098 #notification-status{
2047 2099 display: inline;
2048 2100 }
2049 2101
2050 2102 // Repositories
2051 2103
2052 2104 #summary.fields{
2053 2105 display: table;
2054 2106
2055 2107 .field{
2056 2108 display: table-row;
2057 2109
2058 2110 .label-summary{
2059 2111 display: table-cell;
2060 2112 min-width: @label-summary-minwidth;
2061 2113 padding-top: @padding/2;
2062 2114 padding-bottom: @padding/2;
2063 2115 padding-right: @padding/2;
2064 2116 }
2065 2117
2066 2118 .input{
2067 2119 display: table-cell;
2068 2120 padding: @padding/2;
2069 2121
2070 2122 input{
2071 2123 min-width: 29em;
2072 2124 padding: @padding/4;
2073 2125 }
2074 2126 }
2075 2127 .statistics, .downloads{
2076 2128 .disabled{
2077 2129 color: @grey4;
2078 2130 }
2079 2131 }
2080 2132 }
2081 2133 }
2082 2134
2083 2135 #summary{
2084 2136 width: 70%;
2085 2137 }
2086 2138
2087 2139
2088 2140 // Journal
2089 2141 .journal.title {
2090 2142 h5 {
2091 2143 float: left;
2092 2144 margin: 0;
2093 2145 width: 70%;
2094 2146 }
2095 2147
2096 2148 ul {
2097 2149 float: right;
2098 2150 display: inline-block;
2099 2151 margin: 0;
2100 2152 width: 30%;
2101 2153 text-align: right;
2102 2154
2103 2155 li {
2104 2156 display: inline;
2105 2157 font-size: @journal-fontsize;
2106 2158 line-height: 1em;
2107 2159
2108 2160 list-style-type: none;
2109 2161 }
2110 2162 }
2111 2163 }
2112 2164
2113 2165 .filterexample {
2114 2166 position: absolute;
2115 2167 top: 95px;
2116 2168 left: @contentpadding;
2117 2169 color: @rcblue;
2118 2170 font-size: 11px;
2119 2171 font-family: @text-regular;
2120 2172 cursor: help;
2121 2173
2122 2174 &:hover {
2123 2175 color: @rcdarkblue;
2124 2176 }
2125 2177
2126 2178 @media (max-width:768px) {
2127 2179 position: relative;
2128 2180 top: auto;
2129 2181 left: auto;
2130 2182 display: block;
2131 2183 }
2132 2184 }
2133 2185
2134 2186
2135 2187 #journal{
2136 2188 margin-bottom: @space;
2137 2189
2138 2190 .journal_day{
2139 2191 margin-bottom: @textmargin/2;
2140 2192 padding-bottom: @textmargin/2;
2141 2193 font-size: @journal-fontsize;
2142 2194 border-bottom: @border-thickness solid @border-default-color;
2143 2195 }
2144 2196
2145 2197 .journal_container{
2146 2198 margin-bottom: @space;
2147 2199
2148 2200 .journal_user{
2149 2201 display: inline-block;
2150 2202 }
2151 2203 .journal_action_container{
2152 2204 display: block;
2153 2205 margin-top: @textmargin;
2154 2206
2155 2207 div{
2156 2208 display: inline;
2157 2209 }
2158 2210
2159 2211 div.journal_action_params{
2160 2212 display: block;
2161 2213 }
2162 2214
2163 2215 div.journal_repo:after{
2164 2216 content: "\A";
2165 2217 white-space: pre;
2166 2218 }
2167 2219
2168 2220 div.date{
2169 2221 display: block;
2170 2222 margin-bottom: @textmargin;
2171 2223 }
2172 2224 }
2173 2225 }
2174 2226 }
2175 2227
2176 2228 // Files
2177 2229 .edit-file-title {
2178 2230 font-size: 16px;
2179 2231
2180 2232 .title-heading {
2181 2233 padding: 2px;
2182 2234 }
2183 2235 }
2184 2236
2185 2237 .edit-file-fieldset {
2186 2238 margin: @sidebarpadding 0;
2187 2239
2188 2240 .fieldset {
2189 2241 .left-label {
2190 2242 width: 13%;
2191 2243 }
2192 2244 .right-content {
2193 2245 width: 87%;
2194 2246 max-width: 100%;
2195 2247 }
2196 2248 .filename-label {
2197 2249 margin-top: 13px;
2198 2250 }
2199 2251 .commit-message-label {
2200 2252 margin-top: 4px;
2201 2253 }
2202 2254 .file-upload-input {
2203 2255 input {
2204 2256 display: none;
2205 2257 }
2206 2258 margin-top: 10px;
2207 2259 }
2208 2260 .file-upload-label {
2209 2261 margin-top: 10px;
2210 2262 }
2211 2263 p {
2212 2264 margin-top: 5px;
2213 2265 }
2214 2266
2215 2267 }
2216 2268 .custom-path-link {
2217 2269 margin-left: 5px;
2218 2270 }
2219 2271 #commit {
2220 2272 resize: vertical;
2221 2273 }
2222 2274 }
2223 2275
2224 2276 .delete-file-preview {
2225 2277 max-height: 250px;
2226 2278 }
2227 2279
2228 2280 .new-file,
2229 2281 #filter_activate,
2230 2282 #filter_deactivate {
2231 2283 float: right;
2232 2284 margin: 0 0 0 10px;
2233 2285 }
2234 2286
2235 2287 .file-upload-transaction-wrapper {
2236 2288 margin-top: 57px;
2237 2289 clear: both;
2238 2290 }
2239 2291
2240 2292 .file-upload-transaction-wrapper .error {
2241 2293 color: @color5;
2242 2294 }
2243 2295
2244 2296 .file-upload-transaction {
2245 2297 min-height: 200px;
2246 2298 padding: 54px;
2247 2299 border: 1px solid @grey5;
2248 2300 text-align: center;
2249 2301 clear: both;
2250 2302 }
2251 2303
2252 2304 .file-upload-transaction i {
2253 2305 font-size: 48px
2254 2306 }
2255 2307
2256 2308 h3.files_location{
2257 2309 line-height: 2.4em;
2258 2310 }
2259 2311
2260 2312 .browser-nav {
2261 2313 width: 100%;
2262 2314 display: table;
2263 2315 margin-bottom: 20px;
2264 2316
2265 2317 .info_box {
2266 2318 float: left;
2267 2319 display: inline-table;
2268 2320 height: 2.5em;
2269 2321
2270 2322 .browser-cur-rev, .info_box_elem {
2271 2323 display: table-cell;
2272 2324 vertical-align: middle;
2273 2325 }
2274 2326
2275 2327 .drop-menu {
2276 2328 margin: 0 10px;
2277 2329 }
2278 2330
2279 2331 .info_box_elem {
2280 2332 border-top: @border-thickness solid @grey5;
2281 2333 border-bottom: @border-thickness solid @grey5;
2282 2334 box-shadow: @button-shadow;
2283 2335
2284 2336 #at_rev, a {
2285 2337 padding: 0.6em 0.4em;
2286 2338 margin: 0;
2287 2339 .box-shadow(none);
2288 2340 border: 0;
2289 2341 height: 12px;
2290 2342 color: @grey2;
2291 2343 }
2292 2344
2293 2345 input#at_rev {
2294 2346 max-width: 50px;
2295 2347 text-align: center;
2296 2348 }
2297 2349
2298 2350 &.previous {
2299 2351 border: @border-thickness solid @grey5;
2300 2352 border-top-left-radius: @border-radius;
2301 2353 border-bottom-left-radius: @border-radius;
2302 2354
2303 2355 &:hover {
2304 2356 border-color: @grey4;
2305 2357 }
2306 2358
2307 2359 .disabled {
2308 2360 color: @grey5;
2309 2361 cursor: not-allowed;
2310 2362 opacity: 0.5;
2311 2363 }
2312 2364 }
2313 2365
2314 2366 &.next {
2315 2367 border: @border-thickness solid @grey5;
2316 2368 border-top-right-radius: @border-radius;
2317 2369 border-bottom-right-radius: @border-radius;
2318 2370
2319 2371 &:hover {
2320 2372 border-color: @grey4;
2321 2373 }
2322 2374
2323 2375 .disabled {
2324 2376 color: @grey5;
2325 2377 cursor: not-allowed;
2326 2378 opacity: 0.5;
2327 2379 }
2328 2380 }
2329 2381 }
2330 2382
2331 2383 .browser-cur-rev {
2332 2384
2333 2385 span{
2334 2386 margin: 0;
2335 2387 color: @rcblue;
2336 2388 height: 12px;
2337 2389 display: inline-block;
2338 2390 padding: 0.7em 1em ;
2339 2391 border: @border-thickness solid @rcblue;
2340 2392 margin-right: @padding;
2341 2393 }
2342 2394 }
2343 2395
2344 2396 }
2345 2397
2346 2398 .select-index-number {
2347 2399 margin: 0 0 0 20px;
2348 2400 color: @grey3;
2349 2401 }
2350 2402
2351 2403 .search_activate {
2352 2404 display: table-cell;
2353 2405 vertical-align: middle;
2354 2406
2355 2407 input, label{
2356 2408 margin: 0;
2357 2409 padding: 0;
2358 2410 }
2359 2411
2360 2412 input{
2361 2413 margin-left: @textmargin;
2362 2414 }
2363 2415
2364 2416 }
2365 2417 }
2366 2418
2367 2419 .browser-cur-rev{
2368 2420 margin-bottom: @textmargin;
2369 2421 }
2370 2422
2371 2423 #node_filter_box_loading{
2372 2424 .info_text;
2373 2425 }
2374 2426
2375 2427 .browser-search {
2376 2428 margin: -25px 0px 5px 0px;
2377 2429 }
2378 2430
2379 2431 .files-quick-filter {
2380 2432 float: right;
2381 2433 width: 180px;
2382 2434 position: relative;
2383 2435 }
2384 2436
2385 2437 .files-filter-box {
2386 2438 display: flex;
2387 2439 padding: 0px;
2388 2440 border-radius: 3px;
2389 2441 margin-bottom: 0;
2390 2442
2391 2443 a {
2392 2444 border: none !important;
2393 2445 }
2394 2446
2395 2447 li {
2396 2448 list-style-type: none
2397 2449 }
2398 2450 }
2399 2451
2400 2452 .files-filter-box-path {
2401 2453 line-height: 33px;
2402 2454 padding: 0;
2403 2455 width: 20px;
2404 2456 position: absolute;
2405 2457 z-index: 11;
2406 2458 left: 5px;
2407 2459 }
2408 2460
2409 2461 .files-filter-box-input {
2410 2462 margin-right: 0;
2411 2463
2412 2464 input {
2413 2465 border: 1px solid @white;
2414 2466 padding-left: 25px;
2415 2467 width: 145px;
2416 2468
2417 2469 &:hover {
2418 2470 border-color: @grey6;
2419 2471 }
2420 2472
2421 2473 &:focus {
2422 2474 border-color: @grey5;
2423 2475 }
2424 2476 }
2425 2477 }
2426 2478
2427 2479 .browser-result{
2428 2480 td a{
2429 2481 margin-left: 0.5em;
2430 2482 display: inline-block;
2431 2483
2432 2484 em {
2433 2485 font-weight: @text-bold-weight;
2434 2486 font-family: @text-bold;
2435 2487 }
2436 2488 }
2437 2489 }
2438 2490
2439 2491 .browser-highlight{
2440 2492 background-color: @grey5-alpha;
2441 2493 }
2442 2494
2443 2495
2444 2496 .edit-file-fieldset #location,
2445 2497 .edit-file-fieldset #filename {
2446 2498 display: flex;
2447 2499 width: -moz-available; /* WebKit-based browsers will ignore this. */
2448 2500 width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
2449 2501 width: fill-available;
2450 2502 border: 0;
2451 2503 }
2452 2504
2453 2505 .path-items {
2454 2506 display: flex;
2455 2507 padding: 0;
2456 2508 border: 1px solid #eeeeee;
2457 2509 width: 100%;
2458 2510 float: left;
2459 2511
2460 2512 .breadcrumb-path {
2461 2513 line-height: 30px;
2462 2514 padding: 0 4px;
2463 2515 white-space: nowrap;
2464 2516 }
2465 2517
2466 2518 .location-path {
2467 2519 width: -moz-available; /* WebKit-based browsers will ignore this. */
2468 2520 width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
2469 2521 width: fill-available;
2470 2522
2471 2523 .file-name-input {
2472 2524 padding: 0.5em 0;
2473 2525 }
2474 2526
2475 2527 }
2476 2528
2477 2529 ul {
2478 2530 display: flex;
2479 2531 margin: 0;
2480 2532 padding: 0;
2481 2533 width: 100%;
2482 2534 }
2483 2535
2484 2536 li {
2485 2537 list-style-type: none;
2486 2538 }
2487 2539
2488 2540 }
2489 2541
2490 2542 .editor-items {
2491 2543 height: 40px;
2492 2544 margin: 10px 0 -17px 10px;
2493 2545
2494 2546 .editor-action {
2495 2547 cursor: pointer;
2496 2548 }
2497 2549
2498 2550 .editor-action.active {
2499 2551 border-bottom: 2px solid #5C5C5C;
2500 2552 }
2501 2553
2502 2554 li {
2503 2555 list-style-type: none;
2504 2556 }
2505 2557 }
2506 2558
2507 2559 .edit-file-fieldset .message textarea {
2508 2560 border: 1px solid #eeeeee;
2509 2561 }
2510 2562
2511 2563 #files_data .codeblock {
2512 2564 background-color: #F5F5F5;
2513 2565 }
2514 2566
2515 2567 #editor_preview {
2516 2568 background: white;
2517 2569 }
2518 2570
2519 2571 .show-editor {
2520 2572 padding: 10px;
2521 2573 background-color: white;
2522 2574
2523 2575 }
2524 2576
2525 2577 .show-preview {
2526 2578 padding: 10px;
2527 2579 background-color: white;
2528 2580 border-left: 1px solid #eeeeee;
2529 2581 }
2530 2582 // quick filter
2531 2583 .grid-quick-filter {
2532 2584 float: right;
2533 2585 position: relative;
2534 2586 }
2535 2587
2536 2588 .grid-filter-box {
2537 2589 display: flex;
2538 2590 padding: 0px;
2539 2591 border-radius: 3px;
2540 2592 margin-bottom: 0;
2541 2593
2542 2594 a {
2543 2595 border: none !important;
2544 2596 }
2545 2597
2546 2598 li {
2547 2599 list-style-type: none
2548 2600 }
2549 2601 }
2550 2602
2551 2603 .grid-filter-box-icon {
2552 2604 line-height: 33px;
2553 2605 padding: 0;
2554 2606 width: 20px;
2555 2607 position: absolute;
2556 2608 z-index: 11;
2557 2609 left: 5px;
2558 2610 }
2559 2611
2560 2612 .grid-filter-box-input {
2561 2613 margin-right: 0;
2562 2614
2563 2615 input {
2564 2616 border: 1px solid @white;
2565 2617 padding-left: 25px;
2566 2618 width: 145px;
2567 2619
2568 2620 &:hover {
2569 2621 border-color: @grey6;
2570 2622 }
2571 2623
2572 2624 &:focus {
2573 2625 border-color: @grey5;
2574 2626 }
2575 2627 }
2576 2628 }
2577 2629
2578 2630
2579 2631
2580 2632 // Search
2581 2633
2582 2634 .search-form{
2583 2635 #q {
2584 2636 width: @search-form-width;
2585 2637 }
2586 2638 .fields{
2587 2639 margin: 0 0 @space;
2588 2640 }
2589 2641
2590 2642 label{
2591 2643 display: inline-block;
2592 2644 margin-right: @textmargin;
2593 2645 padding-top: 0.25em;
2594 2646 }
2595 2647
2596 2648
2597 2649 .results{
2598 2650 clear: both;
2599 2651 margin: 0 0 @padding;
2600 2652 }
2601 2653
2602 2654 .search-tags {
2603 2655 padding: 5px 0;
2604 2656 }
2605 2657 }
2606 2658
2607 2659 div.search-feedback-items {
2608 2660 display: inline-block;
2609 2661 }
2610 2662
2611 2663 div.search-code-body {
2612 2664 background-color: #ffffff; padding: 5px 0 5px 10px;
2613 2665 pre {
2614 2666 .match { background-color: #faffa6;}
2615 2667 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
2616 2668 }
2617 2669 }
2618 2670
2619 2671 .expand_commit.search {
2620 2672 .show_more.open {
2621 2673 height: auto;
2622 2674 max-height: none;
2623 2675 }
2624 2676 }
2625 2677
2626 2678 .search-results {
2627 2679
2628 2680 h2 {
2629 2681 margin-bottom: 0;
2630 2682 }
2631 2683 .codeblock {
2632 2684 border: none;
2633 2685 background: transparent;
2634 2686 }
2635 2687
2636 2688 .codeblock-header {
2637 2689 border: none;
2638 2690 background: transparent;
2639 2691 }
2640 2692
2641 2693 .code-body {
2642 2694 border: @border-thickness solid @grey6;
2643 2695 .border-radius(@border-radius);
2644 2696 }
2645 2697
2646 2698 .td-commit {
2647 2699 &:extend(pre);
2648 2700 border-bottom: @border-thickness solid @border-default-color;
2649 2701 }
2650 2702
2651 2703 .message {
2652 2704 height: auto;
2653 2705 max-width: 350px;
2654 2706 white-space: normal;
2655 2707 text-overflow: initial;
2656 2708 overflow: visible;
2657 2709
2658 2710 .match { background-color: #faffa6;}
2659 2711 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
2660 2712 }
2661 2713
2662 2714 .path {
2663 2715 border-bottom: none !important;
2664 2716 border-left: 1px solid @grey6 !important;
2665 2717 border-right: 1px solid @grey6 !important;
2666 2718 }
2667 2719 }
2668 2720
2669 2721 table.rctable td.td-search-results div {
2670 2722 max-width: 100%;
2671 2723 }
2672 2724
2673 2725 #tip-box, .tip-box{
2674 2726 padding: @menupadding/2;
2675 2727 display: block;
2676 2728 border: @border-thickness solid @border-highlight-color;
2677 2729 .border-radius(@border-radius);
2678 2730 background-color: white;
2679 2731 z-index: 99;
2680 2732 white-space: pre-wrap;
2681 2733 }
2682 2734
2683 2735 #linktt {
2684 2736 width: 79px;
2685 2737 }
2686 2738
2687 2739 #help_kb .modal-content{
2688 2740 max-width: 750px;
2689 2741 margin: 10% auto;
2690 2742
2691 2743 table{
2692 2744 td,th{
2693 2745 border-bottom: none;
2694 2746 line-height: 2.5em;
2695 2747 }
2696 2748 th{
2697 2749 padding-bottom: @textmargin/2;
2698 2750 }
2699 2751 td.keys{
2700 2752 text-align: center;
2701 2753 }
2702 2754 }
2703 2755
2704 2756 .block-left{
2705 2757 width: 45%;
2706 2758 margin-right: 5%;
2707 2759 }
2708 2760 .modal-footer{
2709 2761 clear: both;
2710 2762 }
2711 2763 .key.tag{
2712 2764 padding: 0.5em;
2713 2765 background-color: @rcblue;
2714 2766 color: white;
2715 2767 border-color: @rcblue;
2716 2768 .box-shadow(none);
2717 2769 }
2718 2770 }
2719 2771
2720 2772
2721 2773
2722 2774 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2723 2775
2724 2776 @import 'statistics-graph';
2725 2777 @import 'tables';
2726 2778 @import 'forms';
2727 2779 @import 'diff';
2728 2780 @import 'summary';
2729 2781 @import 'navigation';
2730 2782
2731 2783 //--- SHOW/HIDE SECTIONS --//
2732 2784
2733 2785 .btn-collapse {
2734 2786 float: right;
2735 2787 text-align: right;
2736 2788 font-family: @text-light;
2737 2789 font-size: @basefontsize;
2738 2790 cursor: pointer;
2739 2791 border: none;
2740 2792 color: @rcblue;
2741 2793 }
2742 2794
2743 2795 table.rctable,
2744 2796 table.dataTable {
2745 2797 .btn-collapse {
2746 2798 float: right;
2747 2799 text-align: right;
2748 2800 }
2749 2801 }
2750 2802
2751 2803 table.rctable {
2752 2804 &.permissions {
2753 2805
2754 2806 th.td-owner {
2755 2807 padding: 0;
2756 2808 }
2757 2809
2758 2810 th {
2759 2811 font-weight: normal;
2760 2812 padding: 0 5px;
2761 2813 }
2762 2814
2763 2815 }
2764 2816 }
2765 2817
2766 2818
2767 2819 // TODO: johbo: Fix for IE10, this avoids that we see a border
2768 2820 // and padding around checkboxes and radio boxes. Move to the right place,
2769 2821 // or better: Remove this once we did the form refactoring.
2770 2822 input[type=checkbox],
2771 2823 input[type=radio] {
2772 2824 padding: 0;
2773 2825 border: none;
2774 2826 }
2775 2827
2776 2828 .toggle-ajax-spinner{
2777 2829 height: 16px;
2778 2830 width: 16px;
2779 2831 }
2780 2832
2781 2833
2782 2834 .markup-form .clearfix {
2783 2835 .border-radius(@border-radius);
2784 2836 margin: 0px;
2785 2837 }
2786 2838
2787 2839 .markup-form-area {
2788 2840 padding: 8px 12px;
2789 2841 border: 1px solid @grey4;
2790 2842 .border-radius(@border-radius);
2791 2843 }
2792 2844
2793 2845 .markup-form-area-header .nav-links {
2794 2846 display: flex;
2795 2847 flex-flow: row wrap;
2796 2848 -webkit-flex-flow: row wrap;
2797 2849 width: 100%;
2798 2850 }
2799 2851
2800 2852 .markup-form-area-footer {
2801 2853 display: flex;
2802 2854 }
2803 2855
2804 2856 .markup-form-area-footer .toolbar {
2805 2857
2806 2858 }
2807 2859
2808 2860 // markup Form
2809 2861 div.markup-form {
2810 2862 margin-top: 20px;
2811 2863 }
2812 2864
2813 2865 .markup-form strong {
2814 2866 display: block;
2815 2867 margin-bottom: 15px;
2816 2868 }
2817 2869
2818 2870 .markup-form textarea {
2819 2871 width: 100%;
2820 2872 height: 100px;
2821 2873 font-family: @text-monospace;
2822 2874 }
2823 2875
2824 2876 form.markup-form {
2825 2877 margin-top: 10px;
2826 2878 margin-left: 10px;
2827 2879 }
2828 2880
2829 2881 .markup-form .comment-block-ta,
2830 2882 .markup-form .preview-box {
2831 2883 .border-radius(@border-radius);
2832 2884 .box-sizing(border-box);
2833 2885 background-color: white;
2834 2886 }
2835 2887
2836 2888 .markup-form .preview-box.unloaded {
2837 2889 height: 50px;
2838 2890 text-align: center;
2839 2891 padding: 20px;
2840 2892 background-color: white;
2841 2893 }
2842 2894
2843 2895
2844 2896 .dropzone-wrapper {
2845 2897 border: 1px solid @grey5;
2846 2898 padding: 20px;
2847 2899 }
2848 2900
2849 2901 .dropzone,
2850 2902 .dropzone-pure {
2851 2903 border: 2px dashed @grey5;
2852 2904 border-radius: 5px;
2853 2905 background: white;
2854 2906 min-height: 200px;
2855 2907 padding: 54px;
2856 2908
2857 2909 .dz-message {
2858 2910 font-weight: 700;
2859 2911 text-align: center;
2860 2912 margin: 2em 0;
2861 2913 }
2862 2914
2863 2915 }
2864 2916
2865 2917 .dz-preview {
2866 2918 margin: 10px 0 !important;
2867 2919 position: relative;
2868 2920 vertical-align: top;
2869 2921 padding: 10px;
2870 2922 border-bottom: 1px solid @grey5;
2871 2923 }
2872 2924
2873 2925 .dz-filename {
2874 2926 font-weight: 700;
2875 2927 float: left;
2876 2928 }
2877 2929
2878 2930 .dz-sending {
2879 2931 float: right;
2880 2932 }
2881 2933
2882 2934 .dz-response {
2883 2935 clear: both
2884 2936 }
2885 2937
2886 2938 .dz-filename-size {
2887 2939 float: right
2888 2940 }
2889 2941
2890 2942 .dz-error-message {
2891 2943 color: @alert2;
2892 2944 padding-top: 10px;
2893 2945 clear: both;
2894 2946 }
2895 2947
2896 2948
2897 2949 .user-hovercard {
2898 2950 padding: 5px;
2899 2951 }
2900 2952
2901 2953 .user-hovercard-icon {
2902 2954 display: inline;
2903 2955 padding: 0;
2904 2956 box-sizing: content-box;
2905 2957 border-radius: 50%;
2906 2958 float: left;
2907 2959 }
2908 2960
2909 2961 .user-hovercard-name {
2910 2962 float: right;
2911 2963 vertical-align: top;
2912 2964 padding-left: 10px;
2913 2965 min-width: 150px;
2914 2966 }
2915 2967
2916 2968 .user-hovercard-bio {
2917 2969 clear: both;
2918 2970 padding-top: 10px;
2919 2971 }
2920 2972
2921 2973 .user-hovercard-header {
2922 2974 clear: both;
2923 2975 min-height: 10px;
2924 2976 }
2925 2977
2926 2978 .user-hovercard-footer {
2927 2979 clear: both;
2928 2980 min-height: 10px;
2929 2981 }
2930 2982
2931 2983 .user-group-hovercard {
2932 2984 padding: 5px;
2933 2985 }
2934 2986
2935 2987 .user-group-hovercard-icon {
2936 2988 display: inline;
2937 2989 padding: 0;
2938 2990 box-sizing: content-box;
2939 2991 border-radius: 50%;
2940 2992 float: left;
2941 2993 }
2942 2994
2943 2995 .user-group-hovercard-name {
2944 2996 float: left;
2945 2997 vertical-align: top;
2946 2998 padding-left: 10px;
2947 2999 min-width: 150px;
2948 3000 }
2949 3001
2950 3002 .user-group-hovercard-icon i {
2951 3003 border: 1px solid @grey4;
2952 3004 border-radius: 4px;
2953 3005 }
2954 3006
2955 3007 .user-group-hovercard-bio {
2956 3008 clear: both;
2957 3009 padding-top: 10px;
2958 3010 line-height: 1.0em;
2959 3011 }
2960 3012
2961 3013 .user-group-hovercard-header {
2962 3014 clear: both;
2963 3015 min-height: 10px;
2964 3016 }
2965 3017
2966 3018 .user-group-hovercard-footer {
2967 3019 clear: both;
2968 3020 min-height: 10px;
2969 3021 }
2970 3022
2971 3023 .pr-hovercard-header {
2972 3024 clear: both;
2973 3025 display: block;
2974 3026 line-height: 20px;
2975 3027 }
2976 3028
2977 3029 .pr-hovercard-user {
2978 3030 display: flex;
2979 3031 align-items: center;
2980 3032 padding-left: 5px;
2981 3033 }
2982 3034
2983 3035 .pr-hovercard-title {
2984 3036 padding-top: 5px;
2985 3037 } No newline at end of file
@@ -1,929 +1,931 b''
1 1 // # Copyright (C) 2010-2019 RhodeCode GmbH
2 2 // #
3 3 // # This program is free software: you can redistribute it and/or modify
4 4 // # it under the terms of the GNU Affero General Public License, version 3
5 5 // # (only), as published by the Free Software Foundation.
6 6 // #
7 7 // # This program is distributed in the hope that it will be useful,
8 8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 // # GNU General Public License for more details.
11 11 // #
12 12 // # You should have received a copy of the GNU Affero General Public License
13 13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 // #
15 15 // # This program is dual-licensed. If you wish to learn more about the
16 16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 var firefoxAnchorFix = function() {
20 20 // hack to make anchor links behave properly on firefox, in our inline
21 21 // comments generation when comments are injected firefox is misbehaving
22 22 // when jumping to anchor links
23 23 if (location.href.indexOf('#') > -1) {
24 24 location.href += '';
25 25 }
26 26 };
27 27
28 28 var linkifyComments = function(comments) {
29 29 var firstCommentId = null;
30 30 if (comments) {
31 31 firstCommentId = $(comments[0]).data('comment-id');
32 32 }
33 33
34 34 if (firstCommentId){
35 35 $('#inline-comments-counter').attr('href', '#comment-' + firstCommentId);
36 36 }
37 37 };
38 38
39 39 var bindToggleButtons = function() {
40 40 $('.comment-toggle').on('click', function() {
41 41 $(this).parent().nextUntil('tr.line').toggle('inline-comments');
42 42 });
43 43 };
44 44
45 45
46 46
47 47 var _submitAjaxPOST = function(url, postData, successHandler, failHandler) {
48 48 failHandler = failHandler || function() {};
49 49 postData = toQueryString(postData);
50 50 var request = $.ajax({
51 51 url: url,
52 52 type: 'POST',
53 53 data: postData,
54 54 headers: {'X-PARTIAL-XHR': true}
55 55 })
56 56 .done(function (data) {
57 57 successHandler(data);
58 58 })
59 59 .fail(function (data, textStatus, errorThrown) {
60 60 failHandler(data, textStatus, errorThrown)
61 61 });
62 62 return request;
63 63 };
64 64
65 65
66 66
67 67
68 68 /* Comment form for main and inline comments */
69 69 (function(mod) {
70 70
71 71 if (typeof exports == "object" && typeof module == "object") {
72 72 // CommonJS
73 73 module.exports = mod();
74 74 }
75 75 else {
76 76 // Plain browser env
77 77 (this || window).CommentForm = mod();
78 78 }
79 79
80 80 })(function() {
81 81 "use strict";
82 82
83 83 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId) {
84 84 if (!(this instanceof CommentForm)) {
85 85 return new CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions, resolvesCommentId);
86 86 }
87 87
88 88 // bind the element instance to our Form
89 89 $(formElement).get(0).CommentForm = this;
90 90
91 91 this.withLineNo = function(selector) {
92 92 var lineNo = this.lineNo;
93 93 if (lineNo === undefined) {
94 94 return selector
95 95 } else {
96 96 return selector + '_' + lineNo;
97 97 }
98 98 };
99 99
100 100 this.commitId = commitId;
101 101 this.pullRequestId = pullRequestId;
102 102 this.lineNo = lineNo;
103 103 this.initAutocompleteActions = initAutocompleteActions;
104 104
105 105 this.previewButton = this.withLineNo('#preview-btn');
106 106 this.previewContainer = this.withLineNo('#preview-container');
107 107
108 108 this.previewBoxSelector = this.withLineNo('#preview-box');
109 109
110 110 this.editButton = this.withLineNo('#edit-btn');
111 111 this.editContainer = this.withLineNo('#edit-container');
112 112 this.cancelButton = this.withLineNo('#cancel-btn');
113 113 this.commentType = this.withLineNo('#comment_type');
114 114
115 115 this.resolvesId = null;
116 116 this.resolvesActionId = null;
117 117
118 118 this.closesPr = '#close_pull_request';
119 119
120 120 this.cmBox = this.withLineNo('#text');
121 121 this.cm = initCommentBoxCodeMirror(this, this.cmBox, this.initAutocompleteActions);
122 122
123 123 this.statusChange = this.withLineNo('#change_status');
124 124
125 125 this.submitForm = formElement;
126 126 this.submitButton = $(this.submitForm).find('input[type="submit"]');
127 127 this.submitButtonText = this.submitButton.val();
128 128
129 129 this.previewUrl = pyroutes.url('repo_commit_comment_preview',
130 130 {'repo_name': templateContext.repo_name,
131 131 'commit_id': templateContext.commit_data.commit_id});
132 132
133 133 if (resolvesCommentId){
134 134 this.resolvesId = '#resolve_comment_{0}'.format(resolvesCommentId);
135 135 this.resolvesActionId = '#resolve_comment_action_{0}'.format(resolvesCommentId);
136 136 $(this.commentType).prop('disabled', true);
137 137 $(this.commentType).addClass('disabled');
138 138
139 139 // disable select
140 140 setTimeout(function() {
141 141 $(self.statusChange).select2('readonly', true);
142 142 }, 10);
143 143
144 144 var resolvedInfo = (
145 145 '<li class="resolve-action">' +
146 146 '<input type="hidden" id="resolve_comment_{0}" name="resolve_comment_{0}" value="{0}">' +
147 147 '<button id="resolve_comment_action_{0}" class="resolve-text btn btn-sm" onclick="return Rhodecode.comments.submitResolution({0})">{1} #{0}</button>' +
148 148 '</li>'
149 149 ).format(resolvesCommentId, _gettext('resolve comment'));
150 150 $(resolvedInfo).insertAfter($(this.commentType).parent());
151 151 }
152 152
153 153 // based on commitId, or pullRequestId decide where do we submit
154 154 // out data
155 155 if (this.commitId){
156 156 this.submitUrl = pyroutes.url('repo_commit_comment_create',
157 157 {'repo_name': templateContext.repo_name,
158 158 'commit_id': this.commitId});
159 159 this.selfUrl = pyroutes.url('repo_commit',
160 160 {'repo_name': templateContext.repo_name,
161 161 'commit_id': this.commitId});
162 162
163 163 } else if (this.pullRequestId) {
164 164 this.submitUrl = pyroutes.url('pullrequest_comment_create',
165 165 {'repo_name': templateContext.repo_name,
166 166 'pull_request_id': this.pullRequestId});
167 167 this.selfUrl = pyroutes.url('pullrequest_show',
168 168 {'repo_name': templateContext.repo_name,
169 169 'pull_request_id': this.pullRequestId});
170 170
171 171 } else {
172 172 throw new Error(
173 173 'CommentForm requires pullRequestId, or commitId to be specified.')
174 174 }
175 175
176 176 // FUNCTIONS and helpers
177 177 var self = this;
178 178
179 179 this.isInline = function(){
180 180 return this.lineNo && this.lineNo != 'general';
181 181 };
182 182
183 183 this.getCmInstance = function(){
184 184 return this.cm
185 185 };
186 186
187 187 this.setPlaceholder = function(placeholder) {
188 188 var cm = this.getCmInstance();
189 189 if (cm){
190 190 cm.setOption('placeholder', placeholder);
191 191 }
192 192 };
193 193
194 194 this.getCommentStatus = function() {
195 195 return $(this.submitForm).find(this.statusChange).val();
196 196 };
197 197 this.getCommentType = function() {
198 198 return $(this.submitForm).find(this.commentType).val();
199 199 };
200 200
201 201 this.getResolvesId = function() {
202 202 return $(this.submitForm).find(this.resolvesId).val() || null;
203 203 };
204 204
205 205 this.getClosePr = function() {
206 206 return $(this.submitForm).find(this.closesPr).val() || null;
207 207 };
208 208
209 209 this.markCommentResolved = function(resolvedCommentId){
210 210 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolved').show();
211 211 $('#comment-label-{0}'.format(resolvedCommentId)).find('.resolve').hide();
212 212 };
213 213
214 214 this.isAllowedToSubmit = function() {
215 215 return !$(this.submitButton).prop('disabled');
216 216 };
217 217
218 218 this.initStatusChangeSelector = function(){
219 219 var formatChangeStatus = function(state, escapeMarkup) {
220 220 var originalOption = state.element;
221 221 var tmpl = '<i class="icon-circle review-status-{0}"></i><span>{1}</span>'.format($(originalOption).data('status'), escapeMarkup(state.text));
222 222 return tmpl
223 223 };
224 224 var formatResult = function(result, container, query, escapeMarkup) {
225 225 return formatChangeStatus(result, escapeMarkup);
226 226 };
227 227
228 228 var formatSelection = function(data, container, escapeMarkup) {
229 229 return formatChangeStatus(data, escapeMarkup);
230 230 };
231 231
232 232 $(this.submitForm).find(this.statusChange).select2({
233 233 placeholder: _gettext('Status Review'),
234 234 formatResult: formatResult,
235 235 formatSelection: formatSelection,
236 236 containerCssClass: "drop-menu status_box_menu",
237 237 dropdownCssClass: "drop-menu-dropdown",
238 238 dropdownAutoWidth: true,
239 239 minimumResultsForSearch: -1
240 240 });
241 241 $(this.submitForm).find(this.statusChange).on('change', function() {
242 242 var status = self.getCommentStatus();
243 243
244 244 if (status && !self.isInline()) {
245 245 $(self.submitButton).prop('disabled', false);
246 246 }
247 247
248 248 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
249 249 self.setPlaceholder(placeholderText)
250 250 })
251 251 };
252 252
253 253 // reset the comment form into it's original state
254 254 this.resetCommentFormState = function(content) {
255 255 content = content || '';
256 256
257 257 $(this.editContainer).show();
258 258 $(this.editButton).parent().addClass('active');
259 259
260 260 $(this.previewContainer).hide();
261 261 $(this.previewButton).parent().removeClass('active');
262 262
263 263 this.setActionButtonsDisabled(true);
264 264 self.cm.setValue(content);
265 265 self.cm.setOption("readOnly", false);
266 266
267 267 if (this.resolvesId) {
268 268 // destroy the resolve action
269 269 $(this.resolvesId).parent().remove();
270 270 }
271 271 // reset closingPR flag
272 272 $('.close-pr-input').remove();
273 273
274 274 $(this.statusChange).select2('readonly', false);
275 275 };
276 276
277 277 this.globalSubmitSuccessCallback = function(){
278 278 // default behaviour is to call GLOBAL hook, if it's registered.
279 279 if (window.commentFormGlobalSubmitSuccessCallback !== undefined){
280 280 commentFormGlobalSubmitSuccessCallback()
281 281 }
282 282 };
283 283
284 284 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
285 285 return _submitAjaxPOST(url, postData, successHandler, failHandler);
286 286 };
287 287
288 288 // overwrite a submitHandler, we need to do it for inline comments
289 289 this.setHandleFormSubmit = function(callback) {
290 290 this.handleFormSubmit = callback;
291 291 };
292 292
293 293 // overwrite a submitSuccessHandler
294 294 this.setGlobalSubmitSuccessCallback = function(callback) {
295 295 this.globalSubmitSuccessCallback = callback;
296 296 };
297 297
298 298 // default handler for for submit for main comments
299 299 this.handleFormSubmit = function() {
300 300 var text = self.cm.getValue();
301 301 var status = self.getCommentStatus();
302 302 var commentType = self.getCommentType();
303 303 var resolvesCommentId = self.getResolvesId();
304 304 var closePullRequest = self.getClosePr();
305 305
306 306 if (text === "" && !status) {
307 307 return;
308 308 }
309 309
310 310 var excludeCancelBtn = false;
311 311 var submitEvent = true;
312 312 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
313 313 self.cm.setOption("readOnly", true);
314 314
315 315 var postData = {
316 316 'text': text,
317 317 'changeset_status': status,
318 318 'comment_type': commentType,
319 319 'csrf_token': CSRF_TOKEN
320 320 };
321 321
322 322 if (resolvesCommentId) {
323 323 postData['resolves_comment_id'] = resolvesCommentId;
324 324 }
325 325
326 326 if (closePullRequest) {
327 327 postData['close_pull_request'] = true;
328 328 }
329 329
330 330 var submitSuccessCallback = function(o) {
331 331 // reload page if we change status for single commit.
332 332 if (status && self.commitId) {
333 333 location.reload(true);
334 334 } else {
335 335 $('#injected_page_comments').append(o.rendered_text);
336 336 self.resetCommentFormState();
337 337 timeagoActivate();
338 338 tooltipActivate();
339 339
340 340 // mark visually which comment was resolved
341 341 if (resolvesCommentId) {
342 342 self.markCommentResolved(resolvesCommentId);
343 343 }
344 344 }
345 345
346 346 // run global callback on submit
347 347 self.globalSubmitSuccessCallback();
348 348
349 349 };
350 350 var submitFailCallback = function(data) {
351 351 alert(
352 352 "Error while submitting comment.\n" +
353 353 "Error code {0} ({1}).".format(data.status, data.statusText)
354 354 );
355 355 self.resetCommentFormState(text);
356 356 };
357 357 self.submitAjaxPOST(
358 358 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
359 359 };
360 360
361 361 this.previewSuccessCallback = function(o) {
362 362 $(self.previewBoxSelector).html(o);
363 363 $(self.previewBoxSelector).removeClass('unloaded');
364 364
365 365 // swap buttons, making preview active
366 366 $(self.previewButton).parent().addClass('active');
367 367 $(self.editButton).parent().removeClass('active');
368 368
369 369 // unlock buttons
370 370 self.setActionButtonsDisabled(false);
371 371 };
372 372
373 373 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
374 374 excludeCancelBtn = excludeCancelBtn || false;
375 375 submitEvent = submitEvent || false;
376 376
377 377 $(this.editButton).prop('disabled', state);
378 378 $(this.previewButton).prop('disabled', state);
379 379
380 380 if (!excludeCancelBtn) {
381 381 $(this.cancelButton).prop('disabled', state);
382 382 }
383 383
384 384 var submitState = state;
385 385 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
386 386 // if the value of commit review status is set, we allow
387 387 // submit button, but only on Main form, isInline means inline
388 388 submitState = false
389 389 }
390 390
391 391 $(this.submitButton).prop('disabled', submitState);
392 392 if (submitEvent) {
393 393 $(this.submitButton).val(_gettext('Submitting...'));
394 394 } else {
395 395 $(this.submitButton).val(this.submitButtonText);
396 396 }
397 397
398 398 };
399 399
400 400 // lock preview/edit/submit buttons on load, but exclude cancel button
401 401 var excludeCancelBtn = true;
402 402 this.setActionButtonsDisabled(true, excludeCancelBtn);
403 403
404 404 // anonymous users don't have access to initialized CM instance
405 405 if (this.cm !== undefined){
406 406 this.cm.on('change', function(cMirror) {
407 407 if (cMirror.getValue() === "") {
408 408 self.setActionButtonsDisabled(true, excludeCancelBtn)
409 409 } else {
410 410 self.setActionButtonsDisabled(false, excludeCancelBtn)
411 411 }
412 412 });
413 413 }
414 414
415 415 $(this.editButton).on('click', function(e) {
416 416 e.preventDefault();
417 417
418 418 $(self.previewButton).parent().removeClass('active');
419 419 $(self.previewContainer).hide();
420 420
421 421 $(self.editButton).parent().addClass('active');
422 422 $(self.editContainer).show();
423 423
424 424 });
425 425
426 426 $(this.previewButton).on('click', function(e) {
427 427 e.preventDefault();
428 428 var text = self.cm.getValue();
429 429
430 430 if (text === "") {
431 431 return;
432 432 }
433 433
434 434 var postData = {
435 435 'text': text,
436 436 'renderer': templateContext.visual.default_renderer,
437 437 'csrf_token': CSRF_TOKEN
438 438 };
439 439
440 440 // lock ALL buttons on preview
441 441 self.setActionButtonsDisabled(true);
442 442
443 443 $(self.previewBoxSelector).addClass('unloaded');
444 444 $(self.previewBoxSelector).html(_gettext('Loading ...'));
445 445
446 446 $(self.editContainer).hide();
447 447 $(self.previewContainer).show();
448 448
449 449 // by default we reset state of comment preserving the text
450 450 var previewFailCallback = function(data){
451 451 alert(
452 452 "Error while preview of comment.\n" +
453 453 "Error code {0} ({1}).".format(data.status, data.statusText)
454 454 );
455 455 self.resetCommentFormState(text)
456 456 };
457 457 self.submitAjaxPOST(
458 458 self.previewUrl, postData, self.previewSuccessCallback,
459 459 previewFailCallback);
460 460
461 461 $(self.previewButton).parent().addClass('active');
462 462 $(self.editButton).parent().removeClass('active');
463 463 });
464 464
465 465 $(this.submitForm).submit(function(e) {
466 466 e.preventDefault();
467 467 var allowedToSubmit = self.isAllowedToSubmit();
468 468 if (!allowedToSubmit){
469 469 return false;
470 470 }
471 471 self.handleFormSubmit();
472 472 });
473 473
474 474 }
475 475
476 476 return CommentForm;
477 477 });
478 478
479 479 /* comments controller */
480 480 var CommentsController = function() {
481 481 var mainComment = '#text';
482 482 var self = this;
483 483
484 484 this.cancelComment = function(node) {
485 485 var $node = $(node);
486 486 var $td = $node.closest('td');
487 487 $node.closest('.comment-inline-form').remove();
488 488 return false;
489 489 };
490 490
491 491 this.getLineNumber = function(node) {
492 492 var $node = $(node);
493 493 var lineNo = $node.closest('td').attr('data-line-no');
494 494 if (lineNo === undefined && $node.data('commentInline')){
495 495 lineNo = $node.data('commentLineNo')
496 496 }
497 497
498 498 return lineNo
499 499 };
500 500
501 501 this.scrollToComment = function(node, offset, outdated) {
502 502 if (offset === undefined) {
503 503 offset = 0;
504 504 }
505 505 var outdated = outdated || false;
506 506 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
507 507
508 508 if (!node) {
509 509 node = $('.comment-selected');
510 510 if (!node.length) {
511 511 node = $('comment-current')
512 512 }
513 513 }
514
514 515 $wrapper = $(node).closest('div.comment');
515 $comment = $(node).closest(klass);
516 $comments = $(klass);
517 516
518 517 // show hidden comment when referenced.
519 518 if (!$wrapper.is(':visible')){
520 519 $wrapper.show();
521 520 }
522 521
522 $comment = $(node).closest(klass);
523 $comments = $(klass);
524
523 525 $('.comment-selected').removeClass('comment-selected');
524 526
525 527 var nextIdx = $(klass).index($comment) + offset;
526 528 if (nextIdx >= $comments.length) {
527 529 nextIdx = 0;
528 530 }
529 531 var $next = $(klass).eq(nextIdx);
530 532
531 533 var $cb = $next.closest('.cb');
532 534 $cb.removeClass('cb-collapsed');
533 535
534 536 var $filediffCollapseState = $cb.closest('.filediff').prev();
535 537 $filediffCollapseState.prop('checked', false);
536 538 $next.addClass('comment-selected');
537 539 scrollToElement($next);
538 540 return false;
539 541 };
540 542
541 543 this.nextComment = function(node) {
542 544 return self.scrollToComment(node, 1);
543 545 };
544 546
545 547 this.prevComment = function(node) {
546 548 return self.scrollToComment(node, -1);
547 549 };
548 550
549 551 this.nextOutdatedComment = function(node) {
550 552 return self.scrollToComment(node, 1, true);
551 553 };
552 554
553 555 this.prevOutdatedComment = function(node) {
554 556 return self.scrollToComment(node, -1, true);
555 557 };
556 558
557 559 this.deleteComment = function(node) {
558 560 if (!confirm(_gettext('Delete this comment?'))) {
559 561 return false;
560 562 }
561 563 var $node = $(node);
562 564 var $td = $node.closest('td');
563 565 var $comment = $node.closest('.comment');
564 566 var comment_id = $comment.attr('data-comment-id');
565 567 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
566 568 var postData = {
567 569 'csrf_token': CSRF_TOKEN
568 570 };
569 571
570 572 $comment.addClass('comment-deleting');
571 573 $comment.hide('fast');
572 574
573 575 var success = function(response) {
574 576 $comment.remove();
575 577 return false;
576 578 };
577 579 var failure = function(data, textStatus, xhr) {
578 580 alert("error processing request: " + textStatus);
579 581 $comment.show('fast');
580 582 $comment.removeClass('comment-deleting');
581 583 return false;
582 584 };
583 585 ajaxPOST(url, postData, success, failure);
584 586 };
585 587
586 588 this.toggleWideMode = function (node) {
587 589 if ($('#content').hasClass('wrapper')) {
588 590 $('#content').removeClass("wrapper");
589 591 $('#content').addClass("wide-mode-wrapper");
590 592 $(node).addClass('btn-success');
591 593 return true
592 594 } else {
593 595 $('#content').removeClass("wide-mode-wrapper");
594 596 $('#content').addClass("wrapper");
595 597 $(node).removeClass('btn-success');
596 598 return false
597 599 }
598 600
599 601 };
600 602
601 603 this.toggleComments = function(node, show) {
602 604 var $filediff = $(node).closest('.filediff');
603 605 if (show === true) {
604 606 $filediff.removeClass('hide-comments');
605 607 } else if (show === false) {
606 608 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
607 609 $filediff.addClass('hide-comments');
608 610 } else {
609 611 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
610 612 $filediff.toggleClass('hide-comments');
611 613 }
612 614 return false;
613 615 };
614 616
615 617 this.toggleLineComments = function(node) {
616 618 self.toggleComments(node, true);
617 619 var $node = $(node);
618 620 // mark outdated comments as visible before the toggle;
619 621 $(node.closest('tr')).find('.comment-outdated').show();
620 622 $node.closest('tr').toggleClass('hide-line-comments');
621 623 };
622 624
623 625 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId){
624 626 var pullRequestId = templateContext.pull_request_data.pull_request_id;
625 627 var commitId = templateContext.commit_data.commit_id;
626 628
627 629 var commentForm = new CommentForm(
628 630 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId);
629 631 var cm = commentForm.getCmInstance();
630 632
631 633 if (resolvesCommentId){
632 var placeholderText = _gettext('Leave a comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
634 var placeholderText = _gettext('Leave a resolution comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
633 635 }
634 636
635 637 setTimeout(function() {
636 638 // callbacks
637 639 if (cm !== undefined) {
638 640 commentForm.setPlaceholder(placeholderText);
639 641 if (commentForm.isInline()) {
640 642 cm.focus();
641 643 cm.refresh();
642 644 }
643 645 }
644 646 }, 10);
645 647
646 648 // trigger scrolldown to the resolve comment, since it might be away
647 649 // from the clicked
648 650 if (resolvesCommentId){
649 651 var actionNode = $(commentForm.resolvesActionId).offset();
650 652
651 653 setTimeout(function() {
652 654 if (actionNode) {
653 655 $('body, html').animate({scrollTop: actionNode.top}, 10);
654 656 }
655 657 }, 100);
656 658 }
657 659
658 660 // add dropzone support
659 661 var insertAttachmentText = function (cm, attachmentName, attachmentStoreUrl, isRendered) {
660 662 var renderer = templateContext.visual.default_renderer;
661 663 if (renderer == 'rst') {
662 664 var attachmentUrl = '`#{0} <{1}>`_'.format(attachmentName, attachmentStoreUrl);
663 665 if (isRendered){
664 666 attachmentUrl = '\n.. image:: {0}'.format(attachmentStoreUrl);
665 667 }
666 668 } else if (renderer == 'markdown') {
667 669 var attachmentUrl = '[{0}]({1})'.format(attachmentName, attachmentStoreUrl);
668 670 if (isRendered){
669 671 attachmentUrl = '!' + attachmentUrl;
670 672 }
671 673 } else {
672 674 var attachmentUrl = '{}'.format(attachmentStoreUrl);
673 675 }
674 676 cm.replaceRange(attachmentUrl+'\n', CodeMirror.Pos(cm.lastLine()));
675 677
676 678 return false;
677 679 };
678 680
679 681 //see: https://www.dropzonejs.com/#configuration
680 682 var storeUrl = pyroutes.url('repo_commit_comment_attachment_upload',
681 683 {'repo_name': templateContext.repo_name,
682 684 'commit_id': templateContext.commit_data.commit_id})
683 685
684 686 var previewTmpl = $(formElement).find('.comment-attachment-uploader-template').get(0);
685 687 if (previewTmpl !== undefined){
686 688 var selectLink = $(formElement).find('.pick-attachment').get(0);
687 689 $(formElement).find('.comment-attachment-uploader').dropzone({
688 690 url: storeUrl,
689 691 headers: {"X-CSRF-Token": CSRF_TOKEN},
690 692 paramName: function () {
691 693 return "attachment"
692 694 }, // The name that will be used to transfer the file
693 695 clickable: selectLink,
694 696 parallelUploads: 1,
695 697 maxFiles: 10,
696 698 maxFilesize: templateContext.attachment_store.max_file_size_mb,
697 699 uploadMultiple: false,
698 700 autoProcessQueue: true, // if false queue will not be processed automatically.
699 701 createImageThumbnails: false,
700 702 previewTemplate: previewTmpl.innerHTML,
701 703
702 704 accept: function (file, done) {
703 705 done();
704 706 },
705 707 init: function () {
706 708
707 709 this.on("sending", function (file, xhr, formData) {
708 710 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').hide();
709 711 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').show();
710 712 });
711 713
712 714 this.on("success", function (file, response) {
713 715 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').show();
714 716 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
715 717
716 718 var isRendered = false;
717 719 var ext = file.name.split('.').pop();
718 720 var imageExts = templateContext.attachment_store.image_ext;
719 721 if (imageExts.indexOf(ext) !== -1){
720 722 isRendered = true;
721 723 }
722 724
723 725 insertAttachmentText(cm, file.name, response.repo_fqn_access_path, isRendered)
724 726 });
725 727
726 728 this.on("error", function (file, errorMessage, xhr) {
727 729 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
728 730
729 731 var error = null;
730 732
731 733 if (xhr !== undefined){
732 734 var httpStatus = xhr.status + " " + xhr.statusText;
733 735 if (xhr !== undefined && xhr.status >= 500) {
734 736 error = httpStatus;
735 737 }
736 738 }
737 739
738 740 if (error === null) {
739 741 error = errorMessage.error || errorMessage || httpStatus;
740 742 }
741 743 $(file.previewElement).find('.dz-error-message').html('ERROR: {0}'.format(error));
742 744
743 745 });
744 746 }
745 747 });
746 748 }
747 749 return commentForm;
748 750 };
749 751
750 752 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
751 753
752 754 var tmpl = $('#cb-comment-general-form-template').html();
753 755 tmpl = tmpl.format(null, 'general');
754 756 var $form = $(tmpl);
755 757
756 758 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
757 759 var curForm = $formPlaceholder.find('form');
758 760 if (curForm){
759 761 curForm.remove();
760 762 }
761 763 $formPlaceholder.append($form);
762 764
763 765 var _form = $($form[0]);
764 766 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
765 767 var commentForm = this.createCommentForm(
766 768 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId);
767 769 commentForm.initStatusChangeSelector();
768 770
769 771 return commentForm;
770 772 };
771 773
772 774 this.createComment = function(node, resolutionComment) {
773 775 var resolvesCommentId = resolutionComment || null;
774 776 var $node = $(node);
775 777 var $td = $node.closest('td');
776 778 var $form = $td.find('.comment-inline-form');
777 779
778 780 if (!$form.length) {
779 781
780 782 var $filediff = $node.closest('.filediff');
781 783 $filediff.removeClass('hide-comments');
782 784 var f_path = $filediff.attr('data-f-path');
783 785 var lineno = self.getLineNumber(node);
784 786 // create a new HTML from template
785 787 var tmpl = $('#cb-comment-inline-form-template').html();
786 788 tmpl = tmpl.format(escapeHtml(f_path), lineno);
787 789 $form = $(tmpl);
788 790
789 791 var $comments = $td.find('.inline-comments');
790 792 if (!$comments.length) {
791 793 $comments = $(
792 794 $('#cb-comments-inline-container-template').html());
793 795 $td.append($comments);
794 796 }
795 797
796 798 $td.find('.cb-comment-add-button').before($form);
797 799
798 800 var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno);
799 801 var _form = $($form[0]).find('form');
800 802 var autocompleteActions = ['as_note', 'as_todo'];
801 803 var commentForm = this.createCommentForm(
802 804 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId);
803 805
804 806 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
805 807 form: _form,
806 808 parent: $td[0],
807 809 lineno: lineno,
808 810 f_path: f_path}
809 811 );
810 812
811 813 // set a CUSTOM submit handler for inline comments.
812 814 commentForm.setHandleFormSubmit(function(o) {
813 815 var text = commentForm.cm.getValue();
814 816 var commentType = commentForm.getCommentType();
815 817 var resolvesCommentId = commentForm.getResolvesId();
816 818
817 819 if (text === "") {
818 820 return;
819 821 }
820 822
821 823 if (lineno === undefined) {
822 824 alert('missing line !');
823 825 return;
824 826 }
825 827 if (f_path === undefined) {
826 828 alert('missing file path !');
827 829 return;
828 830 }
829 831
830 832 var excludeCancelBtn = false;
831 833 var submitEvent = true;
832 834 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
833 835 commentForm.cm.setOption("readOnly", true);
834 836 var postData = {
835 837 'text': text,
836 838 'f_path': f_path,
837 839 'line': lineno,
838 840 'comment_type': commentType,
839 841 'csrf_token': CSRF_TOKEN
840 842 };
841 843 if (resolvesCommentId){
842 844 postData['resolves_comment_id'] = resolvesCommentId;
843 845 }
844 846
845 847 var submitSuccessCallback = function(json_data) {
846 848 $form.remove();
847 849 try {
848 850 var html = json_data.rendered_text;
849 851 var lineno = json_data.line_no;
850 852 var target_id = json_data.target_id;
851 853
852 854 $comments.find('.cb-comment-add-button').before(html);
853 855
854 856 //mark visually which comment was resolved
855 857 if (resolvesCommentId) {
856 858 commentForm.markCommentResolved(resolvesCommentId);
857 859 }
858 860
859 861 // run global callback on submit
860 862 commentForm.globalSubmitSuccessCallback();
861 863
862 864 } catch (e) {
863 865 console.error(e);
864 866 }
865 867
866 868 // re trigger the linkification of next/prev navigation
867 869 linkifyComments($('.inline-comment-injected'));
868 870 timeagoActivate();
869 871 tooltipActivate();
870 872
871 873 if (window.updateSticky !== undefined) {
872 874 // potentially our comments change the active window size, so we
873 875 // notify sticky elements
874 876 updateSticky()
875 877 }
876 878
877 879 commentForm.setActionButtonsDisabled(false);
878 880
879 881 };
880 882 var submitFailCallback = function(data){
881 883 alert(
882 884 "Error while submitting comment.\n" +
883 885 "Error code {0} ({1}).".format(data.status, data.statusText)
884 886 );
885 887 commentForm.resetCommentFormState(text)
886 888 };
887 889 commentForm.submitAjaxPOST(
888 890 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
889 891 });
890 892 }
891 893
892 894 $form.addClass('comment-inline-form-open');
893 895 };
894 896
895 897 this.createResolutionComment = function(commentId){
896 898 // hide the trigger text
897 899 $('#resolve-comment-{0}'.format(commentId)).hide();
898 900
899 901 var comment = $('#comment-'+commentId);
900 902 var commentData = comment.data();
901 903 if (commentData.commentInline) {
902 904 this.createComment(comment, commentId)
903 905 } else {
904 906 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
905 907 }
906 908
907 909 return false;
908 910 };
909 911
910 912 this.submitResolution = function(commentId){
911 913 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
912 914 var commentForm = form.get(0).CommentForm;
913 915
914 916 var cm = commentForm.getCmInstance();
915 917 var renderer = templateContext.visual.default_renderer;
916 918 if (renderer == 'rst'){
917 919 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
918 920 } else if (renderer == 'markdown') {
919 921 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
920 922 } else {
921 923 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
922 924 }
923 925
924 926 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
925 927 form.submit();
926 928 return false;
927 929 };
928 930
929 931 };
@@ -1,1139 +1,1140 b''
1 1 ## -*- coding: utf-8 -*-
2 2
3 3 <%!
4 4 ## base64 filter e.g ${ example | base64 }
5 5 def base64(text):
6 6 import base64
7 7 from rhodecode.lib.helpers import safe_str
8 8 return base64.encodestring(safe_str(text))
9 9 %>
10 10
11 11 <%inherit file="root.mako"/>
12 12
13 13 <%include file="/ejs_templates/templates.html"/>
14 14
15 15 <div class="outerwrapper">
16 16 <!-- HEADER -->
17 17 <div class="header">
18 18 <div id="header-inner" class="wrapper">
19 19 <div id="logo">
20 20 <div class="logo-wrapper">
21 21 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-60x60.png')}" alt="RhodeCode"/></a>
22 22 </div>
23 23 % if c.rhodecode_name:
24 24 <div class="branding">
25 25 <a href="${h.route_path('home')}">${h.branding(c.rhodecode_name)}</a>
26 26 </div>
27 27 % endif
28 28 </div>
29 29 <!-- MENU BAR NAV -->
30 30 ${self.menu_bar_nav()}
31 31 <!-- END MENU BAR NAV -->
32 32 </div>
33 33 </div>
34 34 ${self.menu_bar_subnav()}
35 35 <!-- END HEADER -->
36 36
37 37 <!-- CONTENT -->
38 38 <div id="content" class="wrapper">
39 39
40 40 <rhodecode-toast id="notifications"></rhodecode-toast>
41 41
42 42 <div class="main">
43 43 ${next.main()}
44 44 </div>
45 45 </div>
46 46 <!-- END CONTENT -->
47 47
48 48 </div>
49 49 <!-- FOOTER -->
50 50 <div id="footer">
51 51 <div id="footer-inner" class="title wrapper">
52 52 <div>
53 53 <p class="footer-link-right">
54 54 % if c.visual.show_version:
55 55 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
56 56 % endif
57 57 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
58 58 % if c.visual.rhodecode_support_url:
59 59 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
60 60 % endif
61 61 </p>
62 62 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
63 63 <p class="server-instance" style="display:${sid}">
64 64 ## display hidden instance ID if specially defined
65 65 % if c.rhodecode_instanceid:
66 66 ${_('RhodeCode instance id: {}').format(c.rhodecode_instanceid)}
67 67 % endif
68 68 </p>
69 69 </div>
70 70 </div>
71 71 </div>
72 72
73 73 <!-- END FOOTER -->
74 74
75 75 ### MAKO DEFS ###
76 76
77 77 <%def name="menu_bar_subnav()">
78 78 </%def>
79 79
80 80 <%def name="breadcrumbs(class_='breadcrumbs')">
81 81 <div class="${class_}">
82 82 ${self.breadcrumbs_links()}
83 83 </div>
84 84 </%def>
85 85
86 86 <%def name="admin_menu(active=None)">
87 87
88 88 <div id="context-bar">
89 89 <div class="wrapper">
90 90 <div class="title">
91 91 <div class="title-content">
92 92 <div class="title-main">
93 93 % if c.is_super_admin:
94 94 ${_('Super-admin Panel')}
95 95 % else:
96 96 ${_('Delegated Admin Panel')}
97 97 % endif
98 98 </div>
99 99 </div>
100 100 </div>
101 101
102 102 <ul id="context-pages" class="navigation horizontal-list">
103 103
104 104 ## super-admin case
105 105 % if c.is_super_admin:
106 106 <li class="${h.is_active('audit_logs', active)}"><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
107 107 <li class="${h.is_active('repositories', active)}"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
108 108 <li class="${h.is_active('repository_groups', active)}"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
109 109 <li class="${h.is_active('users', active)}"><a href="${h.route_path('users')}">${_('Users')}</a></li>
110 110 <li class="${h.is_active('user_groups', active)}"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
111 111 <li class="${h.is_active('permissions', active)}"><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
112 112 <li class="${h.is_active('authentication', active)}"><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
113 113 <li class="${h.is_active('integrations', active)}"><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
114 114 <li class="${h.is_active('defaults', active)}"><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
115 115 <li class="${h.is_active('settings', active)}"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li>
116 116
117 117 ## delegated admin
118 118 % elif c.is_delegated_admin:
119 119 <%
120 120 repositories=c.auth_user.repositories_admin or c.can_create_repo
121 121 repository_groups=c.auth_user.repository_groups_admin or c.can_create_repo_group
122 122 user_groups=c.auth_user.user_groups_admin or c.can_create_user_group
123 123 %>
124 124
125 125 %if repositories:
126 126 <li class="${h.is_active('repositories', active)} local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
127 127 %endif
128 128 %if repository_groups:
129 129 <li class="${h.is_active('repository_groups', active)} local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
130 130 %endif
131 131 %if user_groups:
132 132 <li class="${h.is_active('user_groups', active)} local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
133 133 %endif
134 134 % endif
135 135 </ul>
136 136
137 137 </div>
138 138 <div class="clear"></div>
139 139 </div>
140 140 </%def>
141 141
142 142 <%def name="dt_info_panel(elements)">
143 143 <dl class="dl-horizontal">
144 144 %for dt, dd, title, show_items in elements:
145 145 <dt>${dt}:</dt>
146 146 <dd title="${h.tooltip(title)}">
147 147 %if callable(dd):
148 148 ## allow lazy evaluation of elements
149 149 ${dd()}
150 150 %else:
151 151 ${dd}
152 152 %endif
153 153 %if show_items:
154 154 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
155 155 %endif
156 156 </dd>
157 157
158 158 %if show_items:
159 159 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
160 160 %for item in show_items:
161 161 <dt></dt>
162 162 <dd>${item}</dd>
163 163 %endfor
164 164 </div>
165 165 %endif
166 166
167 167 %endfor
168 168 </dl>
169 169 </%def>
170 170
171 171 <%def name="tr_info_entry(element)">
172 172 <% key, val, title, show_items = element %>
173 173
174 174 <tr>
175 175 <td style="vertical-align: top">${key}</td>
176 176 <td title="${h.tooltip(title)}">
177 177 %if callable(val):
178 178 ## allow lazy evaluation of elements
179 179 ${val()}
180 180 %else:
181 181 ${val}
182 182 %endif
183 183 %if show_items:
184 184 <div class="collapsable-content" data-toggle="item-${h.md5_safe(val)[:6]}-details" style="display: none">
185 185 % for item in show_items:
186 186 <dt></dt>
187 187 <dd>${item}</dd>
188 188 % endfor
189 189 </div>
190 190 %endif
191 191 </td>
192 192 <td style="vertical-align: top">
193 193 %if show_items:
194 194 <span class="btn-collapse" data-toggle="item-${h.md5_safe(val)[:6]}-details">${_('Show More')} </span>
195 195 %endif
196 196 </td>
197 197 </tr>
198 198
199 199 </%def>
200 200
201 <%def name="gravatar(email, size=16, tooltip=False, tooltip_alt=None, user=None)">
201 <%def name="gravatar(email, size=16, tooltip=False, tooltip_alt=None, user=None, extra_class=None)">
202 202 <%
203 203 if size > 16:
204 204 gravatar_class = ['gravatar','gravatar-large']
205 205 else:
206 206 gravatar_class = ['gravatar']
207 207
208 208 data_hovercard_url = ''
209 209 data_hovercard_alt = tooltip_alt.replace('<', '&lt;').replace('>', '&gt;') if tooltip_alt else ''
210 210
211 211 if tooltip:
212 212 gravatar_class += ['tooltip-hovercard']
213
213 if extra_class:
214 gravatar_class += extra_class
214 215 if tooltip and user:
215 216 if user.username == h.DEFAULT_USER:
216 217 gravatar_class.pop(-1)
217 218 else:
218 219 data_hovercard_url = request.route_path('hovercard_user', user_id=getattr(user, 'user_id', ''))
219 220 gravatar_class = ' '.join(gravatar_class)
220 221
221 222 %>
222 223 <%doc>
223 224 TODO: johbo: For now we serve double size images to make it smooth
224 225 for retina. This is how it worked until now. Should be replaced
225 226 with a better solution at some point.
226 227 </%doc>
227 228
228 229 <img class="${gravatar_class}" height="${size}" width="${size}" data-hovercard-url="${data_hovercard_url}" data-hovercard-alt="${data_hovercard_alt}" src="${h.gravatar_url(email, size * 2)}" />
229 230 </%def>
230 231
231 232
232 233 <%def name="gravatar_with_user(contact, size=16, show_disabled=False, tooltip=False)">
233 234 <%
234 235 email = h.email_or_none(contact)
235 236 rc_user = h.discover_user(contact)
236 237 %>
237 238
238 239 <div class="rc-user">
239 240 ${self.gravatar(email, size, tooltip=tooltip, tooltip_alt=contact, user=rc_user)}
240 241 <span class="${('user user-disabled' if show_disabled else 'user')}"> ${h.link_to_user(rc_user or contact)}</span>
241 242 </div>
242 243 </%def>
243 244
244 245
245 246 <%def name="user_group_icon(user_group=None, size=16, tooltip=False)">
246 247 <%
247 248 if (size > 16):
248 249 gravatar_class = 'icon-user-group-alt'
249 250 else:
250 251 gravatar_class = 'icon-user-group-alt'
251 252
252 253 if tooltip:
253 254 gravatar_class += ' tooltip-hovercard'
254 255
255 256 data_hovercard_url = request.route_path('hovercard_user_group', user_group_id=user_group.users_group_id)
256 257 %>
257 258 <%doc>
258 259 TODO: johbo: For now we serve double size images to make it smooth
259 260 for retina. This is how it worked until now. Should be replaced
260 261 with a better solution at some point.
261 262 </%doc>
262 263
263 264 <i style="font-size: ${size}px" class="${gravatar_class} x-icon-size-${size}" data-hovercard-url="${data_hovercard_url}"></i>
264 265 </%def>
265 266
266 267 <%def name="repo_page_title(repo_instance)">
267 268 <div class="title-content repo-title">
268 269
269 270 <div class="title-main">
270 271 ## SVN/HG/GIT icons
271 272 %if h.is_hg(repo_instance):
272 273 <i class="icon-hg"></i>
273 274 %endif
274 275 %if h.is_git(repo_instance):
275 276 <i class="icon-git"></i>
276 277 %endif
277 278 %if h.is_svn(repo_instance):
278 279 <i class="icon-svn"></i>
279 280 %endif
280 281
281 282 ## public/private
282 283 %if repo_instance.private:
283 284 <i class="icon-repo-private"></i>
284 285 %else:
285 286 <i class="icon-repo-public"></i>
286 287 %endif
287 288
288 289 ## repo name with group name
289 290 ${h.breadcrumb_repo_link(repo_instance)}
290 291
291 292 ## Context Actions
292 293 <div class="pull-right">
293 294 %if c.rhodecode_user.username != h.DEFAULT_USER:
294 295 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
295 296
296 297 <a href="#WatchRepo" onclick="toggleFollowingRepo(this, templateContext.repo_id); return false" title="${_('Watch this Repository and actions on it in your personalized journal')}" class="btn btn-sm ${('watching' if c.repository_is_user_following else '')}">
297 298 % if c.repository_is_user_following:
298 299 <i class="icon-eye-off"></i>${_('Unwatch')}
299 300 % else:
300 301 <i class="icon-eye"></i>${_('Watch')}
301 302 % endif
302 303
303 304 </a>
304 305 %else:
305 306 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid)}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
306 307 %endif
307 308 </div>
308 309
309 310 </div>
310 311
311 312 ## FORKED
312 313 %if repo_instance.fork:
313 314 <p class="discreet">
314 315 <i class="icon-code-fork"></i> ${_('Fork of')}
315 316 ${h.link_to_if(c.has_origin_repo_read_perm,repo_instance.fork.repo_name, h.route_path('repo_summary', repo_name=repo_instance.fork.repo_name))}
316 317 </p>
317 318 %endif
318 319
319 320 ## IMPORTED FROM REMOTE
320 321 %if repo_instance.clone_uri:
321 322 <p class="discreet">
322 323 <i class="icon-code-fork"></i> ${_('Clone from')}
323 324 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
324 325 </p>
325 326 %endif
326 327
327 328 ## LOCKING STATUS
328 329 %if repo_instance.locked[0]:
329 330 <p class="locking_locked discreet">
330 331 <i class="icon-repo-lock"></i>
331 332 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
332 333 </p>
333 334 %elif repo_instance.enable_locking:
334 335 <p class="locking_unlocked discreet">
335 336 <i class="icon-repo-unlock"></i>
336 337 ${_('Repository not locked. Pull repository to lock it.')}
337 338 </p>
338 339 %endif
339 340
340 341 </div>
341 342 </%def>
342 343
343 344 <%def name="repo_menu(active=None)">
344 345 <%
345 346 ## determine if we have "any" option available
346 347 can_lock = h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking
347 348 has_actions = can_lock
348 349
349 350 %>
350 351 % if c.rhodecode_db_repo.archived:
351 352 <div class="alert alert-warning text-center">
352 353 <strong>${_('This repository has been archived. It is now read-only.')}</strong>
353 354 </div>
354 355 % endif
355 356
356 357 <!--- REPO CONTEXT BAR -->
357 358 <div id="context-bar">
358 359 <div class="wrapper">
359 360
360 361 <div class="title">
361 362 ${self.repo_page_title(c.rhodecode_db_repo)}
362 363 </div>
363 364
364 365 <ul id="context-pages" class="navigation horizontal-list">
365 366 <li class="${h.is_active('summary', active)}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
366 367 <li class="${h.is_active('commits', active)}"><a class="menulink" href="${h.route_path('repo_commits', repo_name=c.repo_name)}"><div class="menulabel">${_('Commits')}</div></a></li>
367 368 <li class="${h.is_active('files', active)}"><a class="menulink" href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.rhodecode_db_repo.landing_rev[1], f_path='')}"><div class="menulabel">${_('Files')}</div></a></li>
368 369 <li class="${h.is_active('compare', active)}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
369 370
370 371 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
371 372 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
372 373 <li class="${h.is_active('showpullrequest', active)}">
373 374 <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${h.tooltip(_('Show Pull Requests for %s') % c.repo_name)}">
374 375 <div class="menulabel">
375 376 ${_('Pull Requests')} <span class="menulink-counter">${c.repository_pull_requests}</span>
376 377 </div>
377 378 </a>
378 379 </li>
379 380 %endif
380 381
381 382 <li class="${h.is_active('artifacts', active)}">
382 383 <a class="menulink" href="${h.route_path('repo_artifacts_list',repo_name=c.repo_name)}">
383 384 <div class="menulabel">
384 385 ${_('Artifacts')} <span class="menulink-counter">${c.repository_artifacts}</span>
385 386 </div>
386 387 </a>
387 388 </li>
388 389
389 390 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
390 391 <li class="${h.is_active('settings', active)}"><a class="menulink" href="${h.route_path('edit_repo',repo_name=c.repo_name)}"><div class="menulabel">${_('Repository Settings')}</div></a></li>
391 392 %endif
392 393
393 394 <li class="${h.is_active('options', active)}">
394 395 % if has_actions:
395 396 <a class="menulink dropdown">
396 397 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
397 398 </a>
398 399 <ul class="submenu">
399 400 %if can_lock:
400 401 %if c.rhodecode_db_repo.locked[0]:
401 402 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock Repository')}</a></li>
402 403 %else:
403 404 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock Repository')}</a></li>
404 405 %endif
405 406 %endif
406 407 </ul>
407 408 % else:
408 409 <a class="menulink disabled">
409 410 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
410 411 </a>
411 412 % endif
412 413 </li>
413 414
414 415 </ul>
415 416 </div>
416 417 <div class="clear"></div>
417 418 </div>
418 419
419 420 <!--- REPO END CONTEXT BAR -->
420 421
421 422 </%def>
422 423
423 424 <%def name="repo_group_page_title(repo_group_instance)">
424 425 <div class="title-content">
425 426 <div class="title-main">
426 427 ## Repository Group icon
427 428 <i class="icon-repo-group"></i>
428 429
429 430 ## repo name with group name
430 431 ${h.breadcrumb_repo_group_link(repo_group_instance)}
431 432 </div>
432 433
433 434 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
434 435 <div class="repo-group-desc discreet">
435 436 ${dt.repo_group_desc(repo_group_instance.description_safe, repo_group_instance.personal, c.visual.stylify_metatags)}
436 437 </div>
437 438
438 439 </div>
439 440 </%def>
440 441
441 442
442 443 <%def name="repo_group_menu(active=None)">
443 444 <%
444 445 gr_name = c.repo_group.group_name if c.repo_group else None
445 446 # create repositories with write permission on group is set to true
446 447 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
447 448
448 449 %>
449 450
450 451
451 452 <!--- REPO GROUP CONTEXT BAR -->
452 453 <div id="context-bar">
453 454 <div class="wrapper">
454 455 <div class="title">
455 456 ${self.repo_group_page_title(c.repo_group)}
456 457 </div>
457 458
458 459 <ul id="context-pages" class="navigation horizontal-list">
459 460 <li class="${h.is_active('home', active)}">
460 461 <a class="menulink" href="${h.route_path('repo_group_home', repo_group_name=c.repo_group.group_name)}"><div class="menulabel">${_('Group Home')}</div></a>
461 462 </li>
462 463 % if c.is_super_admin or group_admin:
463 464 <li class="${h.is_active('settings', active)}">
464 465 <a class="menulink" href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}"><div class="menulabel">${_('Group Settings')}</div></a>
465 466 </li>
466 467 % endif
467 468
468 469 </ul>
469 470 </div>
470 471 <div class="clear"></div>
471 472 </div>
472 473
473 474 <!--- REPO GROUP CONTEXT BAR -->
474 475
475 476 </%def>
476 477
477 478
478 479 <%def name="usermenu(active=False)">
479 480 <%
480 481 not_anonymous = c.rhodecode_user.username != h.DEFAULT_USER
481 482
482 483 gr_name = c.repo_group.group_name if (hasattr(c, 'repo_group') and c.repo_group) else None
483 484 # create repositories with write permission on group is set to true
484 485
485 486 can_fork = c.is_super_admin or h.HasPermissionAny('hg.fork.repository')()
486 487 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
487 488 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
488 489 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
489 490
490 491 can_create_repos = c.is_super_admin or c.can_create_repo
491 492 can_create_repo_groups = c.is_super_admin or c.can_create_repo_group
492 493
493 494 can_create_repos_in_group = c.is_super_admin or group_admin or (group_write and create_on_write)
494 495 can_create_repo_groups_in_group = c.is_super_admin or group_admin
495 496 %>
496 497
497 498 % if not_anonymous:
498 499 <%
499 500 default_target_group = dict()
500 501 if c.rhodecode_user.personal_repo_group:
501 502 default_target_group = dict(parent_group=c.rhodecode_user.personal_repo_group.group_id)
502 503 %>
503 504
504 505 ## create action
505 506 <li>
506 507 <a href="#create-actions" onclick="return false;" class="menulink childs">
507 508 <i class="tooltip icon-plus-circled" title="${_('Create')}"></i>
508 509 </a>
509 510
510 511 <div class="action-menu submenu">
511 512
512 513 <ol>
513 514 ## scope of within a repository
514 515 % if hasattr(c, 'rhodecode_db_repo') and c.rhodecode_db_repo:
515 516 <li class="submenu-title">${_('This Repository')}</li>
516 517 <li>
517 518 <a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a>
518 519 </li>
519 520 % if can_fork:
520 521 <li>
521 522 <a href="${h.route_path('repo_fork_new',repo_name=c.repo_name,_query=default_target_group)}">${_('Fork this repository')}</a>
522 523 </li>
523 524 % endif
524 525 % endif
525 526
526 527 ## scope of within repository groups
527 528 % if hasattr(c, 'repo_group') and c.repo_group and (can_create_repos_in_group or can_create_repo_groups_in_group):
528 529 <li class="submenu-title">${_('This Repository Group')}</li>
529 530
530 531 % if can_create_repos_in_group:
531 532 <li>
532 533 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}">${_('New Repository')}</a>
533 534 </li>
534 535 % endif
535 536
536 537 % if can_create_repo_groups_in_group:
537 538 <li>
538 539 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.repo_group.group_id))}">${_(u'New Repository Group')}</a>
539 540 </li>
540 541 % endif
541 542 % endif
542 543
543 544 ## personal group
544 545 % if c.rhodecode_user.personal_repo_group:
545 546 <li class="submenu-title">Personal Group</li>
546 547
547 548 <li>
548 549 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}" >${_('New Repository')} </a>
549 550 </li>
550 551
551 552 <li>
552 553 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}">${_('New Repository Group')} </a>
553 554 </li>
554 555 % endif
555 556
556 557 ## Global actions
557 558 <li class="submenu-title">RhodeCode</li>
558 559 % if can_create_repos:
559 560 <li>
560 561 <a href="${h.route_path('repo_new')}" >${_('New Repository')}</a>
561 562 </li>
562 563 % endif
563 564
564 565 % if can_create_repo_groups:
565 566 <li>
566 567 <a href="${h.route_path('repo_group_new')}" >${_(u'New Repository Group')}</a>
567 568 </li>
568 569 % endif
569 570
570 571 <li>
571 572 <a href="${h.route_path('gists_new')}">${_(u'New Gist')}</a>
572 573 </li>
573 574
574 575 </ol>
575 576
576 577 </div>
577 578 </li>
578 579
579 580 ## notifications
580 581 <li>
581 582 <a class="${('empty' if c.unread_notifications == 0 else '')}" href="${h.route_path('notifications_show_all')}">
582 583 ${c.unread_notifications}
583 584 </a>
584 585 </li>
585 586 % endif
586 587
587 588 ## USER MENU
588 589 <li id="quick_login_li" class="${'active' if active else ''}">
589 590 % if c.rhodecode_user.username == h.DEFAULT_USER:
590 591 <a id="quick_login_link" class="menulink childs" href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">
591 592 ${gravatar(c.rhodecode_user.email, 20)}
592 593 <span class="user">
593 594 <span>${_('Sign in')}</span>
594 595 </span>
595 596 </a>
596 597 % else:
597 598 ## logged in user
598 599 <a id="quick_login_link" class="menulink childs">
599 600 ${gravatar(c.rhodecode_user.email, 20)}
600 601 <span class="user">
601 602 <span class="menu_link_user">${c.rhodecode_user.username}</span>
602 603 <div class="show_more"></div>
603 604 </span>
604 605 </a>
605 606 ## subnav with menu for logged in user
606 607 <div class="user-menu submenu">
607 608 <div id="quick_login">
608 609 %if c.rhodecode_user.username != h.DEFAULT_USER:
609 610 <div class="">
610 611 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
611 612 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
612 613 <div class="email">${c.rhodecode_user.email}</div>
613 614 </div>
614 615 <div class="">
615 616 <ol class="links">
616 617 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
617 618 % if c.rhodecode_user.personal_repo_group:
618 619 <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
619 620 % endif
620 621 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
621 622
622 623 % if c.debug_style:
623 624 <li>
624 625 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
625 626 <div class="menulabel">${_('[Style]')}</div>
626 627 </a>
627 628 </li>
628 629 % endif
629 630
630 631 ## bookmark-items
631 632 <li class="bookmark-items">
632 633 ${_('Bookmarks')}
633 634 <div class="pull-right">
634 635 <a href="${h.route_path('my_account_bookmarks')}">
635 636
636 637 <i class="icon-cog"></i>
637 638 </a>
638 639 </div>
639 640 </li>
640 641 % if not c.bookmark_items:
641 642 <li>
642 643 <a href="${h.route_path('my_account_bookmarks')}">${_('No Bookmarks yet.')}</a>
643 644 </li>
644 645 % endif
645 646 % for item in c.bookmark_items:
646 647 <li>
647 648 % if item.repository:
648 649 <div>
649 650 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
650 651 <code>${item.position}</code>
651 652 % if item.repository.repo_type == 'hg':
652 653 <i class="icon-hg" title="${_('Repository')}" style="font-size: 16px"></i>
653 654 % elif item.repository.repo_type == 'git':
654 655 <i class="icon-git" title="${_('Repository')}" style="font-size: 16px"></i>
655 656 % elif item.repository.repo_type == 'svn':
656 657 <i class="icon-svn" title="${_('Repository')}" style="font-size: 16px"></i>
657 658 % endif
658 659 ${(item.title or h.shorter(item.repository.repo_name, 30))}
659 660 </a>
660 661 </div>
661 662 % elif item.repository_group:
662 663 <div>
663 664 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
664 665 <code>${item.position}</code>
665 666 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
666 667 ${(item.title or h.shorter(item.repository_group.group_name, 30))}
667 668 </a>
668 669 </div>
669 670 % else:
670 671 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
671 672 <code>${item.position}</code>
672 673 ${item.title}
673 674 </a>
674 675 % endif
675 676 </li>
676 677 % endfor
677 678
678 679 <li class="logout">
679 680 ${h.secure_form(h.route_path('logout'), request=request)}
680 681 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
681 682 ${h.end_form()}
682 683 </li>
683 684 </ol>
684 685 </div>
685 686 %endif
686 687 </div>
687 688 </div>
688 689
689 690 % endif
690 691 </li>
691 692 </%def>
692 693
693 694 <%def name="menu_items(active=None)">
694 695
695 696 <ul id="quick" class="main_nav navigation horizontal-list">
696 697 ## notice box for important system messages
697 698 <li style="display: none">
698 699 <a class="notice-box" href="#openNotice" onclick="return false">
699 700 <div class="menulabel-notice" >
700 701 0
701 702 </div>
702 703 </a>
703 704 </li>
704 705
705 706 ## Main filter
706 707 <li>
707 708 <div class="menulabel main_filter_box">
708 709 <div class="main_filter_input_box">
709 710 <ul class="searchItems">
710 711
711 712 <li class="searchTag searchTagIcon">
712 713 <i class="icon-search"></i>
713 714 </li>
714 715
715 716 % if c.template_context['search_context']['repo_id']:
716 717 <li class="searchTag searchTagFilter searchTagHidable" >
717 718 ##<a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">
718 719 <span class="tag">
719 720 This repo
720 721 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
721 722 </span>
722 723 ##</a>
723 724 </li>
724 725 % elif c.template_context['search_context']['repo_group_id']:
725 726 <li class="searchTag searchTagFilter searchTagHidable">
726 727 ##<a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">
727 728 <span class="tag">
728 729 This group
729 730 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
730 731 </span>
731 732 ##</a>
732 733 </li>
733 734 % endif
734 735
735 736 <li class="searchTagInput">
736 737 <input class="main_filter_input" id="main_filter" size="25" type="text" name="main_filter" placeholder="${_('search / go to...')}" value="" />
737 738 </li>
738 739 <li class="searchTag searchTagHelp">
739 740 <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a>
740 741 </li>
741 742 </ul>
742 743 </div>
743 744 </div>
744 745
745 746 <div id="main_filter_help" style="display: none">
746 747 - Use '/' key to quickly access this field.
747 748
748 749 - Enter a name of repository, or repository group for quick search.
749 750
750 751 - Prefix query to allow special search:
751 752
752 753 user:admin, to search for usernames, always global
753 754
754 755 user_group:devops, to search for user groups, always global
755 756
756 757 commit:efced4, to search for commits, scoped to repositories or groups
757 758
758 759 file:models.py, to search for file paths, scoped to repositories or groups
759 760
760 761 % if c.template_context['search_context']['repo_id']:
761 762 For advanced full text search visit: <a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">repository search</a>
762 763 % elif c.template_context['search_context']['repo_group_id']:
763 764 For advanced full text search visit: <a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">repository group search</a>
764 765 % else:
765 766 For advanced full text search visit: <a href="${h.route_path('search')}">global search</a>
766 767 % endif
767 768 </div>
768 769 </li>
769 770
770 771 ## ROOT MENU
771 772 <li class="${h.is_active('home', active)}">
772 773 <a class="menulink" title="${_('Home')}" href="${h.route_path('home')}">
773 774 <div class="menulabel">${_('Home')}</div>
774 775 </a>
775 776 </li>
776 777
777 778 %if c.rhodecode_user.username != h.DEFAULT_USER:
778 779 <li class="${h.is_active('journal', active)}">
779 780 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
780 781 <div class="menulabel">${_('Journal')}</div>
781 782 </a>
782 783 </li>
783 784 %else:
784 785 <li class="${h.is_active('journal', active)}">
785 786 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
786 787 <div class="menulabel">${_('Public journal')}</div>
787 788 </a>
788 789 </li>
789 790 %endif
790 791
791 792 <li class="${h.is_active('gists', active)}">
792 793 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
793 794 <div class="menulabel">${_('Gists')}</div>
794 795 </a>
795 796 </li>
796 797
797 798 % if c.is_super_admin or c.is_delegated_admin:
798 799 <li class="${h.is_active('admin', active)}">
799 800 <a class="menulink childs" title="${_('Admin settings')}" href="${h.route_path('admin_home')}">
800 801 <div class="menulabel">${_('Admin')} </div>
801 802 </a>
802 803 </li>
803 804 % endif
804 805
805 806 ## render extra user menu
806 807 ${usermenu(active=(active=='my_account'))}
807 808
808 809 </ul>
809 810
810 811 <script type="text/javascript">
811 812 var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True";
812 813
813 814 var formatRepoResult = function(result, container, query, escapeMarkup) {
814 815 return function(data, escapeMarkup) {
815 816 if (!data.repo_id){
816 817 return data.text; // optgroup text Repositories
817 818 }
818 819
819 820 var tmpl = '';
820 821 var repoType = data['repo_type'];
821 822 var repoName = data['text'];
822 823
823 824 if(data && data.type == 'repo'){
824 825 if(repoType === 'hg'){
825 826 tmpl += '<i class="icon-hg"></i> ';
826 827 }
827 828 else if(repoType === 'git'){
828 829 tmpl += '<i class="icon-git"></i> ';
829 830 }
830 831 else if(repoType === 'svn'){
831 832 tmpl += '<i class="icon-svn"></i> ';
832 833 }
833 834 if(data['private']){
834 835 tmpl += '<i class="icon-lock" ></i> ';
835 836 }
836 837 else if(visualShowPublicIcon){
837 838 tmpl += '<i class="icon-unlock-alt"></i> ';
838 839 }
839 840 }
840 841 tmpl += escapeMarkup(repoName);
841 842 return tmpl;
842 843
843 844 }(result, escapeMarkup);
844 845 };
845 846
846 847 var formatRepoGroupResult = function(result, container, query, escapeMarkup) {
847 848 return function(data, escapeMarkup) {
848 849 if (!data.repo_group_id){
849 850 return data.text; // optgroup text Repositories
850 851 }
851 852
852 853 var tmpl = '';
853 854 var repoGroupName = data['text'];
854 855
855 856 if(data){
856 857
857 858 tmpl += '<i class="icon-repo-group"></i> ';
858 859
859 860 }
860 861 tmpl += escapeMarkup(repoGroupName);
861 862 return tmpl;
862 863
863 864 }(result, escapeMarkup);
864 865 };
865 866
866 867 var escapeRegExChars = function (value) {
867 868 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
868 869 };
869 870
870 871 var getRepoIcon = function(repo_type) {
871 872 if (repo_type === 'hg') {
872 873 return '<i class="icon-hg"></i> ';
873 874 }
874 875 else if (repo_type === 'git') {
875 876 return '<i class="icon-git"></i> ';
876 877 }
877 878 else if (repo_type === 'svn') {
878 879 return '<i class="icon-svn"></i> ';
879 880 }
880 881 return ''
881 882 };
882 883
883 884 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
884 885
885 886 if (value.split(':').length === 2) {
886 887 value = value.split(':')[1]
887 888 }
888 889
889 890 var searchType = data['type'];
890 891 var searchSubType = data['subtype'];
891 892 var valueDisplay = data['value_display'];
892 893 var valueIcon = data['value_icon'];
893 894
894 895 var pattern = '(' + escapeRegExChars(value) + ')';
895 896
896 897 valueDisplay = Select2.util.escapeMarkup(valueDisplay);
897 898
898 899 // highlight match
899 900 if (searchType != 'text') {
900 901 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
901 902 }
902 903
903 904 var icon = '';
904 905
905 906 if (searchType === 'hint') {
906 907 icon += '<i class="icon-repo-group"></i> ';
907 908 }
908 909 // full text search/hints
909 910 else if (searchType === 'search') {
910 911 if (valueIcon === undefined) {
911 912 icon += '<i class="icon-more"></i> ';
912 913 } else {
913 914 icon += valueIcon + ' ';
914 915 }
915 916
916 917 if (searchSubType !== undefined && searchSubType == 'repo') {
917 918 valueDisplay += '<div class="pull-right tag">repository</div>';
918 919 }
919 920 else if (searchSubType !== undefined && searchSubType == 'repo_group') {
920 921 valueDisplay += '<div class="pull-right tag">repo group</div>';
921 922 }
922 923 }
923 924 // repository
924 925 else if (searchType === 'repo') {
925 926
926 927 var repoIcon = getRepoIcon(data['repo_type']);
927 928 icon += repoIcon;
928 929
929 930 if (data['private']) {
930 931 icon += '<i class="icon-lock" ></i> ';
931 932 }
932 933 else if (visualShowPublicIcon) {
933 934 icon += '<i class="icon-unlock-alt"></i> ';
934 935 }
935 936 }
936 937 // repository groups
937 938 else if (searchType === 'repo_group') {
938 939 icon += '<i class="icon-repo-group"></i> ';
939 940 }
940 941 // user group
941 942 else if (searchType === 'user_group') {
942 943 icon += '<i class="icon-group"></i> ';
943 944 }
944 945 // user
945 946 else if (searchType === 'user') {
946 947 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
947 948 }
948 949 // commit
949 950 else if (searchType === 'commit') {
950 951 var repo_data = data['repo_data'];
951 952 var repoIcon = getRepoIcon(repo_data['repository_type']);
952 953 if (repoIcon) {
953 954 icon += repoIcon;
954 955 } else {
955 956 icon += '<i class="icon-tag"></i>';
956 957 }
957 958 }
958 959 // file
959 960 else if (searchType === 'file') {
960 961 var repo_data = data['repo_data'];
961 962 var repoIcon = getRepoIcon(repo_data['repository_type']);
962 963 if (repoIcon) {
963 964 icon += repoIcon;
964 965 } else {
965 966 icon += '<i class="icon-tag"></i>';
966 967 }
967 968 }
968 969 // generic text
969 970 else if (searchType === 'text') {
970 971 icon = '';
971 972 }
972 973
973 974 var tmpl = '<div class="ac-container-wrap">{0}{1}</div>';
974 975 return tmpl.format(icon, valueDisplay);
975 976 };
976 977
977 978 var handleSelect = function(element, suggestion) {
978 979 if (suggestion.type === "hint") {
979 980 // we skip action
980 981 $('#main_filter').focus();
981 982 }
982 983 else if (suggestion.type === "text") {
983 984 // we skip action
984 985 $('#main_filter').focus();
985 986
986 987 } else {
987 988 window.location = suggestion['url'];
988 989 }
989 990 };
990 991
991 992 var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) {
992 993 if (queryLowerCase.split(':').length === 2) {
993 994 queryLowerCase = queryLowerCase.split(':')[1]
994 995 }
995 996 if (suggestion.type === "text") {
996 997 // special case we don't want to "skip" display for
997 998 return true
998 999 }
999 1000 return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1;
1000 1001 };
1001 1002
1002 1003 var cleanContext = {
1003 1004 repo_view_type: null,
1004 1005
1005 1006 repo_id: null,
1006 1007 repo_name: "",
1007 1008
1008 1009 repo_group_id: null,
1009 1010 repo_group_name: null
1010 1011 };
1011 1012 var removeGoToFilter = function () {
1012 1013 $('.searchTagHidable').hide();
1013 1014 $('#main_filter').autocomplete(
1014 1015 'setOptions', {params:{search_context: cleanContext}});
1015 1016 };
1016 1017
1017 1018 $('#main_filter').autocomplete({
1018 1019 serviceUrl: pyroutes.url('goto_switcher_data'),
1019 1020 params: {
1020 1021 "search_context": templateContext.search_context
1021 1022 },
1022 1023 minChars:2,
1023 1024 maxHeight:400,
1024 1025 deferRequestBy: 300, //miliseconds
1025 1026 tabDisabled: true,
1026 1027 autoSelectFirst: false,
1027 1028 containerClass: 'autocomplete-qfilter-suggestions',
1028 1029 formatResult: autocompleteMainFilterFormatResult,
1029 1030 lookupFilter: autocompleteMainFilterResult,
1030 1031 onSelect: function (element, suggestion) {
1031 1032 handleSelect(element, suggestion);
1032 1033 return false;
1033 1034 },
1034 1035 onSearchError: function (element, query, jqXHR, textStatus, errorThrown) {
1035 1036 if (jqXHR !== 'abort') {
1036 1037 alert("Error during search.\nError code: {0}".format(textStatus));
1037 1038 window.location = '';
1038 1039 }
1039 1040 },
1040 1041 onSearchStart: function (params) {
1041 1042 $('.searchTag.searchTagIcon').html('<i class="icon-spin animate-spin"></i>')
1042 1043 },
1043 1044 onSearchComplete: function (query, suggestions) {
1044 1045 $('.searchTag.searchTagIcon').html('<i class="icon-search"></i>')
1045 1046 },
1046 1047 });
1047 1048
1048 1049 showMainFilterBox = function () {
1049 1050 $('#main_filter_help').toggle();
1050 1051 };
1051 1052
1052 1053 $('#main_filter').on('keydown.autocomplete', function (e) {
1053 1054
1054 1055 var BACKSPACE = 8;
1055 1056 var el = $(e.currentTarget);
1056 1057 if(e.which === BACKSPACE){
1057 1058 var inputVal = el.val();
1058 1059 if (inputVal === ""){
1059 1060 removeGoToFilter()
1060 1061 }
1061 1062 }
1062 1063 });
1063 1064
1064 1065 </script>
1065 1066 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
1066 1067 </%def>
1067 1068
1068 1069 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
1069 1070 <div class="modal-dialog">
1070 1071 <div class="modal-content">
1071 1072 <div class="modal-header">
1072 1073 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1073 1074 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
1074 1075 </div>
1075 1076 <div class="modal-body">
1076 1077 <div class="block-left">
1077 1078 <table class="keyboard-mappings">
1078 1079 <tbody>
1079 1080 <tr>
1080 1081 <th></th>
1081 1082 <th>${_('Site-wide shortcuts')}</th>
1082 1083 </tr>
1083 1084 <%
1084 1085 elems = [
1085 1086 ('/', 'Use quick search box'),
1086 1087 ('g h', 'Goto home page'),
1087 1088 ('g g', 'Goto my private gists page'),
1088 1089 ('g G', 'Goto my public gists page'),
1089 1090 ('g 0-9', 'Goto bookmarked items from 0-9'),
1090 1091 ('n r', 'New repository page'),
1091 1092 ('n g', 'New gist page'),
1092 1093 ]
1093 1094 %>
1094 1095 %for key, desc in elems:
1095 1096 <tr>
1096 1097 <td class="keys">
1097 1098 <span class="key tag">${key}</span>
1098 1099 </td>
1099 1100 <td>${desc}</td>
1100 1101 </tr>
1101 1102 %endfor
1102 1103 </tbody>
1103 1104 </table>
1104 1105 </div>
1105 1106 <div class="block-left">
1106 1107 <table class="keyboard-mappings">
1107 1108 <tbody>
1108 1109 <tr>
1109 1110 <th></th>
1110 1111 <th>${_('Repositories')}</th>
1111 1112 </tr>
1112 1113 <%
1113 1114 elems = [
1114 1115 ('g s', 'Goto summary page'),
1115 1116 ('g c', 'Goto changelog page'),
1116 1117 ('g f', 'Goto files page'),
1117 1118 ('g F', 'Goto files page with file search activated'),
1118 1119 ('g p', 'Goto pull requests page'),
1119 1120 ('g o', 'Goto repository settings'),
1120 1121 ('g O', 'Goto repository access permissions settings'),
1121 1122 ]
1122 1123 %>
1123 1124 %for key, desc in elems:
1124 1125 <tr>
1125 1126 <td class="keys">
1126 1127 <span class="key tag">${key}</span>
1127 1128 </td>
1128 1129 <td>${desc}</td>
1129 1130 </tr>
1130 1131 %endfor
1131 1132 </tbody>
1132 1133 </table>
1133 1134 </div>
1134 1135 </div>
1135 1136 <div class="modal-footer">
1136 1137 </div>
1137 1138 </div><!-- /.modal-content -->
1138 1139 </div><!-- /.modal-dialog -->
1139 1140 </div><!-- /.modal -->
@@ -1,1178 +1,1181 b''
1 1 <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/>
2 2
3 3 <%def name="diff_line_anchor(commit, filename, line, type)"><%
4 4 return '%s_%s_%i' % (h.md5_safe(commit+filename), type, line)
5 5 %></%def>
6 6
7 7 <%def name="action_class(action)">
8 8 <%
9 9 return {
10 10 '-': 'cb-deletion',
11 11 '+': 'cb-addition',
12 12 ' ': 'cb-context',
13 13 }.get(action, 'cb-empty')
14 14 %>
15 15 </%def>
16 16
17 17 <%def name="op_class(op_id)">
18 18 <%
19 19 return {
20 20 DEL_FILENODE: 'deletion', # file deleted
21 21 BIN_FILENODE: 'warning' # binary diff hidden
22 22 }.get(op_id, 'addition')
23 23 %>
24 24 </%def>
25 25
26 26
27 27
28 28 <%def name="render_diffset(diffset, commit=None,
29 29
30 30 # collapse all file diff entries when there are more than this amount of files in the diff
31 31 collapse_when_files_over=20,
32 32
33 33 # collapse lines in the diff when more than this amount of lines changed in the file diff
34 34 lines_changed_limit=500,
35 35
36 36 # add a ruler at to the output
37 37 ruler_at_chars=0,
38 38
39 39 # show inline comments
40 40 use_comments=False,
41 41
42 42 # disable new comments
43 43 disable_new_comments=False,
44 44
45 45 # special file-comments that were deleted in previous versions
46 46 # it's used for showing outdated comments for deleted files in a PR
47 47 deleted_files_comments=None,
48 48
49 49 # for cache purpose
50 50 inline_comments=None,
51 51
52 52 # additional menu for PRs
53 pull_request_menu=None
53 pull_request_menu=None,
54
55 # show/hide todo next to comments
56 show_todos=True,
54 57
55 58 )">
56 59
57 60 <%
58 61 diffset_container_id = h.md5(diffset.target_ref)
59 62 collapse_all = len(diffset.files) > collapse_when_files_over
60 63 %>
61 64
62 65 %if use_comments:
63 66 <div id="cb-comments-inline-container-template" class="js-template">
64 67 ${inline_comments_container([], inline_comments)}
65 68 </div>
66 69 <div class="js-template" id="cb-comment-inline-form-template">
67 70 <div class="comment-inline-form ac">
68 71
69 72 %if c.rhodecode_user.username != h.DEFAULT_USER:
70 73 ## render template for inline comments
71 74 ${commentblock.comment_form(form_type='inline')}
72 75 %else:
73 76 ${h.form('', class_='inline-form comment-form-login', method='get')}
74 77 <div class="pull-left">
75 78 <div class="comment-help pull-right">
76 79 ${_('You need to be logged in to leave comments.')} <a href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">${_('Login now')}</a>
77 80 </div>
78 81 </div>
79 82 <div class="comment-button pull-right">
80 83 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
81 84 ${_('Cancel')}
82 85 </button>
83 86 </div>
84 87 <div class="clearfix"></div>
85 88 ${h.end_form()}
86 89 %endif
87 90 </div>
88 91 </div>
89 92
90 93 %endif
91 94
92 95 %if c.user_session_attrs["diffmode"] == 'sideside':
93 96 <style>
94 97 .wrapper {
95 98 max-width: 1600px !important;
96 99 }
97 100 </style>
98 101 %endif
99 102
100 103 %if ruler_at_chars:
101 104 <style>
102 105 .diff table.cb .cb-content:after {
103 106 content: "";
104 107 border-left: 1px solid blue;
105 108 position: absolute;
106 109 top: 0;
107 110 height: 18px;
108 111 opacity: .2;
109 112 z-index: 10;
110 113 //## +5 to account for diff action (+/-)
111 114 left: ${ruler_at_chars + 5}ch;
112 115 </style>
113 116 %endif
114 117
115 118 <div class="diffset ${disable_new_comments and 'diffset-comments-disabled'}">
116 119
117 120 <div style="height: 20px; line-height: 20px">
118 121 ## expand/collapse action
119 122 <div class="pull-left">
120 123 <a class="${'collapsed' if collapse_all else ''}" href="#expand-files" onclick="toggleExpand(this, '${diffset_container_id}'); return false">
121 124 % if collapse_all:
122 125 <i class="icon-plus-squared-alt icon-no-margin"></i>${_('Expand all files')}
123 126 % else:
124 127 <i class="icon-minus-squared-alt icon-no-margin"></i>${_('Collapse all files')}
125 128 % endif
126 129 </a>
127 130
128 131 </div>
129 132
130 133 ## todos
131 % if getattr(c, 'at_version', None):
134 % if show_todos and getattr(c, 'at_version', None):
132 135 <div class="pull-right">
133 136 <i class="icon-flag-filled" style="color: #949494">TODOs:</i>
134 137 ${_('not available in this view')}
135 138 </div>
136 % else:
139 % elif show_todos:
137 140 <div class="pull-right">
138 141 <div class="comments-number" style="padding-left: 10px">
139 142 % if hasattr(c, 'unresolved_comments') and hasattr(c, 'resolved_comments'):
140 143 <i class="icon-flag-filled" style="color: #949494">TODOs:</i>
141 144 % if c.unresolved_comments:
142 145 <a href="#show-todos" onclick="$('#todo-box').toggle(); return false">
143 146 ${_('{} unresolved').format(len(c.unresolved_comments))}
144 147 </a>
145 148 % else:
146 149 ${_('0 unresolved')}
147 150 % endif
148 151
149 152 ${_('{} Resolved').format(len(c.resolved_comments))}
150 153 % endif
151 154 </div>
152 155 </div>
153 156 % endif
154 157
155 158 ## comments
156 159 <div class="pull-right">
157 160 <div class="comments-number" style="padding-left: 10px">
158 161 % if hasattr(c, 'comments') and hasattr(c, 'inline_cnt'):
159 162 <i class="icon-comment" style="color: #949494">COMMENTS:</i>
160 163 % if c.comments:
161 164 <a href="#comments">${_ungettext("{} General", "{} General", len(c.comments)).format(len(c.comments))}</a>,
162 165 % else:
163 166 ${_('0 General')}
164 167 % endif
165 168
166 169 % if c.inline_cnt:
167 170 <a href="#" onclick="return Rhodecode.comments.nextComment();"
168 171 id="inline-comments-counter">${_ungettext("{} Inline", "{} Inline", c.inline_cnt).format(c.inline_cnt)}
169 172 </a>
170 173 % else:
171 174 ${_('0 Inline')}
172 175 % endif
173 176 % endif
174 177
175 178 % if pull_request_menu:
176 179 <%
177 180 outdated_comm_count_ver = pull_request_menu['outdated_comm_count_ver']
178 181 %>
179 182
180 183 % if outdated_comm_count_ver:
181 184 <a href="#" onclick="showOutdated(); Rhodecode.comments.nextOutdatedComment(); return false;">
182 185 (${_("{} Outdated").format(outdated_comm_count_ver)})
183 186 </a>
184 187 <a href="#" class="showOutdatedComments" onclick="showOutdated(this); return false;"> | ${_('show outdated')}</a>
185 188 <a href="#" class="hideOutdatedComments" style="display: none" onclick="hideOutdated(this); return false;"> | ${_('hide outdated')}</a>
186 189 % else:
187 190 (${_("{} Outdated").format(outdated_comm_count_ver)})
188 191 % endif
189 192
190 193 % endif
191 194
192 195 </div>
193 196 </div>
194 197
195 198 </div>
196 199
197 200 % if diffset.limited_diff:
198 201 <div class="diffset-heading ${(diffset.limited_diff and 'diffset-heading-warning' or '')}">
199 202 <h2 class="clearinner">
200 203 ${_('The requested changes are too big and content was truncated.')}
201 204 <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
202 205 </h2>
203 206 </div>
204 207 ## commit range header for each individual diff
205 208 % elif commit and hasattr(c, 'commit_ranges') and len(c.commit_ranges) > 1:
206 209 <div class="diffset-heading ${(diffset.limited_diff and 'diffset-heading-warning' or '')}">
207 210 <div class="clearinner">
208 211 <a class="tooltip revision" title="${h.tooltip(commit.message)}" href="${h.route_path('repo_commit',repo_name=diffset.repo_name,commit_id=commit.raw_id)}">${('r%s:%s' % (commit.idx,h.short_id(commit.raw_id)))}</a>
209 212 </div>
210 213 </div>
211 214 % endif
212 215
213 216 <div id="todo-box">
214 217 % if hasattr(c, 'unresolved_comments') and c.unresolved_comments:
215 218 % for co in c.unresolved_comments:
216 219 <a class="permalink" href="#comment-${co.comment_id}"
217 220 onclick="Rhodecode.comments.scrollToComment($('#comment-${co.comment_id}'))">
218 221 <i class="icon-flag-filled-red"></i>
219 222 ${co.comment_id}</a>${('' if loop.last else ',')}
220 223 % endfor
221 224 % endif
222 225 </div>
223 226 %if diffset.has_hidden_changes:
224 227 <p class="empty_data">${_('Some changes may be hidden')}</p>
225 228 %elif not diffset.files:
226 229 <p class="empty_data">${_('No files')}</p>
227 230 %endif
228 231
229 232 <div class="filediffs">
230 233
231 234 ## initial value could be marked as False later on
232 235 <% over_lines_changed_limit = False %>
233 236 %for i, filediff in enumerate(diffset.files):
234 237
235 238 <%
236 239 lines_changed = filediff.patch['stats']['added'] + filediff.patch['stats']['deleted']
237 240 over_lines_changed_limit = lines_changed > lines_changed_limit
238 241 %>
239 242 ## anchor with support of sticky header
240 243 <div class="anchor" id="a_${h.FID(filediff.raw_id, filediff.patch['filename'])}"></div>
241 244
242 245 <input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state collapse-${diffset_container_id}" id="filediff-collapse-${id(filediff)}" type="checkbox" onchange="updateSticky();">
243 246 <div
244 247 class="filediff"
245 248 data-f-path="${filediff.patch['filename']}"
246 249 data-anchor-id="${h.FID(filediff.raw_id, filediff.patch['filename'])}"
247 250 >
248 251 <label for="filediff-collapse-${id(filediff)}" class="filediff-heading">
249 252 <div class="filediff-collapse-indicator icon-"></div>
250 253 ${diff_ops(filediff)}
251 254 </label>
252 255
253 256 ${diff_menu(filediff, use_comments=use_comments)}
254 257 <table data-f-path="${filediff.patch['filename']}" data-anchor-id="${h.FID(filediff.raw_id, filediff.patch['filename'])}" class="code-visible-block cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${(over_lines_changed_limit and 'cb-collapsed' or '')}">
255 258
256 259 ## new/deleted/empty content case
257 260 % if not filediff.hunks:
258 261 ## Comment container, on "fakes" hunk that contains all data to render comments
259 262 ${render_hunk_lines(filediff, c.user_session_attrs["diffmode"], filediff.hunk_ops, use_comments=use_comments, inline_comments=inline_comments)}
260 263 % endif
261 264
262 265 %if filediff.limited_diff:
263 266 <tr class="cb-warning cb-collapser">
264 267 <td class="cb-text" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=6')}>
265 268 ${_('The requested commit or file is too big and content was truncated.')} <a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
266 269 </td>
267 270 </tr>
268 271 %else:
269 272 %if over_lines_changed_limit:
270 273 <tr class="cb-warning cb-collapser">
271 274 <td class="cb-text" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=6')}>
272 275 ${_('This diff has been collapsed as it changes many lines, (%i lines changed)' % lines_changed)}
273 276 <a href="#" class="cb-expand"
274 277 onclick="$(this).closest('table').removeClass('cb-collapsed'); updateSticky(); return false;">${_('Show them')}
275 278 </a>
276 279 <a href="#" class="cb-collapse"
277 280 onclick="$(this).closest('table').addClass('cb-collapsed'); updateSticky(); return false;">${_('Hide them')}
278 281 </a>
279 282 </td>
280 283 </tr>
281 284 %endif
282 285 %endif
283 286
284 287 % for hunk in filediff.hunks:
285 288 <tr class="cb-hunk">
286 289 <td ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=3' or '')}>
287 290 ## TODO: dan: add ajax loading of more context here
288 291 ## <a href="#">
289 292 <i class="icon-more"></i>
290 293 ## </a>
291 294 </td>
292 295 <td ${(c.user_session_attrs["diffmode"] == 'sideside' and 'colspan=5' or '')}>
293 296 @@
294 297 -${hunk.source_start},${hunk.source_length}
295 298 +${hunk.target_start},${hunk.target_length}
296 299 ${hunk.section_header}
297 300 </td>
298 301 </tr>
299 302 ${render_hunk_lines(filediff, c.user_session_attrs["diffmode"], hunk, use_comments=use_comments, inline_comments=inline_comments)}
300 303 % endfor
301 304
302 305 <% unmatched_comments = (inline_comments or {}).get(filediff.patch['filename'], {}) %>
303 306
304 307 ## outdated comments that do not fit into currently displayed lines
305 308 % for lineno, comments in unmatched_comments.items():
306 309
307 310 %if c.user_session_attrs["diffmode"] == 'unified':
308 311 % if loop.index == 0:
309 312 <tr class="cb-hunk">
310 313 <td colspan="3"></td>
311 314 <td>
312 315 <div>
313 ${_('Unmatched inline comments below')}
316 ${_('Unmatched/outdated inline comments below')}
314 317 </div>
315 318 </td>
316 319 </tr>
317 320 % endif
318 321 <tr class="cb-line">
319 322 <td class="cb-data cb-context"></td>
320 323 <td class="cb-lineno cb-context"></td>
321 324 <td class="cb-lineno cb-context"></td>
322 325 <td class="cb-content cb-context">
323 326 ${inline_comments_container(comments, inline_comments)}
324 327 </td>
325 328 </tr>
326 329 %elif c.user_session_attrs["diffmode"] == 'sideside':
327 330 % if loop.index == 0:
328 331 <tr class="cb-comment-info">
329 332 <td colspan="2"></td>
330 333 <td class="cb-line">
331 334 <div>
332 ${_('Unmatched inline comments below')}
335 ${_('Unmatched/outdated inline comments below')}
333 336 </div>
334 337 </td>
335 338 <td colspan="2"></td>
336 339 <td class="cb-line">
337 340 <div>
338 ${_('Unmatched comments below')}
341 ${_('Unmatched/outdated comments below')}
339 342 </div>
340 343 </td>
341 344 </tr>
342 345 % endif
343 346 <tr class="cb-line">
344 347 <td class="cb-data cb-context"></td>
345 348 <td class="cb-lineno cb-context"></td>
346 349 <td class="cb-content cb-context">
347 350 % if lineno.startswith('o'):
348 351 ${inline_comments_container(comments, inline_comments)}
349 352 % endif
350 353 </td>
351 354
352 355 <td class="cb-data cb-context"></td>
353 356 <td class="cb-lineno cb-context"></td>
354 357 <td class="cb-content cb-context">
355 358 % if lineno.startswith('n'):
356 359 ${inline_comments_container(comments, inline_comments)}
357 360 % endif
358 361 </td>
359 362 </tr>
360 363 %endif
361 364
362 365 % endfor
363 366
364 367 </table>
365 368 </div>
366 369 %endfor
367 370
368 371 ## outdated comments that are made for a file that has been deleted
369 372 % for filename, comments_dict in (deleted_files_comments or {}).items():
370 373
371 374 <%
372 375 display_state = 'display: none'
373 376 open_comments_in_file = [x for x in comments_dict['comments'] if x.outdated is False]
374 377 if open_comments_in_file:
375 378 display_state = ''
376 379 fid = str(id(filename))
377 380 %>
378 381 <div class="filediffs filediff-outdated" style="${display_state}">
379 382 <input ${(collapse_all and 'checked' or '')} class="filediff-collapse-state collapse-${diffset_container_id}" id="filediff-collapse-${id(filename)}" type="checkbox" onchange="updateSticky();">
380 383 <div class="filediff" data-f-path="${filename}" id="a_${h.FID(fid, filename)}">
381 384 <label for="filediff-collapse-${id(filename)}" class="filediff-heading">
382 385 <div class="filediff-collapse-indicator icon-"></div>
383 386
384 387 <span class="pill">
385 388 ## file was deleted
386 389 ${filename}
387 390 </span>
388 391 <span class="pill-group pull-left" >
389 392 ## file op, doesn't need translation
390 393 <span class="pill" op="removed">removed in this version</span>
391 394 </span>
392 395 <a class="pill filediff-anchor" href="#a_${h.FID(fid, filename)}">ΒΆ</a>
393 396 <span class="pill-group pull-right">
394 397 <span class="pill" op="deleted">-${comments_dict['stats']}</span>
395 398 </span>
396 399 </label>
397 400
398 401 <table class="cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${(over_lines_changed_limit and 'cb-collapsed' or '')}">
399 402 <tr>
400 403 % if c.user_session_attrs["diffmode"] == 'unified':
401 404 <td></td>
402 405 %endif
403 406
404 407 <td></td>
405 408 <td class="cb-text cb-${op_class(BIN_FILENODE)}" ${(c.user_session_attrs["diffmode"] == 'unified' and 'colspan=4' or 'colspan=5')}>
406 409 ${_('File was deleted in this version. There are still outdated/unresolved comments attached to it.')}
407 410 </td>
408 411 </tr>
409 412 %if c.user_session_attrs["diffmode"] == 'unified':
410 413 <tr class="cb-line">
411 414 <td class="cb-data cb-context"></td>
412 415 <td class="cb-lineno cb-context"></td>
413 416 <td class="cb-lineno cb-context"></td>
414 417 <td class="cb-content cb-context">
415 418 ${inline_comments_container(comments_dict['comments'], inline_comments)}
416 419 </td>
417 420 </tr>
418 421 %elif c.user_session_attrs["diffmode"] == 'sideside':
419 422 <tr class="cb-line">
420 423 <td class="cb-data cb-context"></td>
421 424 <td class="cb-lineno cb-context"></td>
422 425 <td class="cb-content cb-context"></td>
423 426
424 427 <td class="cb-data cb-context"></td>
425 428 <td class="cb-lineno cb-context"></td>
426 429 <td class="cb-content cb-context">
427 430 ${inline_comments_container(comments_dict['comments'], inline_comments)}
428 431 </td>
429 432 </tr>
430 433 %endif
431 434 </table>
432 435 </div>
433 436 </div>
434 437 % endfor
435 438
436 439 </div>
437 440 </div>
438 441 </%def>
439 442
440 443 <%def name="diff_ops(filediff)">
441 444 <%
442 445 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
443 446 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
444 447 %>
445 448 <span class="pill">
446 449 <i class="icon-file-text"></i>
447 450 %if filediff.source_file_path and filediff.target_file_path:
448 451 %if filediff.source_file_path != filediff.target_file_path:
449 452 ## file was renamed, or copied
450 453 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
451 454 ${filediff.target_file_path} β¬… <del>${filediff.source_file_path}</del>
452 455 <% final_path = filediff.target_file_path %>
453 456 %elif COPIED_FILENODE in filediff.patch['stats']['ops']:
454 457 ${filediff.target_file_path} β¬… ${filediff.source_file_path}
455 458 <% final_path = filediff.target_file_path %>
456 459 %endif
457 460 %else:
458 461 ## file was modified
459 462 ${filediff.source_file_path}
460 463 <% final_path = filediff.source_file_path %>
461 464 %endif
462 465 %else:
463 466 %if filediff.source_file_path:
464 467 ## file was deleted
465 468 ${filediff.source_file_path}
466 469 <% final_path = filediff.source_file_path %>
467 470 %else:
468 471 ## file was added
469 472 ${filediff.target_file_path}
470 473 <% final_path = filediff.target_file_path %>
471 474 %endif
472 475 %endif
473 476 <i style="color: #aaa" class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${final_path}" title="${_('Copy the full path')}" onclick="return false;"></i>
474 477 </span>
475 478 ## anchor link
476 479 <a class="pill filediff-anchor" href="#a_${h.FID(filediff.raw_id, filediff.patch['filename'])}">ΒΆ</a>
477 480
478 481 <span class="pill-group pull-right">
479 482
480 483 ## ops pills
481 484 %if filediff.limited_diff:
482 485 <span class="pill tooltip" op="limited" title="The stats for this diff are not complete">limited diff</span>
483 486 %endif
484 487
485 488 %if NEW_FILENODE in filediff.patch['stats']['ops']:
486 489 <span class="pill" op="created">created</span>
487 490 %if filediff['target_mode'].startswith('120'):
488 491 <span class="pill" op="symlink">symlink</span>
489 492 %else:
490 493 <span class="pill" op="mode">${nice_mode(filediff['target_mode'])}</span>
491 494 %endif
492 495 %endif
493 496
494 497 %if RENAMED_FILENODE in filediff.patch['stats']['ops']:
495 498 <span class="pill" op="renamed">renamed</span>
496 499 %endif
497 500
498 501 %if COPIED_FILENODE in filediff.patch['stats']['ops']:
499 502 <span class="pill" op="copied">copied</span>
500 503 %endif
501 504
502 505 %if DEL_FILENODE in filediff.patch['stats']['ops']:
503 506 <span class="pill" op="removed">removed</span>
504 507 %endif
505 508
506 509 %if CHMOD_FILENODE in filediff.patch['stats']['ops']:
507 510 <span class="pill" op="mode">
508 511 ${nice_mode(filediff['source_mode'])} ➑ ${nice_mode(filediff['target_mode'])}
509 512 </span>
510 513 %endif
511 514
512 515 %if BIN_FILENODE in filediff.patch['stats']['ops']:
513 516 <span class="pill" op="binary">binary</span>
514 517 %if MOD_FILENODE in filediff.patch['stats']['ops']:
515 518 <span class="pill" op="modified">modified</span>
516 519 %endif
517 520 %endif
518 521
519 522 <span class="pill" op="added">${('+' if filediff.patch['stats']['added'] else '')}${filediff.patch['stats']['added']}</span>
520 523 <span class="pill" op="deleted">${((h.safe_int(filediff.patch['stats']['deleted']) or 0) * -1)}</span>
521 524
522 525 </span>
523 526
524 527 </%def>
525 528
526 529 <%def name="nice_mode(filemode)">
527 530 ${(filemode.startswith('100') and filemode[3:] or filemode)}
528 531 </%def>
529 532
530 533 <%def name="diff_menu(filediff, use_comments=False)">
531 534 <div class="filediff-menu">
532 535
533 536 %if filediff.diffset.source_ref:
534 537
535 538 ## FILE BEFORE CHANGES
536 539 %if filediff.operation in ['D', 'M']:
537 540 <a
538 541 class="tooltip"
539 542 href="${h.route_path('repo_files',repo_name=filediff.diffset.target_repo_name,commit_id=filediff.diffset.source_ref,f_path=filediff.source_file_path)}"
540 543 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
541 544 >
542 545 ${_('Show file before')}
543 546 </a> |
544 547 %else:
545 548 <span
546 549 class="tooltip"
547 550 title="${h.tooltip(_('File not present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
548 551 >
549 552 ${_('Show file before')}
550 553 </span> |
551 554 %endif
552 555
553 556 ## FILE AFTER CHANGES
554 557 %if filediff.operation in ['A', 'M']:
555 558 <a
556 559 class="tooltip"
557 560 href="${h.route_path('repo_files',repo_name=filediff.diffset.source_repo_name,commit_id=filediff.diffset.target_ref,f_path=filediff.target_file_path)}"
558 561 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
559 562 >
560 563 ${_('Show file after')}
561 564 </a>
562 565 %else:
563 566 <span
564 567 class="tooltip"
565 568 title="${h.tooltip(_('File not present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
566 569 >
567 570 ${_('Show file after')}
568 571 </span>
569 572 %endif
570 573
571 574 % if use_comments:
572 575 |
573 576 <a href="#" onclick="return Rhodecode.comments.toggleComments(this);">
574 577 <span class="show-comment-button">${_('Show comments')}</span><span class="hide-comment-button">${_('Hide comments')}</span>
575 578 </a>
576 579 % endif
577 580
578 581 %endif
579 582
580 583 </div>
581 584 </%def>
582 585
583 586
584 587 <%def name="inline_comments_container(comments, inline_comments)">
585 588 <div class="inline-comments">
586 589 %for comment in comments:
587 590 ${commentblock.comment_block(comment, inline=True)}
588 591 %endfor
589 592 % if comments and comments[-1].outdated:
590 593 <span class="btn btn-secondary cb-comment-add-button comment-outdated}" style="display: none;}">
591 594 ${_('Add another comment')}
592 595 </span>
593 596 % else:
594 597 <span onclick="return Rhodecode.comments.createComment(this)" class="btn btn-secondary cb-comment-add-button">
595 598 ${_('Add another comment')}
596 599 </span>
597 600 % endif
598 601
599 602 </div>
600 603 </%def>
601 604
602 605 <%!
603 606 def get_comments_for(diff_type, comments, filename, line_version, line_number):
604 607 if hasattr(filename, 'unicode_path'):
605 608 filename = filename.unicode_path
606 609
607 610 if not isinstance(filename, (unicode, str)):
608 611 return None
609 612
610 613 line_key = '{}{}'.format(line_version, line_number) ## e.g o37, n12
611 614
612 615 if comments and filename in comments:
613 616 file_comments = comments[filename]
614 617 if line_key in file_comments:
615 618 data = file_comments.pop(line_key)
616 619 return data
617 620 %>
618 621
619 622 <%def name="render_hunk_lines_sideside(filediff, hunk, use_comments=False, inline_comments=None)">
620 623 %for i, line in enumerate(hunk.sideside):
621 624 <%
622 625 old_line_anchor, new_line_anchor = None, None
623 626
624 627 if line.original.lineno:
625 628 old_line_anchor = diff_line_anchor(filediff.raw_id, hunk.source_file_path, line.original.lineno, 'o')
626 629 if line.modified.lineno:
627 630 new_line_anchor = diff_line_anchor(filediff.raw_id, hunk.target_file_path, line.modified.lineno, 'n')
628 631 %>
629 632
630 633 <tr class="cb-line">
631 634 <td class="cb-data ${action_class(line.original.action)}"
632 635 data-line-no="${line.original.lineno}"
633 636 >
634 637 <div>
635 638
636 639 <% line_old_comments = None %>
637 640 %if line.original.get_comment_args:
638 641 <% line_old_comments = get_comments_for('side-by-side', inline_comments, *line.original.get_comment_args) %>
639 642 %endif
640 643 %if line_old_comments:
641 644 <% has_outdated = any([x.outdated for x in line_old_comments]) %>
642 645 % if has_outdated:
643 646 <i title="${_('comments including outdated')}:${len(line_old_comments)}" class="icon-comment-toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
644 647 % else:
645 648 <i title="${_('comments')}: ${len(line_old_comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
646 649 % endif
647 650 %endif
648 651 </div>
649 652 </td>
650 653 <td class="cb-lineno ${action_class(line.original.action)}"
651 654 data-line-no="${line.original.lineno}"
652 655 %if old_line_anchor:
653 656 id="${old_line_anchor}"
654 657 %endif
655 658 >
656 659 %if line.original.lineno:
657 660 <a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a>
658 661 %endif
659 662 </td>
660 663 <td class="cb-content ${action_class(line.original.action)}"
661 664 data-line-no="o${line.original.lineno}"
662 665 >
663 666 %if use_comments and line.original.lineno:
664 667 ${render_add_comment_button()}
665 668 %endif
666 669 <span class="cb-code"><span class="cb-action ${action_class(line.original.action)}"></span>${line.original.content or '' | n}</span>
667 670
668 671 %if use_comments and line.original.lineno and line_old_comments:
669 672 ${inline_comments_container(line_old_comments, inline_comments)}
670 673 %endif
671 674
672 675 </td>
673 676 <td class="cb-data ${action_class(line.modified.action)}"
674 677 data-line-no="${line.modified.lineno}"
675 678 >
676 679 <div>
677 680
678 681 %if line.modified.get_comment_args:
679 682 <% line_new_comments = get_comments_for('side-by-side', inline_comments, *line.modified.get_comment_args) %>
680 683 %else:
681 684 <% line_new_comments = None%>
682 685 %endif
683 686 %if line_new_comments:
684 687 <% has_outdated = any([x.outdated for x in line_new_comments]) %>
685 688 % if has_outdated:
686 689 <i title="${_('comments including outdated')}:${len(line_new_comments)}" class="icon-comment-toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
687 690 % else:
688 691 <i title="${_('comments')}: ${len(line_new_comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
689 692 % endif
690 693 %endif
691 694 </div>
692 695 </td>
693 696 <td class="cb-lineno ${action_class(line.modified.action)}"
694 697 data-line-no="${line.modified.lineno}"
695 698 %if new_line_anchor:
696 699 id="${new_line_anchor}"
697 700 %endif
698 701 >
699 702 %if line.modified.lineno:
700 703 <a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a>
701 704 %endif
702 705 </td>
703 706 <td class="cb-content ${action_class(line.modified.action)}"
704 707 data-line-no="n${line.modified.lineno}"
705 708 >
706 709 %if use_comments and line.modified.lineno:
707 710 ${render_add_comment_button()}
708 711 %endif
709 712 <span class="cb-code"><span class="cb-action ${action_class(line.modified.action)}"></span>${line.modified.content or '' | n}</span>
710 713 %if use_comments and line.modified.lineno and line_new_comments:
711 714 ${inline_comments_container(line_new_comments, inline_comments)}
712 715 %endif
713 716 </td>
714 717 </tr>
715 718 %endfor
716 719 </%def>
717 720
718 721
719 722 <%def name="render_hunk_lines_unified(filediff, hunk, use_comments=False, inline_comments=None)">
720 723 %for old_line_no, new_line_no, action, content, comments_args in hunk.unified:
721 724
722 725 <%
723 726 old_line_anchor, new_line_anchor = None, None
724 727 if old_line_no:
725 728 old_line_anchor = diff_line_anchor(filediff.raw_id, hunk.source_file_path, old_line_no, 'o')
726 729 if new_line_no:
727 730 new_line_anchor = diff_line_anchor(filediff.raw_id, hunk.target_file_path, new_line_no, 'n')
728 731 %>
729 732 <tr class="cb-line">
730 733 <td class="cb-data ${action_class(action)}">
731 734 <div>
732 735
733 736 %if comments_args:
734 737 <% comments = get_comments_for('unified', inline_comments, *comments_args) %>
735 738 %else:
736 739 <% comments = None %>
737 740 %endif
738 741
739 742 % if comments:
740 743 <% has_outdated = any([x.outdated for x in comments]) %>
741 744 % if has_outdated:
742 745 <i title="${_('comments including outdated')}:${len(comments)}" class="icon-comment-toggle" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
743 746 % else:
744 747 <i title="${_('comments')}: ${len(comments)}" class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
745 748 % endif
746 749 % endif
747 750 </div>
748 751 </td>
749 752 <td class="cb-lineno ${action_class(action)}"
750 753 data-line-no="${old_line_no}"
751 754 %if old_line_anchor:
752 755 id="${old_line_anchor}"
753 756 %endif
754 757 >
755 758 %if old_line_anchor:
756 759 <a name="${old_line_anchor}" href="#${old_line_anchor}">${old_line_no}</a>
757 760 %endif
758 761 </td>
759 762 <td class="cb-lineno ${action_class(action)}"
760 763 data-line-no="${new_line_no}"
761 764 %if new_line_anchor:
762 765 id="${new_line_anchor}"
763 766 %endif
764 767 >
765 768 %if new_line_anchor:
766 769 <a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a>
767 770 %endif
768 771 </td>
769 772 <td class="cb-content ${action_class(action)}"
770 773 data-line-no="${(new_line_no and 'n' or 'o')}${(new_line_no or old_line_no)}"
771 774 >
772 775 %if use_comments:
773 776 ${render_add_comment_button()}
774 777 %endif
775 778 <span class="cb-code"><span class="cb-action ${action_class(action)}"></span> ${content or '' | n}</span>
776 779 %if use_comments and comments:
777 780 ${inline_comments_container(comments, inline_comments)}
778 781 %endif
779 782 </td>
780 783 </tr>
781 784 %endfor
782 785 </%def>
783 786
784 787
785 788 <%def name="render_hunk_lines(filediff, diff_mode, hunk, use_comments, inline_comments)">
786 789 % if diff_mode == 'unified':
787 790 ${render_hunk_lines_unified(filediff, hunk, use_comments=use_comments, inline_comments=inline_comments)}
788 791 % elif diff_mode == 'sideside':
789 792 ${render_hunk_lines_sideside(filediff, hunk, use_comments=use_comments, inline_comments=inline_comments)}
790 793 % else:
791 794 <tr class="cb-line">
792 795 <td>unknown diff mode</td>
793 796 </tr>
794 797 % endif
795 798 </%def>file changes
796 799
797 800
798 801 <%def name="render_add_comment_button()">
799 802 <button class="btn btn-small btn-primary cb-comment-box-opener" onclick="return Rhodecode.comments.createComment(this)">
800 803 <span><i class="icon-comment"></i></span>
801 804 </button>
802 805 </%def>
803 806
804 807 <%def name="render_diffset_menu(diffset, range_diff_on=None)">
805 808 <% diffset_container_id = h.md5(diffset.target_ref) %>
806 809
807 810 <div id="diff-file-sticky" class="diffset-menu clearinner">
808 811 ## auto adjustable
809 812 <div class="sidebar__inner">
810 813 <div class="sidebar__bar">
811 814 <div class="pull-right">
812 815 <div class="btn-group">
813 816 <a class="btn tooltip toggle-wide-diff" href="#toggle-wide-diff" onclick="toggleWideDiff(this); return false" title="${h.tooltip(_('Toggle wide diff'))}">
814 817 <i class="icon-wide-mode"></i>
815 818 </a>
816 819 </div>
817 820 <div class="btn-group">
818 821
819 822 <a
820 823 class="btn ${(c.user_session_attrs["diffmode"] == 'sideside' and 'btn-active')} tooltip"
821 824 title="${h.tooltip(_('View diff as side by side'))}"
822 825 href="${h.current_route_path(request, diffmode='sideside')}">
823 826 <span>${_('Side by Side')}</span>
824 827 </a>
825 828
826 829 <a
827 830 class="btn ${(c.user_session_attrs["diffmode"] == 'unified' and 'btn-active')} tooltip"
828 831 title="${h.tooltip(_('View diff as unified'))}" href="${h.current_route_path(request, diffmode='unified')}">
829 832 <span>${_('Unified')}</span>
830 833 </a>
831 834
832 835 % if range_diff_on is True:
833 836 <a
834 837 title="${_('Turn off: Show the diff as commit range')}"
835 838 class="btn btn-primary"
836 839 href="${h.current_route_path(request, **{"range-diff":"0"})}">
837 840 <span>${_('Range Diff')}</span>
838 841 </a>
839 842 % elif range_diff_on is False:
840 843 <a
841 844 title="${_('Show the diff as commit range')}"
842 845 class="btn"
843 846 href="${h.current_route_path(request, **{"range-diff":"1"})}">
844 847 <span>${_('Range Diff')}</span>
845 848 </a>
846 849 % endif
847 850 </div>
848 851 <div class="btn-group">
849 852
850 853 <div class="pull-left">
851 854 ${h.hidden('diff_menu_{}'.format(diffset_container_id))}
852 855 </div>
853 856
854 857 </div>
855 858 </div>
856 859 <div class="pull-left">
857 860 <div class="btn-group">
858 861 <div class="pull-left">
859 862 ${h.hidden('file_filter_{}'.format(diffset_container_id))}
860 863 </div>
861 864
862 865 </div>
863 866 </div>
864 867 </div>
865 868 <div class="fpath-placeholder">
866 869 <i class="icon-file-text"></i>
867 870 <strong class="fpath-placeholder-text">
868 871 Context file:
869 872 </strong>
870 873 </div>
871 874 <div class="sidebar_inner_shadow"></div>
872 875 </div>
873 876 </div>
874 877
875 878 % if diffset:
876 879 %if diffset.limited_diff:
877 880 <% file_placeholder = _ungettext('%(num)s file changed', '%(num)s files changed', diffset.changed_files) % {'num': diffset.changed_files} %>
878 881 %else:
879 882 <% file_placeholder = h.literal(_ungettext('%(num)s file changed: <span class="op-added">%(linesadd)s inserted</span>, <span class="op-deleted">%(linesdel)s deleted</span>', '%(num)s files changed: <span class="op-added">%(linesadd)s inserted</span>, <span class="op-deleted">%(linesdel)s deleted</span>',
880 883 diffset.changed_files) % {'num': diffset.changed_files, 'linesadd': diffset.lines_added, 'linesdel': diffset.lines_deleted}) %>
881 884
882 885 %endif
883 886 ## case on range-diff placeholder needs to be updated
884 887 % if range_diff_on is True:
885 888 <% file_placeholder = _('Disabled on range diff') %>
886 889 % endif
887 890
888 891 <script type="text/javascript">
889 892 var feedFilesOptions = function (query, initialData) {
890 893 var data = {results: []};
891 894 var isQuery = typeof query.term !== 'undefined';
892 895
893 896 var section = _gettext('Changed files');
894 897 var filteredData = [];
895 898
896 899 //filter results
897 900 $.each(initialData.results, function (idx, value) {
898 901
899 902 if (!isQuery || query.term.length === 0 || value.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
900 903 filteredData.push({
901 904 'id': this.id,
902 905 'text': this.text,
903 906 "ops": this.ops,
904 907 })
905 908 }
906 909
907 910 });
908 911
909 912 data.results = filteredData;
910 913
911 914 query.callback(data);
912 915 };
913 916
914 917 var selectionFormatter = function(data, escapeMarkup) {
915 918 var container = '<div class="filelist" style="padding-right:100px">{0}</div>';
916 919 var tmpl = '<div><strong>{0}</strong></div>'.format(escapeMarkup(data['text']));
917 920 var pill = '<div class="pill-group" style="position: absolute; top:7px; right: 0">' +
918 921 '<span class="pill" op="added">{0}</span>' +
919 922 '<span class="pill" op="deleted">{1}</span>' +
920 923 '</div>'
921 924 ;
922 925 var added = data['ops']['added'];
923 926 if (added === 0) {
924 927 // don't show +0
925 928 added = 0;
926 929 } else {
927 930 added = '+' + added;
928 931 }
929 932
930 933 var deleted = -1*data['ops']['deleted'];
931 934
932 935 tmpl += pill.format(added, deleted);
933 936 return container.format(tmpl);
934 937 };
935 938 var formatFileResult = function(result, container, query, escapeMarkup) {
936 939 return selectionFormatter(result, escapeMarkup);
937 940 };
938 941
939 942 var formatSelection = function (data, container) {
940 943 return '${file_placeholder}'
941 944 };
942 945
943 946 if (window.preloadFileFilterData === undefined) {
944 947 window.preloadFileFilterData = {}
945 948 }
946 949
947 950 preloadFileFilterData["${diffset_container_id}"] = {
948 951 results: [
949 952 % for filediff in diffset.files:
950 953 {id:"a_${h.FID(filediff.raw_id, filediff.patch['filename'])}",
951 954 text:"${filediff.patch['filename']}",
952 955 ops:${h.json.dumps(filediff.patch['stats'])|n}}${('' if loop.last else ',')}
953 956 % endfor
954 957 ]
955 958 };
956 959
957 960 var diffFileFilterId = "#file_filter_" + "${diffset_container_id}";
958 961 var diffFileFilter = $(diffFileFilterId).select2({
959 962 'dropdownAutoWidth': true,
960 963 'width': 'auto',
961 964
962 965 containerCssClass: "drop-menu",
963 966 dropdownCssClass: "drop-menu-dropdown",
964 967 data: preloadFileFilterData["${diffset_container_id}"],
965 968 query: function(query) {
966 969 feedFilesOptions(query, preloadFileFilterData["${diffset_container_id}"]);
967 970 },
968 971 initSelection: function(element, callback) {
969 972 callback({'init': true});
970 973 },
971 974 formatResult: formatFileResult,
972 975 formatSelection: formatSelection
973 976 });
974 977
975 978 % if range_diff_on is True:
976 979 diffFileFilter.select2("enable", false);
977 980 % endif
978 981
979 982 $(diffFileFilterId).on('select2-selecting', function (e) {
980 983 var idSelector = e.choice.id;
981 984
982 985 // expand the container if we quick-select the field
983 986 $('#'+idSelector).next().prop('checked', false);
984 987 // hide the mast as we later do preventDefault()
985 988 $("#select2-drop-mask").click();
986 989
987 990 window.location.hash = '#'+idSelector;
988 991 updateSticky();
989 992
990 993 e.preventDefault();
991 994 });
992 995
993 996 </script>
994 997 % endif
995 998
996 999 <script type="text/javascript">
997 1000 $(document).ready(function () {
998 1001
999 1002 var contextPrefix = _gettext('Context file: ');
1000 1003 ## sticky sidebar
1001 1004 var sidebarElement = document.getElementById('diff-file-sticky');
1002 1005 sidebar = new StickySidebar(sidebarElement, {
1003 1006 topSpacing: 0,
1004 1007 bottomSpacing: 0,
1005 1008 innerWrapperSelector: '.sidebar__inner'
1006 1009 });
1007 1010 sidebarElement.addEventListener('affixed.static.stickySidebar', function () {
1008 1011 // reset our file so it's not holding new value
1009 1012 $('.fpath-placeholder-text').html(contextPrefix + ' - ')
1010 1013 });
1011 1014
1012 1015 updateSticky = function () {
1013 1016 sidebar.updateSticky();
1014 1017 Waypoint.refreshAll();
1015 1018 };
1016 1019
1017 1020 var animateText = function (fPath, anchorId) {
1018 1021 fPath = Select2.util.escapeMarkup(fPath);
1019 1022 $('.fpath-placeholder-text').html(contextPrefix + '<a href="#a_' + anchorId + '">' + fPath + '</a>')
1020 1023 };
1021 1024
1022 1025 ## dynamic file waypoints
1023 1026 var setFPathInfo = function(fPath, anchorId){
1024 1027 animateText(fPath, anchorId)
1025 1028 };
1026 1029
1027 1030 var codeBlock = $('.filediff');
1028 1031
1029 1032 // forward waypoint
1030 1033 codeBlock.waypoint(
1031 1034 function(direction) {
1032 1035 if (direction === "down"){
1033 1036 setFPathInfo($(this.element).data('fPath'), $(this.element).data('anchorId'))
1034 1037 }
1035 1038 }, {
1036 1039 offset: function () {
1037 1040 return 70;
1038 1041 },
1039 1042 context: '.fpath-placeholder'
1040 1043 }
1041 1044 );
1042 1045
1043 1046 // backward waypoint
1044 1047 codeBlock.waypoint(
1045 1048 function(direction) {
1046 1049 if (direction === "up"){
1047 1050 setFPathInfo($(this.element).data('fPath'), $(this.element).data('anchorId'))
1048 1051 }
1049 1052 }, {
1050 1053 offset: function () {
1051 1054 return -this.element.clientHeight + 90;
1052 1055 },
1053 1056 context: '.fpath-placeholder'
1054 1057 }
1055 1058 );
1056 1059
1057 1060 toggleWideDiff = function (el) {
1058 1061 updateSticky();
1059 1062 var wide = Rhodecode.comments.toggleWideMode(this);
1060 1063 storeUserSessionAttr('rc_user_session_attr.wide_diff_mode', wide);
1061 1064 if (wide === true) {
1062 1065 $(el).addClass('btn-active');
1063 1066 } else {
1064 1067 $(el).removeClass('btn-active');
1065 1068 }
1066 1069 return null;
1067 1070 };
1068 1071
1069 1072 var preloadDiffMenuData = {
1070 1073 results: [
1071 1074
1072 1075 ## Whitespace change
1073 1076 % if request.GET.get('ignorews', '') == '1':
1074 1077 {
1075 1078 id: 2,
1076 1079 text: _gettext('Show whitespace changes'),
1077 1080 action: function () {},
1078 1081 url: "${h.current_route_path(request, ignorews=0)|n}"
1079 1082 },
1080 1083 % else:
1081 1084 {
1082 1085 id: 2,
1083 1086 text: _gettext('Hide whitespace changes'),
1084 1087 action: function () {},
1085 1088 url: "${h.current_route_path(request, ignorews=1)|n}"
1086 1089 },
1087 1090 % endif
1088 1091
1089 1092 ## FULL CONTEXT
1090 1093 % if request.GET.get('fullcontext', '') == '1':
1091 1094 {
1092 1095 id: 3,
1093 1096 text: _gettext('Hide full context diff'),
1094 1097 action: function () {},
1095 1098 url: "${h.current_route_path(request, fullcontext=0)|n}"
1096 1099 },
1097 1100 % else:
1098 1101 {
1099 1102 id: 3,
1100 1103 text: _gettext('Show full context diff'),
1101 1104 action: function () {},
1102 1105 url: "${h.current_route_path(request, fullcontext=1)|n}"
1103 1106 },
1104 1107 % endif
1105 1108
1106 1109 ]
1107 1110 };
1108 1111
1109 1112 var diffMenuId = "#diff_menu_" + "${diffset_container_id}";
1110 1113 $(diffMenuId).select2({
1111 1114 minimumResultsForSearch: -1,
1112 1115 containerCssClass: "drop-menu-no-width",
1113 1116 dropdownCssClass: "drop-menu-dropdown",
1114 1117 dropdownAutoWidth: true,
1115 1118 data: preloadDiffMenuData,
1116 1119 placeholder: "${_('...')}",
1117 1120 });
1118 1121 $(diffMenuId).on('select2-selecting', function (e) {
1119 1122 e.choice.action();
1120 1123 if (e.choice.url !== null) {
1121 1124 window.location = e.choice.url
1122 1125 }
1123 1126 });
1124 1127 toggleExpand = function (el, diffsetEl) {
1125 1128 var el = $(el);
1126 1129 if (el.hasClass('collapsed')) {
1127 1130 $('.filediff-collapse-state.collapse-{0}'.format(diffsetEl)).prop('checked', false);
1128 1131 el.removeClass('collapsed');
1129 1132 el.html(
1130 1133 '<i class="icon-minus-squared-alt icon-no-margin"></i>' +
1131 1134 _gettext('Collapse all files'));
1132 1135 }
1133 1136 else {
1134 1137 $('.filediff-collapse-state.collapse-{0}'.format(diffsetEl)).prop('checked', true);
1135 1138 el.addClass('collapsed');
1136 1139 el.html(
1137 1140 '<i class="icon-plus-squared-alt icon-no-margin"></i>' +
1138 1141 _gettext('Expand all files'));
1139 1142 }
1140 1143 updateSticky()
1141 1144 };
1142 1145
1143 1146 toggleCommitExpand = function (el) {
1144 1147 var $el = $(el);
1145 1148 var commits = $el.data('toggleCommitsCnt');
1146 1149 var collapseMsg = _ngettext('Collapse {0} commit', 'Collapse {0} commits', commits).format(commits);
1147 1150 var expandMsg = _ngettext('Expand {0} commit', 'Expand {0} commits', commits).format(commits);
1148 1151
1149 1152 if ($el.hasClass('collapsed')) {
1150 1153 $('.compare_select').show();
1151 1154 $('.compare_select_hidden').hide();
1152 1155
1153 1156 $el.removeClass('collapsed');
1154 1157 $el.html(
1155 1158 '<i class="icon-minus-squared-alt icon-no-margin"></i>' +
1156 1159 collapseMsg);
1157 1160 }
1158 1161 else {
1159 1162 $('.compare_select').hide();
1160 1163 $('.compare_select_hidden').show();
1161 1164 $el.addClass('collapsed');
1162 1165 $el.html(
1163 1166 '<i class="icon-plus-squared-alt icon-no-margin"></i>' +
1164 1167 expandMsg);
1165 1168 }
1166 1169 updateSticky();
1167 1170 };
1168 1171
1169 1172 // get stored diff mode and pre-enable it
1170 1173 if (templateContext.session_attrs.wide_diff_mode === "true") {
1171 1174 Rhodecode.comments.toggleWideMode(null);
1172 1175 $('.toggle-wide-diff').addClass('btn-active');
1173 1176 updateSticky();
1174 1177 }
1175 1178 });
1176 1179 </script>
1177 1180
1178 1181 </%def>
@@ -1,842 +1,907 b''
1 1 <%inherit file="/base/base.mako"/>
2 2 <%namespace name="base" file="/base/base.mako"/>
3 3 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
4 4
5 5 <%def name="title()">
6 6 ${_('{} Pull Request !{}').format(c.repo_name, c.pull_request.pull_request_id)}
7 7 %if c.rhodecode_name:
8 8 &middot; ${h.branding(c.rhodecode_name)}
9 9 %endif
10 10 </%def>
11 11
12 12 <%def name="breadcrumbs_links()">
13 13
14 14 <div id="pr-title">
15 15 % if c.pull_request.is_closed():
16 16 <span class="pr-title-closed-tag tag">${_('Closed')}</span>
17 17 % endif
18 18 <input class="pr-title-input large disabled" disabled="disabled" name="pullrequest_title" type="text" value="${c.pull_request.title}">
19 19 </div>
20 20 <div id="pr-title-edit" class="input" style="display: none;">
21 21 <input class="pr-title-input large" id="pr-title-input" name="pullrequest_title" type="text" value="${c.pull_request.title}">
22 22 </div>
23 23 </%def>
24 24
25 25 <%def name="menu_bar_nav()">
26 26 ${self.menu_items(active='repositories')}
27 27 </%def>
28 28
29 29 <%def name="menu_bar_subnav()">
30 30 ${self.repo_menu(active='showpullrequest')}
31 31 </%def>
32 32
33 33 <%def name="main()">
34 34
35 35 <script type="text/javascript">
36 36 // TODO: marcink switch this to pyroutes
37 37 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__')}";
38 38 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
39 39 </script>
40 40
41 41 <div class="box">
42 42
43 43 ${self.breadcrumbs()}
44 44
45 45 <div class="box pr-summary">
46 46
47 47 <div class="summary-details block-left">
48 48 <% summary = lambda n:{False:'summary-short'}.get(n) %>
49 49 <div class="pr-details-title">
50 50 <div class="pull-left">
51 51 <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>
52 52 ${_('Created on')}
53 53 <span class="tooltip" title="${_('Last updated on')} ${h.format_date(c.pull_request.updated_on)}">${h.format_date(c.pull_request.created_on)},</span>
54 54 <span class="pr-details-title-author-pref">${_('by')}</span>
55 55 </div>
56 56
57 57 <div class="pull-left">
58 58 ${self.gravatar_with_user(c.pull_request.author.email, 16, tooltip=True)}
59 59 </div>
60 60
61 61 %if c.allowed_to_update:
62 <div id="delete_pullrequest" class="pull-right action_button ${('' if c.allowed_to_delete else 'disabled' )}" >
63 % if c.allowed_to_delete:
64 ${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)}
65 ${h.submit('remove_%s' % c.pull_request.pull_request_id, _('Delete'),
66 class_="btn btn-link btn-danger no-margin",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")}
67 ${h.end_form()}
68 % else:
69 ${_('Delete')}
70 % endif
62 <div class="pull-right">
63 <div id="edit_pull_request" class="action_button pr-save" style="display: none;">${_('Update title & description')}</div>
64 <div id="delete_pullrequest" class="action_button pr-save ${('' if c.allowed_to_delete else 'disabled' )}" style="display: none;">
65 % if c.allowed_to_delete:
66 ${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)}
67 ${h.submit('remove_%s' % c.pull_request.pull_request_id, _('Delete pull request'),
68 class_="btn btn-link btn-danger no-margin",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")}
69 ${h.end_form()}
70 % else:
71 <span class="tooltip" title="${_('Not allowed to delete this pull request')}">${_('Delete pull request')}</span>
72 % endif
73 </div>
74 <div id="open_edit_pullrequest" class="action_button">${_('Edit')}</div>
75 <div id="close_edit_pullrequest" class="action_button" style="display: none;">${_('Cancel')}</div>
71 76 </div>
72 <div id="open_edit_pullrequest" class="pull-right action_button">${_('Edit')}</div>
73 <div id="close_edit_pullrequest" class="pull-right action_button" style="display: none;">${_('Cancel')}</div>
74 <div id="edit_pull_request" class="pull-right action_button pr-save" style="display: none;">${_('Save Changes')}</div>
77
75 78 %endif
76 79 </div>
77 80
78 81 <div id="pr-desc" class="input" title="${_('Rendered using {} renderer').format(c.renderer)}">
79 82 ${h.render(c.pull_request.description, renderer=c.renderer, repo_name=c.repo_name)}
80 83 </div>
81 84
82 85 <div id="pr-desc-edit" class="input textarea" style="display: none;">
83 86 <input id="pr-renderer-input" type="hidden" name="description_renderer" value="${c.visual.default_renderer}">
84 87 ${dt.markup_form('pr-description-input', form_text=c.pull_request.description)}
85 88 </div>
86 89
87 90 <div id="summary" class="fields pr-details-content">
88 91
89 92 ## review
90 93 <div class="field">
91 94 <div class="label-pr-detail">
92 95 <label>${_('Review status')}:</label>
93 96 </div>
94 97 <div class="input">
95 98 %if c.pull_request_review_status:
96 99 <div class="tag status-tag-${c.pull_request_review_status}">
97 100 <i class="icon-circle review-status-${c.pull_request_review_status}"></i>
98 101 <span class="changeset-status-lbl">
99 102 %if c.pull_request.is_closed():
100 103 ${_('Closed')},
101 104 %endif
102 105
103 106 ${h.commit_status_lbl(c.pull_request_review_status)}
104 107
105 108 </span>
106 109 </div>
107 110 - ${_ungettext('calculated based on {} reviewer vote', 'calculated based on {} reviewers votes', len(c.pull_request_reviewers)).format(len(c.pull_request_reviewers))}
108 111 %endif
109 112 </div>
110 113 </div>
111 114
112 115 ## source
113 116 <div class="field">
114 117 <div class="label-pr-detail">
115 118 <label>${_('Commit flow')}:</label>
116 119 </div>
117 120 <div class="input">
118 121 <div class="pr-commit-flow">
119 122 ## Source
120 123 %if c.pull_request.source_ref_parts.type == 'branch':
121 124 <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>
122 125 %else:
123 126 <code class="pr-source-info">${'{}:{}'.format(c.pull_request.source_ref_parts.type, c.pull_request.source_ref_parts.name)}</code>
124 127 %endif
125 128 ${_('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>
126 129 &rarr;
127 130 ## Target
128 131 %if c.pull_request.target_ref_parts.type == 'branch':
129 132 <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>
130 133 %else:
131 134 <code class="pr-target-info">${'{}:{}'.format(c.pull_request.target_ref_parts.type, c.pull_request.target_ref_parts.name)}</code>
132 135 %endif
133 136
134 137 ${_('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>
135 138
136 139 <a class="source-details-action" href="#expand-source-details" onclick="return versionController.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>'>
137 140 <i class="icon-angle-down">more details</i>
138 141 </a>
139 142
140 143 </div>
141 144
142 145 <div class="source-details" style="display: none">
143 146
144 147 <ul>
145 148
146 149 ## common ancestor
147 150 <li>
148 151 ${_('Common ancestor')}:
149 152 % if c.ancestor_commit:
150 153 <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>
151 154 % else:
152 155 ${_('not available')}
153 156 % endif
154 157 </li>
155 158
156 159 ## pull url
157 160 <li>
158 161 %if h.is_hg(c.pull_request.source_repo):
159 162 <% clone_url = 'hg pull -r {} {}'.format(h.short_id(c.source_ref), c.pull_request.source_repo.clone_url()) %>
160 163 %elif h.is_git(c.pull_request.source_repo):
161 164 <% clone_url = 'git pull {} {}'.format(c.pull_request.source_repo.clone_url(), c.pull_request.source_ref_parts.name) %>
162 165 %endif
163 166
164 167 <span>${_('Pull changes from source')}</span>: <input type="text" class="input-monospace pr-pullinfo" value="${clone_url}" readonly="readonly">
165 168 <i class="tooltip icon-clipboard clipboard-action pull-right pr-pullinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the pull url')}"></i>
166 169 </li>
167 170
168 171 ## Shadow repo
169 172 <li>
170 173 % if not c.pull_request.is_closed() and c.pull_request.shadow_merge_ref:
171 174 %if h.is_hg(c.pull_request.target_repo):
172 175 <% 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) %>
173 176 %elif h.is_git(c.pull_request.target_repo):
174 177 <% 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) %>
175 178 %endif
176 179
177 180 <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">
178 181 <i class="tooltip icon-clipboard clipboard-action pull-right pr-mergeinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the clone url')}"></i>
179 182
180 183 % else:
181 184 <div class="">
182 185 ${_('Shadow repository data not available')}.
183 186 </div>
184 187 % endif
185 188 </li>
186 189
187 190 </ul>
188 191
189 192 </div>
190 193
191 194 </div>
192 195
193 196 </div>
194 197
195 198 ## versions
196 199 <div class="field">
197 200 <div class="label-pr-detail">
198 201 <label>${_('Versions')}:</label>
199 202 </div>
200 203
201 204 <% outdated_comm_count_ver = len(c.inline_versions[None]['outdated']) %>
202 205 <% general_outdated_comm_count_ver = len(c.comment_versions[None]['outdated']) %>
203 206
204 207 <div class="pr-versions">
205 208 % if c.show_version_changes:
206 209 <% outdated_comm_count_ver = len(c.inline_versions[c.at_version_num]['outdated']) %>
207 210 <% general_outdated_comm_count_ver = len(c.comment_versions[c.at_version_num]['outdated']) %>
208 211 ${_ungettext('{} version available for this pull request, ', '{} versions available for this pull request, ', len(c.versions)).format(len(c.versions))}
209 212 <a id="show-pr-versions" onclick="return versionController.toggleVersionView(this)" href="#show-pr-versions"
210 213 data-toggle-on="${_('show versions')}."
211 214 data-toggle-off="${_('hide versions')}.">
212 215 ${_('show versions')}.
213 216 </a>
214 217 <table>
215 218 ## SHOW ALL VERSIONS OF PR
216 219 <% ver_pr = None %>
217 220
218 221 % for data in reversed(list(enumerate(c.versions, 1))):
219 222 <% ver_pos = data[0] %>
220 223 <% ver = data[1] %>
221 224 <% ver_pr = ver.pull_request_version_id %>
222 225 <% display_row = '' if c.at_version and (c.at_version_num == ver_pr or c.from_version_num == ver_pr) else 'none' %>
223 226
224 227 <tr class="version-pr" style="display: ${display_row}">
225 228 <td>
226 229 <code>
227 230 <a href="${request.current_route_path(_query=dict(version=ver_pr or 'latest'))}">v${ver_pos}</a>
228 231 </code>
229 232 </td>
230 233 <td>
231 234 <input ${('checked="checked"' if c.from_version_num == ver_pr else '')} class="compare-radio-button" type="radio" name="ver_source" value="${ver_pr or 'latest'}" data-ver-pos="${ver_pos}"/>
232 235 <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}"/>
233 236 </td>
234 237 <td>
235 238 <% review_status = c.review_versions[ver_pr].status if ver_pr in c.review_versions else 'not_reviewed' %>
236 239 <i class="tooltip icon-circle review-status-${review_status}" title="${_('Your review status at this version')}"></i>
237 240
238 241 </td>
239 242 <td>
240 243 % if c.at_version_num != ver_pr:
241 244 <i class="tooltip icon-comment" title="${_('Comments from pull request version v{0}').format(ver_pos)}"></i>
242 245 <code>
243 246 General:${len(c.comment_versions[ver_pr]['at'])} / Inline:${len(c.inline_versions[ver_pr]['at'])}
244 247 </code>
245 248 % endif
246 249 </td>
247 250 <td>
248 251 ##<code>${ver.source_ref_parts.commit_id[:6]}</code>
249 252 </td>
250 253 <td>
251 254 <code>${h.age_component(ver.updated_on, time_is_local=True, tooltip=False)}</code>
252 255 </td>
253 256 </tr>
254 257 % endfor
255 258
256 259 <tr>
257 260 <td colspan="6">
258 261 <button id="show-version-diff" onclick="return versionController.showVersionDiff()" class="btn btn-sm" style="display: none"
259 262 data-label-text-locked="${_('select versions to show changes')}"
260 263 data-label-text-diff="${_('show changes between versions')}"
261 264 data-label-text-show="${_('show pull request for this version')}"
262 265 >
263 266 ${_('select versions to show changes')}
264 267 </button>
265 268 </td>
266 269 </tr>
267 270 </table>
268 271 % else:
269 <div class="input">
272 <div>
270 273 ${_('Pull request versions not available')}.
271 274 </div>
272 275 % endif
273 276 </div>
274 277 </div>
275 278
276 279 </div>
277 280
278 281 </div>
279 282
280 283 ## REVIEW RULES
281 284 <div id="review_rules" style="display: none" class="reviewers-title block-right">
282 285 <div class="pr-details-title">
283 286 ${_('Reviewer rules')}
284 287 %if c.allowed_to_update:
285 288 <span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
286 289 %endif
287 290 </div>
288 291 <div class="pr-reviewer-rules">
289 292 ## review rules will be appended here, by default reviewers logic
290 293 </div>
291 294 <input id="review_data" type="hidden" name="review_data" value="">
292 295 </div>
293 296
294 297 ## REVIEWERS
295 298 <div class="reviewers-title block-right">
296 299 <div class="pr-details-title">
297 300 ${_('Pull request reviewers')}
298 301 %if c.allowed_to_update:
299 302 <span id="open_edit_reviewers" class="block-right action_button last-item">${_('Edit')}</span>
300 303 %endif
301 304 </div>
302 305 </div>
303 306 <div id="reviewers" class="block-right pr-details-content reviewers">
304 307
305 308 ## members redering block
306 309 <input type="hidden" name="__start__" value="review_members:sequence">
307 310 <ul id="review_members" class="group_members">
308 311
309 312 % for review_obj, member, reasons, mandatory, status in c.pull_request_reviewers:
310 313 <script>
311 314 var member = ${h.json.dumps(h.reviewer_as_json(member, reasons=reasons, mandatory=mandatory, user_group=review_obj.rule_user_group_data()))|n};
312 315 var status = "${(status[0][1].status if status else 'not_reviewed')}";
313 316 var status_lbl = "${h.commit_status_lbl(status[0][1].status if status else 'not_reviewed')}";
314 317 var allowed_to_update = ${h.json.dumps(c.allowed_to_update)};
315 318
316 319 var entry = renderTemplate('reviewMemberEntry', {
317 320 'member': member,
318 321 'mandatory': member.mandatory,
319 322 'reasons': member.reasons,
320 323 'allowed_to_update': allowed_to_update,
321 324 'review_status': status,
322 325 'review_status_label': status_lbl,
323 326 'user_group': member.user_group,
324 327 'create': false
325 328 });
326 329 $('#review_members').append(entry)
327 330 </script>
328 331
329 332 % endfor
330 333
331 334 </ul>
332 335
333 336 <input type="hidden" name="__end__" value="review_members:sequence">
334 337 ## end members redering block
335 338
336 339 %if not c.pull_request.is_closed():
337 340 <div id="add_reviewer" class="ac" style="display: none;">
338 341 %if c.allowed_to_update:
339 342 % if not c.forbid_adding_reviewers:
340 343 <div id="add_reviewer_input" class="reviewer_ac">
341 344 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
342 345 <div id="reviewers_container"></div>
343 346 </div>
344 347 % endif
345 348 <div class="pull-right">
346 349 <button id="update_pull_request" class="btn btn-small no-margin">${_('Save Changes')}</button>
347 350 </div>
348 351 %endif
349 352 </div>
350 353 %endif
351 354 </div>
352 355
353 ## ## TODOs will be listed here
354 ## <div class="reviewers-title block-right">
355 ## <div class="pr-details-title">
356 ## ${_('TODOs')}
357 ## </div>
358 ## </div>
359 ## <div class="block-right pr-details-content reviewers">
360 ## <ul class="group_members">
361 ## <li>
362 ## XXXX
363 ## </li>
364 ## </ul>
365 ## </div>
366 ## </div>
356 ## TODOs will be listed here
357 <div class="reviewers-title block-right">
358 <div class="pr-details-title">
359 ## Only show unresolved, that is only what matters
360 TODO Comments - ${len(c.unresolved_comments)} / ${(len(c.unresolved_comments) + len(c.resolved_comments))}
361
362 % if not c.at_version:
363 % if c.resolved_comments:
364 <span class="block-right action_button last-item noselect" onclick="$('.unresolved-todo-text').toggle(); return versionController.toggleElement(this, '.unresolved-todo');" data-toggle-on="Show resolved" data-toggle-off="Hide resolved">Show resolved</span>
365 % else:
366 <span class="block-right last-item noselect">Show resolved</span>
367 % endif
368 % endif
369 </div>
370 </div>
371 <div class="block-right pr-details-content reviewers">
372
373 <table class="todo-table">
374 <%
375 def sorter(entry):
376 user_id = entry.author.user_id
377 resolved = '1' if entry.resolved else '0'
378 if user_id == c.rhodecode_user.user_id:
379 # own comments first
380 user_id = 0
381 return '{}_{}_{}'.format(resolved, user_id, str(entry.comment_id).zfill(100))
382 %>
383
384 % if c.at_version:
385 <tr>
386 <td class="unresolved-todo-text">${_('unresolved TODOs unavailable in this view')}.</td>
387 </tr>
388 % else:
389 % for todo_comment in sorted(c.unresolved_comments + c.resolved_comments, key=sorter):
390 <% resolved = todo_comment.resolved %>
391 % if inline:
392 <% outdated_at_ver = todo_comment.outdated_at_version(getattr(c, 'at_version_num', None)) %>
393 % else:
394 <% outdated_at_ver = todo_comment.older_than_version(getattr(c, 'at_version_num', None)) %>
395 % endif
396
397 <tr ${('class="unresolved-todo" style="display: none"' if resolved else '') |n}>
398
399 <td class="td-todo-number">
400 % if resolved:
401 <a class="permalink todo-resolved tooltip" title="${_('Resolved by comment #{}').format(todo_comment.resolved.comment_id)}" href="#comment-${todo_comment.comment_id}" onclick="return Rhodecode.comments.scrollToComment($('#comment-${todo_comment.comment_id}'), 0, ${h.json.dumps(outdated_at_ver)})">
402 <i class="icon-flag-filled"></i> ${todo_comment.comment_id}</a>
403 % else:
404 <a class="permalink" href="#comment-${todo_comment.comment_id}" onclick="return Rhodecode.comments.scrollToComment($('#comment-${todo_comment.comment_id}'), 0, ${h.json.dumps(outdated_at_ver)})">
405 <i class="icon-flag-filled"></i> ${todo_comment.comment_id}</a>
406 % endif
407 </td>
408 <td class="td-todo-gravatar">
409 ${base.gravatar(todo_comment.author.email, 16, user=todo_comment.author, tooltip=True, extra_class=['no-margin'])}
410 </td>
411 <td class="todo-comment-text-wrapper">
412 <div class="todo-comment-text">
413 <code>${h.chop_at_smart(todo_comment.text, '\n', suffix_if_chopped='...')}</code>
414 </div>
415 </td>
416
417 </tr>
418 % endfor
419
420 % if len(c.unresolved_comments) == 0:
421 <tr>
422 <td class="unresolved-todo-text">${_('No unresolved TODOs')}.</td>
423 </tr>
424 % endif
425
426 % endif
427
428 </table>
429
430 </div>
431 </div>
367 432
368 433 </div>
369 434
370 435 <div class="box">
371 436
372 437 % if c.state_progressing:
373 438
374 439 <h2 style="text-align: center">
375 440 ${_('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>
376 441 </h2>
377 442
378 443 % else:
379 444
380 445 ## Diffs rendered here
381 446 <div class="table" >
382 447 <div id="changeset_compare_view_content">
383 448 ##CS
384 449 % if c.missing_requirements:
385 450 <div class="box">
386 451 <div class="alert alert-warning">
387 452 <div>
388 453 <strong>${_('Missing requirements:')}</strong>
389 454 ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')}
390 455 </div>
391 456 </div>
392 457 </div>
393 458 % elif c.missing_commits:
394 459 <div class="box">
395 460 <div class="alert alert-warning">
396 461 <div>
397 462 <strong>${_('Missing commits')}:</strong>
398 463 ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}
399 464 ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}
400 465 ${_('Consider doing a {force_refresh_url} in case you think this is an error.').format(force_refresh_url=h.link_to('force refresh', h.current_route_path(request, force_refresh='1')))|n}
401 466 </div>
402 467 </div>
403 468 </div>
404 469 % endif
405 470
406 471 <div class="compare_view_commits_title">
407 472 % if not c.compare_mode:
408 473
409 474 % if c.at_version_pos:
410 475 <h4>
411 476 ${_('Showing changes at v%d, commenting is disabled.') % c.at_version_pos}
412 477 </h4>
413 478 % endif
414 479
415 480 <div class="pull-left">
416 481 <div class="btn-group">
417 482 <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)} >
418 483 % if c.collapse_all_commits:
419 484 <i class="icon-plus-squared-alt icon-no-margin"></i>
420 485 ${_ungettext('Expand {} commit', 'Expand {} commits', len(c.commit_ranges)).format(len(c.commit_ranges))}
421 486 % else:
422 487 <i class="icon-minus-squared-alt icon-no-margin"></i>
423 488 ${_ungettext('Collapse {} commit', 'Collapse {} commits', len(c.commit_ranges)).format(len(c.commit_ranges))}
424 489 % endif
425 490 </a>
426 491 </div>
427 492 </div>
428 493
429 494 <div class="pull-right">
430 495 % if c.allowed_to_update and not c.pull_request.is_closed():
431 496
432 497 <div class="btn-group btn-group-actions">
433 498 <a id="update_commits" class="btn btn-primary no-margin" onclick="updateController.updateCommits(this); return false">
434 499 ${_('Update commits')}
435 500 </a>
436 501
437 502 <a id="update_commits_switcher" class="tooltip btn btn-primary" style="margin-left: -1px" data-toggle="dropdown" aria-pressed="false" role="button" title="${_('more update options')}">
438 503 <i class="icon-down"></i>
439 504 </a>
440 505
441 506 <div class="btn-action-switcher-container" id="update-commits-switcher">
442 507 <ul class="btn-action-switcher" role="menu">
443 508 <li>
444 509 <a href="#forceUpdate" onclick="updateController.forceUpdateCommits(this); return false">
445 510 ${_('Force update commits')}
446 511 </a>
447 512 <div class="action-help-block">
448 513 ${_('Update commits and force refresh this pull request.')}
449 514 </div>
450 515 </li>
451 516 </ul>
452 517 </div>
453 518 </div>
454 519
455 520 % else:
456 521 <a class="tooltip btn disabled pull-right" disabled="disabled" title="${_('Update is disabled for current view')}">${_('Update commits')}</a>
457 522 % endif
458 523
459 524 </div>
460 525 % endif
461 526 </div>
462 527
463 528 % if not c.missing_commits:
464 529 % if c.compare_mode:
465 530 % if c.at_version:
466 531 <h4>
467 532 ${_('Commits and changes between v{ver_from} and {ver_to} of this pull request, commenting is disabled').format(ver_from=c.from_version_pos, ver_to=c.at_version_pos if c.at_version_pos else 'latest')}:
468 533 </h4>
469 534
470 535 <div class="subtitle-compare">
471 536 ${_('commits added: {}, removed: {}').format(len(c.commit_changes_summary.added), len(c.commit_changes_summary.removed))}
472 537 </div>
473 538
474 539 <div class="container">
475 540 <table class="rctable compare_view_commits">
476 541 <tr>
477 542 <th></th>
478 543 <th>${_('Time')}</th>
479 544 <th>${_('Author')}</th>
480 545 <th>${_('Commit')}</th>
481 546 <th></th>
482 547 <th>${_('Description')}</th>
483 548 </tr>
484 549
485 550 % for c_type, commit in c.commit_changes:
486 551 % if c_type in ['a', 'r']:
487 552 <%
488 553 if c_type == 'a':
489 554 cc_title = _('Commit added in displayed changes')
490 555 elif c_type == 'r':
491 556 cc_title = _('Commit removed in displayed changes')
492 557 else:
493 558 cc_title = ''
494 559 %>
495 560 <tr id="row-${commit.raw_id}" commit_id="${commit.raw_id}" class="compare_select">
496 561 <td>
497 562 <div class="commit-change-indicator color-${c_type}-border">
498 563 <div class="commit-change-content color-${c_type} tooltip" title="${h.tooltip(cc_title)}">
499 564 ${c_type.upper()}
500 565 </div>
501 566 </div>
502 567 </td>
503 568 <td class="td-time">
504 569 ${h.age_component(commit.date)}
505 570 </td>
506 571 <td class="td-user">
507 572 ${base.gravatar_with_user(commit.author, 16, tooltip=True)}
508 573 </td>
509 574 <td class="td-hash">
510 575 <code>
511 576 <a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=commit.raw_id)}">
512 577 r${commit.idx}:${h.short_id(commit.raw_id)}
513 578 </a>
514 579 ${h.hidden('revisions', commit.raw_id)}
515 580 </code>
516 581 </td>
517 582 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_( 'Expand commit message')}" onclick="commitsController.expandCommit(this); return false">
518 583 <i class="icon-expand-linked"></i>
519 584 </td>
520 585 <td class="mid td-description">
521 586 <div class="log-container truncate-wrap">
522 587 <div class="message truncate" id="c-${commit.raw_id}" data-message-raw="${commit.message}">${h.urlify_commit_message(commit.message, c.repo_name)}</div>
523 588 </div>
524 589 </td>
525 590 </tr>
526 591 % endif
527 592 % endfor
528 593 </table>
529 594 </div>
530 595
531 596 % endif
532 597
533 598 % else:
534 599 <%include file="/compare/compare_commits.mako" />
535 600 % endif
536 601
537 602 <div class="cs_files">
538 603 <%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/>
539 604 % if c.at_version:
540 605 <% c.inline_cnt = len(c.inline_versions[c.at_version_num]['display']) %>
541 606 <% c.comments = c.comment_versions[c.at_version_num]['display'] %>
542 607 % else:
543 608 <% c.inline_cnt = len(c.inline_versions[c.at_version_num]['until']) %>
544 609 <% c.comments = c.comment_versions[c.at_version_num]['until'] %>
545 610 % endif
546 611
547 612 <%
548 613 pr_menu_data = {
549 614 'outdated_comm_count_ver': outdated_comm_count_ver
550 615 }
551 616 %>
552 617
553 618 ${cbdiffs.render_diffset_menu(c.diffset, range_diff_on=c.range_diff_on)}
554 619
555 620 % if c.range_diff_on:
556 621 % for commit in c.commit_ranges:
557 622 ${cbdiffs.render_diffset(
558 623 c.changes[commit.raw_id],
559 624 commit=commit, use_comments=True,
560 625 collapse_when_files_over=5,
561 626 disable_new_comments=True,
562 627 deleted_files_comments=c.deleted_files_comments,
563 628 inline_comments=c.inline_comments,
564 pull_request_menu=pr_menu_data)}
629 pull_request_menu=pr_menu_data, show_todos=False)}
565 630 % endfor
566 631 % else:
567 632 ${cbdiffs.render_diffset(
568 633 c.diffset, use_comments=True,
569 634 collapse_when_files_over=30,
570 635 disable_new_comments=not c.allowed_to_comment,
571 636 deleted_files_comments=c.deleted_files_comments,
572 637 inline_comments=c.inline_comments,
573 pull_request_menu=pr_menu_data)}
638 pull_request_menu=pr_menu_data, show_todos=False)}
574 639 % endif
575 640
576 641 </div>
577 642 % else:
578 643 ## skipping commits we need to clear the view for missing commits
579 644 <div style="clear:both;"></div>
580 645 % endif
581 646
582 647 </div>
583 648 </div>
584 649
585 650 ## template for inline comment form
586 651 <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
587 652
588 653 ## comments heading with count
589 654 <div class="comments-heading">
590 655 <i class="icon-comment"></i>
591 656 ${_('Comments')} ${len(c.comments)}
592 657 </div>
593 658
594 659 ## render general comments
595 660 <div id="comment-tr-show">
596 661 % if general_outdated_comm_count_ver:
597 662 <div class="info-box">
598 663 % if general_outdated_comm_count_ver == 1:
599 664 ${_('there is {num} general comment from older versions').format(num=general_outdated_comm_count_ver)},
600 665 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show it')}</a>
601 666 % else:
602 667 ${_('there are {num} general comments from older versions').format(num=general_outdated_comm_count_ver)},
603 668 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show them')}</a>
604 669 % endif
605 670 </div>
606 671 % endif
607 672 </div>
608 673
609 674 ${comment.generate_comments(c.comments, include_pull_request=True, is_pull_request=True)}
610 675
611 676 % if not c.pull_request.is_closed():
612 677 ## main comment form and it status
613 678 ${comment.comments(h.route_path('pullrequest_comment_create', repo_name=c.repo_name,
614 679 pull_request_id=c.pull_request.pull_request_id),
615 680 c.pull_request_review_status,
616 681 is_pull_request=True, change_status=c.allowed_to_change_status)}
617 682
618 683 ## merge status, and merge action
619 684 <div class="pull-request-merge">
620 685 <%include file="/pullrequests/pullrequest_merge_checks.mako"/>
621 686 </div>
622 687
623 688 %endif
624 689
625 690 % endif
626 691 </div>
627 692
628 693 <script type="text/javascript">
629 694
630 695 versionController = new VersionController();
631 696 versionController.init();
632 697
633 698 reviewersController = new ReviewersController();
634 699 commitsController = new CommitsController();
635 700
636 701 updateController = new UpdatePrController();
637 702
638 703 $(function () {
639 704
640 705 // custom code mirror
641 706 var codeMirrorInstance = $('#pr-description-input').get(0).MarkupForm.cm;
642 707
643 708 var PRDetails = {
644 709 editButton: $('#open_edit_pullrequest'),
645 710 closeButton: $('#close_edit_pullrequest'),
646 711 deleteButton: $('#delete_pullrequest'),
647 712 viewFields: $('#pr-desc, #pr-title'),
648 713 editFields: $('#pr-desc-edit, #pr-title-edit, .pr-save'),
649 714
650 715 init: function () {
651 716 var that = this;
652 717 this.editButton.on('click', function (e) {
653 718 that.edit();
654 719 });
655 720 this.closeButton.on('click', function (e) {
656 721 that.view();
657 722 });
658 723 },
659 724
660 725 edit: function (event) {
661 726 this.viewFields.hide();
662 727 this.editButton.hide();
663 728 this.deleteButton.hide();
664 729 this.closeButton.show();
665 730 this.editFields.show();
666 731 codeMirrorInstance.refresh();
667 732 },
668 733
669 734 view: function (event) {
670 735 this.editButton.show();
671 736 this.deleteButton.show();
672 737 this.editFields.hide();
673 738 this.closeButton.hide();
674 739 this.viewFields.show();
675 740 }
676 741 };
677 742
678 743 var ReviewersPanel = {
679 744 editButton: $('#open_edit_reviewers'),
680 745 closeButton: $('#close_edit_reviewers'),
681 746 addButton: $('#add_reviewer'),
682 747 removeButtons: $('.reviewer_member_remove,.reviewer_member_mandatory_remove'),
683 748
684 749 init: function () {
685 750 var self = this;
686 751 this.editButton.on('click', function (e) {
687 752 self.edit();
688 753 });
689 754 this.closeButton.on('click', function (e) {
690 755 self.close();
691 756 });
692 757 },
693 758
694 759 edit: function (event) {
695 760 this.editButton.hide();
696 761 this.closeButton.show();
697 762 this.addButton.show();
698 763 this.removeButtons.css('visibility', 'visible');
699 764 // review rules
700 765 reviewersController.loadReviewRules(
701 766 ${c.pull_request.reviewer_data_json | n});
702 767 },
703 768
704 769 close: function (event) {
705 770 this.editButton.show();
706 771 this.closeButton.hide();
707 772 this.addButton.hide();
708 773 this.removeButtons.css('visibility', 'hidden');
709 774 // hide review rules
710 775 reviewersController.hideReviewRules()
711 776 }
712 777 };
713 778
714 779 PRDetails.init();
715 780 ReviewersPanel.init();
716 781
717 782 showOutdated = function (self) {
718 783 $('.comment-inline.comment-outdated').show();
719 784 $('.filediff-outdated').show();
720 785 $('.showOutdatedComments').hide();
721 786 $('.hideOutdatedComments').show();
722 787 };
723 788
724 789 hideOutdated = function (self) {
725 790 $('.comment-inline.comment-outdated').hide();
726 791 $('.filediff-outdated').hide();
727 792 $('.hideOutdatedComments').hide();
728 793 $('.showOutdatedComments').show();
729 794 };
730 795
731 796 refreshMergeChecks = function () {
732 797 var loadUrl = "${request.current_route_path(_query=dict(merge_checks=1))}";
733 798 $('.pull-request-merge').css('opacity', 0.3);
734 799 $('.action-buttons-extra').css('opacity', 0.3);
735 800
736 801 $('.pull-request-merge').load(
737 802 loadUrl, function () {
738 803 $('.pull-request-merge').css('opacity', 1);
739 804
740 805 $('.action-buttons-extra').css('opacity', 1);
741 806 }
742 807 );
743 808 };
744 809
745 810 closePullRequest = function (status) {
746 811 if (!confirm(_gettext('Are you sure to close this pull request without merging?'))) {
747 812 return false;
748 813 }
749 814 // inject closing flag
750 815 $('.action-buttons-extra').append('<input type="hidden" class="close-pr-input" id="close_pull_request" value="1">');
751 816 $(generalCommentForm.statusChange).select2("val", status).trigger('change');
752 817 $(generalCommentForm.submitForm).submit();
753 818 };
754 819
755 820 $('#show-outdated-comments').on('click', function (e) {
756 821 var button = $(this);
757 822 var outdated = $('.comment-outdated');
758 823
759 824 if (button.html() === "(Show)") {
760 825 button.html("(Hide)");
761 826 outdated.show();
762 827 } else {
763 828 button.html("(Show)");
764 829 outdated.hide();
765 830 }
766 831 });
767 832
768 833 $('.show-inline-comments').on('change', function (e) {
769 834 var show = 'none';
770 835 var target = e.currentTarget;
771 836 if (target.checked) {
772 837 show = ''
773 838 }
774 839 var boxid = $(target).attr('id_for');
775 840 var comments = $('#{0} .inline-comments'.format(boxid));
776 841 var fn_display = function (idx) {
777 842 $(this).css('display', show);
778 843 };
779 844 $(comments).each(fn_display);
780 845 var btns = $('#{0} .inline-comments-button'.format(boxid));
781 846 $(btns).each(fn_display);
782 847 });
783 848
784 849 $('#merge_pull_request_form').submit(function () {
785 850 if (!$('#merge_pull_request').attr('disabled')) {
786 851 $('#merge_pull_request').attr('disabled', 'disabled');
787 852 }
788 853 return true;
789 854 });
790 855
791 856 $('#edit_pull_request').on('click', function (e) {
792 857 var title = $('#pr-title-input').val();
793 858 var description = codeMirrorInstance.getValue();
794 859 var renderer = $('#pr-renderer-input').val();
795 860 editPullRequest(
796 861 "${c.repo_name}", "${c.pull_request.pull_request_id}",
797 862 title, description, renderer);
798 863 });
799 864
800 865 $('#update_pull_request').on('click', function (e) {
801 866 $(this).attr('disabled', 'disabled');
802 867 $(this).addClass('disabled');
803 868 $(this).html(_gettext('Saving...'));
804 869 reviewersController.updateReviewers(
805 870 "${c.repo_name}", "${c.pull_request.pull_request_id}");
806 871 });
807 872
808 873
809 874 // fixing issue with caches on firefox
810 875 $('#update_commits').removeAttr("disabled");
811 876
812 877 $('.show-inline-comments').on('click', function (e) {
813 878 var boxid = $(this).attr('data-comment-id');
814 879 var button = $(this);
815 880
816 881 if (button.hasClass("comments-visible")) {
817 882 $('#{0} .inline-comments'.format(boxid)).each(function (index) {
818 883 $(this).hide();
819 884 });
820 885 button.removeClass("comments-visible");
821 886 } else {
822 887 $('#{0} .inline-comments'.format(boxid)).each(function (index) {
823 888 $(this).show();
824 889 });
825 890 button.addClass("comments-visible");
826 891 }
827 892 });
828 893
829 894 // register submit callback on commentForm form to track TODOs
830 895 window.commentFormGlobalSubmitSuccessCallback = function () {
831 896 refreshMergeChecks();
832 897 };
833 898
834 899 ReviewerAutoComplete('#user');
835 900
836 901 })
837 902
838 903 </script>
839 904
840 905 </div>
841 906
842 907 </%def>
General Comments 0
You need to be logged in to leave comments. Login now