##// END OF EJS Templates
templates: switched most of confirm dialogs to sweetalert2
ergo -
r4313:4bf03e9f default
parent child Browse files
Show More
@@ -1,1371 +1,1371 b''
1 1 .swal2-popup.swal2-toast {
2 2 flex-direction: row;
3 3 align-items: center;
4 4 width: auto;
5 5 padding: 0.625em;
6 6 overflow-y: hidden;
7 7 background: #fff;
8 8 box-shadow: 0 0 0.625em #d9d9d9;
9 9 }
10 10 .swal2-popup.swal2-toast .swal2-header {
11 11 flex-direction: row;
12 12 }
13 13 .swal2-popup.swal2-toast .swal2-title {
14 14 flex-grow: 1;
15 15 justify-content: flex-start;
16 16 margin: 0 0.6em;
17 17 font-size: 1em;
18 18 }
19 19 .swal2-popup.swal2-toast .swal2-footer {
20 20 margin: 0.5em 0 0;
21 21 padding: 0.5em 0 0;
22 22 font-size: 0.8em;
23 23 }
24 24 .swal2-popup.swal2-toast .swal2-close {
25 25 position: static;
26 26 width: 0.8em;
27 27 height: 0.8em;
28 28 line-height: 0.8;
29 29 }
30 30 .swal2-popup.swal2-toast .swal2-content {
31 31 justify-content: flex-start;
32 32 font-size: 1em;
33 33 }
34 34 .swal2-popup.swal2-toast .swal2-icon {
35 35 width: 2em;
36 36 min-width: 2em;
37 37 height: 2em;
38 38 margin: 0;
39 39 }
40 40 .swal2-popup.swal2-toast .swal2-icon .swal2-icon-content {
41 41 display: flex;
42 42 align-items: center;
43 43 font-size: 1.8em;
44 44 font-weight: bold;
45 45 }
46 46 @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
47 47 .swal2-popup.swal2-toast .swal2-icon .swal2-icon-content {
48 48 font-size: 0.25em;
49 49 }
50 50 }
51 51 .swal2-popup.swal2-toast .swal2-icon.swal2-success .swal2-success-ring {
52 52 width: 2em;
53 53 height: 2em;
54 54 }
55 55 .swal2-popup.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line] {
56 56 top: 0.875em;
57 57 width: 1.375em;
58 58 }
59 59 .swal2-popup.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=left] {
60 60 left: 0.3125em;
61 61 }
62 62 .swal2-popup.swal2-toast .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=right] {
63 63 right: 0.3125em;
64 64 }
65 65 .swal2-popup.swal2-toast .swal2-actions {
66 66 flex-basis: auto !important;
67 67 width: auto;
68 68 height: auto;
69 69 margin: 0 0.3125em;
70 70 }
71 71 .swal2-popup.swal2-toast .swal2-styled {
72 72 margin: 0 0.3125em;
73 73 padding: 0.3125em 0.625em;
74 74 font-size: 1em;
75 75 }
76 76 .swal2-popup.swal2-toast .swal2-styled:focus {
77 77 box-shadow: 0 0 0 1px #fff, 0 0 0 3px rgba(50, 100, 150, 0.4);
78 78 }
79 79 .swal2-popup.swal2-toast .swal2-success {
80 80 border-color: @alert1;
81 81 }
82 82 .swal2-popup.swal2-toast .swal2-success [class^=swal2-success-circular-line] {
83 83 position: absolute;
84 84 width: 1.6em;
85 85 height: 3em;
86 86 transform: rotate(45deg);
87 87 border-radius: 50%;
88 88 }
89 89 .swal2-popup.swal2-toast .swal2-success [class^=swal2-success-circular-line][class$=left] {
90 90 top: -0.8em;
91 91 left: -0.5em;
92 92 transform: rotate(-45deg);
93 93 transform-origin: 2em 2em;
94 94 border-radius: 4em 0 0 4em;
95 95 }
96 96 .swal2-popup.swal2-toast .swal2-success [class^=swal2-success-circular-line][class$=right] {
97 97 top: -0.25em;
98 98 left: 0.9375em;
99 99 transform-origin: 0 1.5em;
100 100 border-radius: 0 4em 4em 0;
101 101 }
102 102 .swal2-popup.swal2-toast .swal2-success .swal2-success-ring {
103 103 width: 2em;
104 104 height: 2em;
105 105 }
106 106 .swal2-popup.swal2-toast .swal2-success .swal2-success-fix {
107 107 top: 0;
108 108 left: 0.4375em;
109 109 width: 0.4375em;
110 110 height: 2.6875em;
111 111 }
112 112 .swal2-popup.swal2-toast .swal2-success [class^=swal2-success-line] {
113 113 height: 0.3125em;
114 114 }
115 115 .swal2-popup.swal2-toast .swal2-success [class^=swal2-success-line][class$=tip] {
116 116 top: 1.125em;
117 117 left: 0.1875em;
118 118 width: 0.75em;
119 119 }
120 120 .swal2-popup.swal2-toast .swal2-success [class^=swal2-success-line][class$=long] {
121 121 top: 0.9375em;
122 122 right: 0.1875em;
123 123 width: 1.375em;
124 124 }
125 125 .swal2-popup.swal2-toast .swal2-success.swal2-icon-show .swal2-success-line-tip {
126 126 -webkit-animation: swal2-toast-animate-success-line-tip 0.75s;
127 127 animation: swal2-toast-animate-success-line-tip 0.75s;
128 128 }
129 129 .swal2-popup.swal2-toast .swal2-success.swal2-icon-show .swal2-success-line-long {
130 130 -webkit-animation: swal2-toast-animate-success-line-long 0.75s;
131 131 animation: swal2-toast-animate-success-line-long 0.75s;
132 132 }
133 133 .swal2-popup.swal2-toast.swal2-show {
134 134 -webkit-animation: swal2-toast-show 0.5s;
135 135 animation: swal2-toast-show 0.5s;
136 136 }
137 137 .swal2-popup.swal2-toast.swal2-hide {
138 138 -webkit-animation: swal2-toast-hide 0.1s forwards;
139 139 animation: swal2-toast-hide 0.1s forwards;
140 140 }
141 141
142 142 .swal2-container {
143 143 display: flex;
144 144 position: fixed;
145 145 z-index: 1060;
146 146 top: 0;
147 147 right: 0;
148 148 bottom: 0;
149 149 left: 0;
150 150 flex-direction: row;
151 151 align-items: center;
152 152 justify-content: center;
153 153 padding: 0.625em;
154 154 overflow-x: hidden;
155 155 transition: background-color 0.1s;
156 156 -webkit-overflow-scrolling: touch;
157 157 }
158 158 .swal2-container.swal2-backdrop-show, .swal2-container.swal2-noanimation {
159 159 background: rgba(0, 0, 0, 0.4);
160 160 }
161 161 .swal2-container.swal2-backdrop-hide {
162 162 background: transparent !important;
163 163 }
164 164 .swal2-container.swal2-top {
165 165 align-items: flex-start;
166 166 }
167 167 .swal2-container.swal2-top-start, .swal2-container.swal2-top-left {
168 168 align-items: flex-start;
169 169 justify-content: flex-start;
170 170 }
171 171 .swal2-container.swal2-top-end, .swal2-container.swal2-top-right {
172 172 align-items: flex-start;
173 173 justify-content: flex-end;
174 174 }
175 175 .swal2-container.swal2-center {
176 176 align-items: center;
177 177 }
178 178 .swal2-container.swal2-center-start, .swal2-container.swal2-center-left {
179 179 align-items: center;
180 180 justify-content: flex-start;
181 181 }
182 182 .swal2-container.swal2-center-end, .swal2-container.swal2-center-right {
183 183 align-items: center;
184 184 justify-content: flex-end;
185 185 }
186 186 .swal2-container.swal2-bottom {
187 187 align-items: flex-end;
188 188 }
189 189 .swal2-container.swal2-bottom-start, .swal2-container.swal2-bottom-left {
190 190 align-items: flex-end;
191 191 justify-content: flex-start;
192 192 }
193 193 .swal2-container.swal2-bottom-end, .swal2-container.swal2-bottom-right {
194 194 align-items: flex-end;
195 195 justify-content: flex-end;
196 196 }
197 197 .swal2-container.swal2-bottom > :first-child, .swal2-container.swal2-bottom-start > :first-child, .swal2-container.swal2-bottom-left > :first-child, .swal2-container.swal2-bottom-end > :first-child, .swal2-container.swal2-bottom-right > :first-child {
198 198 margin-top: auto;
199 199 }
200 200 .swal2-container.swal2-grow-fullscreen > .swal2-modal {
201 201 display: flex !important;
202 202 flex: 1;
203 203 align-self: stretch;
204 204 justify-content: center;
205 205 }
206 206 .swal2-container.swal2-grow-row > .swal2-modal {
207 207 display: flex !important;
208 208 flex: 1;
209 209 align-content: center;
210 210 justify-content: center;
211 211 }
212 212 .swal2-container.swal2-grow-column {
213 213 flex: 1;
214 214 flex-direction: column;
215 215 }
216 216 .swal2-container.swal2-grow-column.swal2-top, .swal2-container.swal2-grow-column.swal2-center, .swal2-container.swal2-grow-column.swal2-bottom {
217 217 align-items: center;
218 218 }
219 219 .swal2-container.swal2-grow-column.swal2-top-start, .swal2-container.swal2-grow-column.swal2-center-start, .swal2-container.swal2-grow-column.swal2-bottom-start, .swal2-container.swal2-grow-column.swal2-top-left, .swal2-container.swal2-grow-column.swal2-center-left, .swal2-container.swal2-grow-column.swal2-bottom-left {
220 220 align-items: flex-start;
221 221 }
222 222 .swal2-container.swal2-grow-column.swal2-top-end, .swal2-container.swal2-grow-column.swal2-center-end, .swal2-container.swal2-grow-column.swal2-bottom-end, .swal2-container.swal2-grow-column.swal2-top-right, .swal2-container.swal2-grow-column.swal2-center-right, .swal2-container.swal2-grow-column.swal2-bottom-right {
223 223 align-items: flex-end;
224 224 }
225 225 .swal2-container.swal2-grow-column > .swal2-modal {
226 226 display: flex !important;
227 227 flex: 1;
228 228 align-content: center;
229 229 justify-content: center;
230 230 }
231 231 .swal2-container.swal2-no-transition {
232 232 transition: none !important;
233 233 }
234 234 .swal2-container:not(.swal2-top):not(.swal2-top-start):not(.swal2-top-end):not(.swal2-top-left):not(.swal2-top-right):not(.swal2-center-start):not(.swal2-center-end):not(.swal2-center-left):not(.swal2-center-right):not(.swal2-bottom):not(.swal2-bottom-start):not(.swal2-bottom-end):not(.swal2-bottom-left):not(.swal2-bottom-right):not(.swal2-grow-fullscreen) > .swal2-modal {
235 235 margin: auto;
236 236 }
237 237 @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
238 238 .swal2-container .swal2-modal {
239 239 margin: 0 !important;
240 240 }
241 241 }
242 242
243 243 .swal2-popup {
244 244 display: none;
245 245 position: relative;
246 246 box-sizing: border-box;
247 247 flex-direction: column;
248 248 justify-content: center;
249 width: 32em;
249 width: 38em;
250 250 max-width: 100%;
251 251 padding: 1.25em;
252 252 border: none;
253 253 border-radius: 0.3125em;
254 254 background: #fff;
255 255 font-family: inherit;
256 256 font-size: 1rem;
257 257 }
258 258 .swal2-popup:focus {
259 259 outline: none;
260 260 }
261 261 .swal2-popup.swal2-loading {
262 262 overflow-y: hidden;
263 263 }
264 264
265 265 .swal2-header {
266 266 display: flex;
267 267 flex-direction: column;
268 268 align-items: center;
269 269 }
270 270
271 271 .swal2-title {
272 272 position: relative;
273 273 max-width: 100%;
274 274 margin: 0 0 0.4em;
275 275 padding: 0;
276 276 color: #595959;
277 277 font-size: 1.875em;
278 278 font-weight: 600;
279 279 text-align: center;
280 280 text-transform: none;
281 281 word-wrap: break-word;
282 282 }
283 283
284 284 .swal2-actions {
285 285 display: flex;
286 286 z-index: 1;
287 287 flex-wrap: wrap;
288 288 align-items: center;
289 289 justify-content: center;
290 290 width: 100%;
291 291 margin: 1.25em auto 0;
292 292 }
293 293 .swal2-actions:not(.swal2-loading) .swal2-styled[disabled] {
294 294 opacity: 0.4;
295 295 }
296 296 .swal2-actions:not(.swal2-loading) .swal2-styled:hover {
297 297 background-image: linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1));
298 298 }
299 299 .swal2-actions:not(.swal2-loading) .swal2-styled:active {
300 300 background-image: linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2));
301 301 }
302 302 .swal2-actions.swal2-loading .swal2-styled.swal2-confirm {
303 303 box-sizing: border-box;
304 304 width: 2.5em;
305 305 height: 2.5em;
306 306 margin: 0.46875em;
307 307 padding: 0;
308 308 -webkit-animation: swal2-rotate-loading 1.5s linear 0s infinite normal;
309 309 animation: swal2-rotate-loading 1.5s linear 0s infinite normal;
310 310 border: 0.25em solid transparent;
311 311 border-radius: 100%;
312 312 border-color: transparent;
313 313 background-color: transparent !important;
314 314 color: transparent !important;
315 315 cursor: default;
316 316 -webkit-user-select: none;
317 317 -moz-user-select: none;
318 318 -ms-user-select: none;
319 319 user-select: none;
320 320 }
321 321 .swal2-actions.swal2-loading .swal2-styled.swal2-cancel {
322 322 margin-right: 30px;
323 323 margin-left: 30px;
324 324 }
325 325 .swal2-actions.swal2-loading :not(.swal2-styled).swal2-confirm::after {
326 326 content: "";
327 327 display: inline-block;
328 328 width: 15px;
329 329 height: 15px;
330 330 margin-left: 5px;
331 331 -webkit-animation: swal2-rotate-loading 1.5s linear 0s infinite normal;
332 332 animation: swal2-rotate-loading 1.5s linear 0s infinite normal;
333 333 border: 3px solid #999999;
334 334 border-radius: 50%;
335 335 border-right-color: transparent;
336 336 box-shadow: 1px 1px 1px #fff;
337 337 }
338 338
339 339 .swal2-styled {
340 340 margin: 0.3125em;
341 341 padding: 0.625em 2em;
342 342 box-shadow: none;
343 343 font-weight: 500;
344 344 }
345 345 .swal2-styled:not([disabled]) {
346 346 cursor: pointer;
347 347 }
348 348 .swal2-styled.swal2-confirm {
349 349 border: 0;
350 350 border-radius: 0.25em;
351 351 background: initial;
352 352 background-color: @alert4;
353 353 color: #fff;
354 354 font-size: 1.0625em;
355 355 }
356 356 .swal2-styled.swal2-cancel {
357 357 border: 0;
358 358 border-radius: 0.25em;
359 359 background: initial;
360 360 background-color: #aaa;
361 361 color: #fff;
362 362 font-size: 1.0625em;
363 363 }
364 364 .swal2-styled:focus {
365 365 outline: none;
366 366 box-shadow: 0 0 0 1px #fff, 0 0 0 3px rgba(50, 100, 150, 0.4);
367 367 }
368 368 .swal2-styled::-moz-focus-inner {
369 369 border: 0;
370 370 }
371 371
372 372 .swal2-footer {
373 373 justify-content: center;
374 374 margin: 1.25em 0 0;
375 375 padding: 1em 0 0;
376 376 border-top: 1px solid #eee;
377 377 color: #545454;
378 378 font-size: 1em;
379 379 }
380 380
381 381 .swal2-timer-progress-bar-container {
382 382 position: absolute;
383 383 right: 0;
384 384 bottom: 0;
385 385 left: 0;
386 386 height: 0.25em;
387 387 overflow: hidden;
388 388 border-bottom-right-radius: 0.3125em;
389 389 border-bottom-left-radius: 0.3125em;
390 390 }
391 391
392 392 .swal2-timer-progress-bar {
393 393 width: 100%;
394 394 height: 0.25em;
395 395 background: rgba(0, 0, 0, 0.2);
396 396 }
397 397
398 398 .swal2-image {
399 399 max-width: 100%;
400 400 margin: 1.25em auto;
401 401 }
402 402
403 403 .swal2-close {
404 404 position: absolute;
405 405 z-index: 2;
406 406 top: 0;
407 407 right: 0;
408 408 align-items: center;
409 409 justify-content: center;
410 410 width: 1.2em;
411 411 height: 1.2em;
412 412 padding: 0;
413 413 overflow: hidden;
414 414 transition: color 0.1s ease-out;
415 415 border: none;
416 416 border-radius: 0;
417 417 background: transparent;
418 418 color: #cccccc;
419 419 font-family: serif;
420 420 font-size: 2.5em;
421 421 line-height: 1.2;
422 422 cursor: pointer;
423 423 }
424 424 .swal2-close:hover {
425 425 transform: none;
426 426 background: transparent;
427 427 color: #f27474;
428 428 }
429 429 .swal2-close::-moz-focus-inner {
430 430 border: 0;
431 431 }
432 432
433 433 .swal2-content {
434 434 z-index: 1;
435 435 justify-content: center;
436 436 margin: 0;
437 437 padding: 0;
438 438 color: #545454;
439 439 font-size: 1.125em;
440 440 font-weight: normal;
441 441 line-height: normal;
442 442 text-align: center;
443 443 word-wrap: break-word;
444 444 }
445 445
446 446 .swal2-input,
447 447 .swal2-file,
448 448 .swal2-textarea,
449 449 .swal2-select,
450 450 .swal2-radio,
451 451 .swal2-checkbox {
452 452 margin: 1em auto;
453 453 }
454 454
455 455 .swal2-input,
456 456 .swal2-file,
457 457 .swal2-textarea {
458 458 box-sizing: border-box;
459 459 width: 100%;
460 460 transition: border-color 0.3s, box-shadow 0.3s;
461 461 border: 1px solid #d9d9d9;
462 462 border-radius: 0.1875em;
463 463 background: inherit;
464 464 box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.06);
465 465 color: inherit;
466 466 font-size: 1.125em;
467 467 }
468 468 .swal2-input.swal2-inputerror,
469 469 .swal2-file.swal2-inputerror,
470 470 .swal2-textarea.swal2-inputerror {
471 471 border-color: #f27474 !important;
472 472 box-shadow: 0 0 2px #f27474 !important;
473 473 }
474 474 .swal2-input:focus,
475 475 .swal2-file:focus,
476 476 .swal2-textarea:focus {
477 477 border: 1px solid #b4dbed;
478 478 outline: none;
479 479 box-shadow: 0 0 3px #c4e6f5;
480 480 }
481 481 .swal2-input::-webkit-input-placeholder, .swal2-file::-webkit-input-placeholder, .swal2-textarea::-webkit-input-placeholder {
482 482 color: #cccccc;
483 483 }
484 484 .swal2-input::-moz-placeholder, .swal2-file::-moz-placeholder, .swal2-textarea::-moz-placeholder {
485 485 color: #cccccc;
486 486 }
487 487 .swal2-input:-ms-input-placeholder, .swal2-file:-ms-input-placeholder, .swal2-textarea:-ms-input-placeholder {
488 488 color: #cccccc;
489 489 }
490 490 .swal2-input::-ms-input-placeholder, .swal2-file::-ms-input-placeholder, .swal2-textarea::-ms-input-placeholder {
491 491 color: #cccccc;
492 492 }
493 493 .swal2-input::placeholder,
494 494 .swal2-file::placeholder,
495 495 .swal2-textarea::placeholder {
496 496 color: #cccccc;
497 497 }
498 498
499 499 .swal2-range {
500 500 margin: 1em auto;
501 501 background: #fff;
502 502 }
503 503 .swal2-range input {
504 504 width: 80%;
505 505 }
506 506 .swal2-range output {
507 507 width: 20%;
508 508 color: inherit;
509 509 font-weight: 600;
510 510 text-align: center;
511 511 }
512 512 .swal2-range input,
513 513 .swal2-range output {
514 514 height: 2.625em;
515 515 padding: 0;
516 516 font-size: 1.125em;
517 517 line-height: 2.625em;
518 518 }
519 519
520 520 .swal2-input {
521 521 height: 2.625em;
522 522 padding: 0 0.75em;
523 523 }
524 524 .swal2-input[type=number] {
525 525 max-width: 10em;
526 526 }
527 527
528 528 .swal2-file {
529 529 background: inherit;
530 530 font-size: 1.125em;
531 531 }
532 532
533 533 .swal2-textarea {
534 534 height: 6.75em;
535 535 padding: 0.75em;
536 536 }
537 537
538 538 .swal2-select {
539 539 min-width: 50%;
540 540 max-width: 100%;
541 541 padding: 0.375em 0.625em;
542 542 background: inherit;
543 543 color: inherit;
544 544 font-size: 1.125em;
545 545 }
546 546
547 547 .swal2-radio,
548 548 .swal2-checkbox {
549 549 align-items: center;
550 550 justify-content: center;
551 551 background: #fff;
552 552 color: inherit;
553 553 }
554 554 .swal2-radio label,
555 555 .swal2-checkbox label {
556 556 margin: 0 0.6em;
557 557 font-size: 1.125em;
558 558 }
559 559 .swal2-radio input,
560 560 .swal2-checkbox input {
561 561 margin: 0 0.4em;
562 562 }
563 563
564 564 .swal2-validation-message {
565 565 display: none;
566 566 align-items: center;
567 567 justify-content: center;
568 568 padding: 0.625em;
569 569 overflow: hidden;
570 570 background: #f0f0f0;
571 571 color: #666666;
572 572 font-size: 1em;
573 573 font-weight: 300;
574 574 }
575 575 .swal2-validation-message::before {
576 576 content: "!";
577 577 display: inline-block;
578 578 width: 1.5em;
579 579 min-width: 1.5em;
580 580 height: 1.5em;
581 581 margin: 0 0.625em;
582 582 border-radius: 50%;
583 583 background-color: #f27474;
584 584 color: #fff;
585 585 font-weight: 600;
586 586 line-height: 1.5em;
587 587 text-align: center;
588 588 }
589 589
590 590 .swal2-icon {
591 591 position: relative;
592 592 box-sizing: content-box;
593 593 justify-content: center;
594 594 width: 5em;
595 595 height: 5em;
596 596 margin: 1.25em auto 1.875em;
597 597 border: 0.25em solid transparent;
598 598 border-radius: 50%;
599 599 font-family: inherit;
600 600 line-height: 5em;
601 601 cursor: default;
602 602 -webkit-user-select: none;
603 603 -moz-user-select: none;
604 604 -ms-user-select: none;
605 605 user-select: none;
606 606 }
607 607 .swal2-icon .swal2-icon-content {
608 608 display: flex;
609 609 align-items: center;
610 610 font-size: 3.75em;
611 611 }
612 612 .swal2-icon.swal2-error {
613 613 border-color: @alert2;
614 614 color: @alert2;
615 615 }
616 616 .swal2-icon.swal2-error .swal2-x-mark {
617 617 position: relative;
618 618 flex-grow: 1;
619 619 }
620 620 .swal2-icon.swal2-error [class^=swal2-x-mark-line] {
621 621 display: block;
622 622 position: absolute;
623 623 top: 2.3125em;
624 624 width: 2.9375em;
625 625 height: 0.3125em;
626 626 border-radius: 0.125em;
627 627 background-color: @alert2;
628 628 }
629 629 .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=left] {
630 630 left: 1.0625em;
631 631 transform: rotate(45deg);
632 632 }
633 633 .swal2-icon.swal2-error [class^=swal2-x-mark-line][class$=right] {
634 634 right: 1em;
635 635 transform: rotate(-45deg);
636 636 }
637 637 .swal2-icon.swal2-error.swal2-icon-show {
638 638 //-webkit-animation: swal2-animate-error-icon 0.5s;
639 639 // animation: swal2-animate-error-icon 0.5s;
640 640 }
641 641 .swal2-icon.swal2-error.swal2-icon-show .swal2-x-mark {
642 642 //-webkit-animation: swal2-animate-error-x-mark 0.5s;
643 643 // animation: swal2-animate-error-x-mark 0.5s;
644 644 }
645 645 .swal2-icon.swal2-warning {
646 646 border-color: @alert3;
647 647 color: @alert3;
648 648 }
649 649 .swal2-icon.swal2-info {
650 650 border-color: #9de0f6;
651 651 color: #3fc3ee;
652 652 }
653 653 .swal2-icon.swal2-question {
654 654 border-color: #c9dae1;
655 655 color: #87adbd;
656 656 }
657 657 .swal2-icon.swal2-success {
658 658 border-color: @alert1;
659 659 color: @alert1;
660 660 }
661 661 .swal2-icon.swal2-success [class^=swal2-success-circular-line] {
662 662 position: absolute;
663 663 width: 3.75em;
664 664 height: 7.5em;
665 665 transform: rotate(45deg);
666 666 border-radius: 50%;
667 667 }
668 668 .swal2-icon.swal2-success [class^=swal2-success-circular-line][class$=left] {
669 669 top: -0.4375em;
670 670 left: -2.0635em;
671 671 transform: rotate(-45deg);
672 672 transform-origin: 3.75em 3.75em;
673 673 border-radius: 7.5em 0 0 7.5em;
674 674 }
675 675 .swal2-icon.swal2-success [class^=swal2-success-circular-line][class$=right] {
676 676 top: -0.6875em;
677 677 left: 1.875em;
678 678 transform: rotate(-45deg);
679 679 transform-origin: 0 3.75em;
680 680 border-radius: 0 7.5em 7.5em 0;
681 681 }
682 682 .swal2-icon.swal2-success .swal2-success-ring {
683 683 position: absolute;
684 684 z-index: 2;
685 685 top: -0.25em;
686 686 left: -0.25em;
687 687 box-sizing: content-box;
688 688 width: 100%;
689 689 height: 100%;
690 690 border: 0.25em solid rgba(165, 220, 134, 0.3);
691 691 border-radius: 50%;
692 692 }
693 693 .swal2-icon.swal2-success .swal2-success-fix {
694 694 position: absolute;
695 695 z-index: 1;
696 696 top: 0.5em;
697 697 left: 1.625em;
698 698 width: 0.4375em;
699 699 height: 5.625em;
700 700 transform: rotate(-45deg);
701 701 }
702 702 .swal2-icon.swal2-success [class^=swal2-success-line] {
703 703 display: block;
704 704 position: absolute;
705 705 z-index: 2;
706 706 height: 0.3125em;
707 707 border-radius: 0.125em;
708 708 background-color: @alert1;
709 709 }
710 710 .swal2-icon.swal2-success [class^=swal2-success-line][class$=tip] {
711 711 top: 2.875em;
712 712 left: 0.8125em;
713 713 width: 1.5625em;
714 714 transform: rotate(45deg);
715 715 }
716 716 .swal2-icon.swal2-success [class^=swal2-success-line][class$=long] {
717 717 top: 2.375em;
718 718 right: 0.5em;
719 719 width: 2.9375em;
720 720 transform: rotate(-45deg);
721 721 }
722 722 .swal2-icon.swal2-success.swal2-icon-show .swal2-success-line-tip {
723 723 -webkit-animation: swal2-animate-success-line-tip 0.75s;
724 724 animation: swal2-animate-success-line-tip 0.75s;
725 725 }
726 726 .swal2-icon.swal2-success.swal2-icon-show .swal2-success-line-long {
727 727 -webkit-animation: swal2-animate-success-line-long 0.75s;
728 728 animation: swal2-animate-success-line-long 0.75s;
729 729 }
730 730 .swal2-icon.swal2-success.swal2-icon-show .swal2-success-circular-line-right {
731 731 -webkit-animation: swal2-rotate-success-circular-line 4.25s ease-in;
732 732 animation: swal2-rotate-success-circular-line 4.25s ease-in;
733 733 }
734 734
735 735 .swal2-progress-steps {
736 736 align-items: center;
737 737 margin: 0 0 1.25em;
738 738 padding: 0;
739 739 background: inherit;
740 740 font-weight: 600;
741 741 }
742 742 .swal2-progress-steps li {
743 743 display: inline-block;
744 744 position: relative;
745 745 }
746 746 .swal2-progress-steps .swal2-progress-step {
747 747 z-index: 20;
748 748 width: 2em;
749 749 height: 2em;
750 750 border-radius: 2em;
751 751 background: #3085d6;
752 752 color: #fff;
753 753 line-height: 2em;
754 754 text-align: center;
755 755 }
756 756 .swal2-progress-steps .swal2-progress-step.swal2-active-progress-step {
757 757 background: #3085d6;
758 758 }
759 759 .swal2-progress-steps .swal2-progress-step.swal2-active-progress-step ~ .swal2-progress-step {
760 760 background: #add8e6;
761 761 color: #fff;
762 762 }
763 763 .swal2-progress-steps .swal2-progress-step.swal2-active-progress-step ~ .swal2-progress-step-line {
764 764 background: #add8e6;
765 765 }
766 766 .swal2-progress-steps .swal2-progress-step-line {
767 767 z-index: 10;
768 768 width: 2.5em;
769 769 height: 0.4em;
770 770 margin: 0 -1px;
771 771 background: #3085d6;
772 772 }
773 773
774 774 [class^=swal2] {
775 775 -webkit-tap-highlight-color: transparent;
776 776 }
777 777
778 778 .swal2-show {
779 779 -webkit-animation: swal2-show 0.3s;
780 780 animation: swal2-show 0.3s;
781 781 }
782 782
783 783 .swal2-hide {
784 784 -webkit-animation: swal2-hide 0.15s forwards;
785 785 animation: swal2-hide 0.15s forwards;
786 786 }
787 787
788 788 .swal2-noanimation {
789 789 transition: none;
790 790 }
791 791
792 792 .swal2-scrollbar-measure {
793 793 position: absolute;
794 794 top: -9999px;
795 795 width: 50px;
796 796 height: 50px;
797 797 overflow: scroll;
798 798 }
799 799
800 800 .swal2-rtl .swal2-close {
801 801 right: auto;
802 802 left: 0;
803 803 }
804 804 .swal2-rtl .swal2-timer-progress-bar {
805 805 right: 0;
806 806 left: auto;
807 807 }
808 808
809 809 @supports (-ms-accelerator: true) {
810 810 .swal2-range input {
811 811 width: 100% !important;
812 812 }
813 813 .swal2-range output {
814 814 display: none;
815 815 }
816 816 }
817 817 @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
818 818 .swal2-range input {
819 819 width: 100% !important;
820 820 }
821 821 .swal2-range output {
822 822 display: none;
823 823 }
824 824 }
825 825 @-moz-document url-prefix() {
826 826 .swal2-close:focus {
827 827 outline: 2px solid rgba(50, 100, 150, 0.4);
828 828 }
829 829 }
830 830 @-webkit-keyframes swal2-toast-show {
831 831 0% {
832 832 transform: translateY(-0.625em) rotateZ(2deg);
833 833 }
834 834 33% {
835 835 transform: translateY(0) rotateZ(-2deg);
836 836 }
837 837 66% {
838 838 transform: translateY(0.3125em) rotateZ(2deg);
839 839 }
840 840 100% {
841 841 transform: translateY(0) rotateZ(0deg);
842 842 }
843 843 }
844 844 @keyframes swal2-toast-show {
845 845 0% {
846 846 transform: translateY(-0.625em) rotateZ(2deg);
847 847 }
848 848 33% {
849 849 transform: translateY(0) rotateZ(-2deg);
850 850 }
851 851 66% {
852 852 transform: translateY(0.3125em) rotateZ(2deg);
853 853 }
854 854 100% {
855 855 transform: translateY(0) rotateZ(0deg);
856 856 }
857 857 }
858 858 @-webkit-keyframes swal2-toast-hide {
859 859 100% {
860 860 transform: rotateZ(1deg);
861 861 opacity: 0;
862 862 }
863 863 }
864 864 @keyframes swal2-toast-hide {
865 865 100% {
866 866 transform: rotateZ(1deg);
867 867 opacity: 0;
868 868 }
869 869 }
870 870 @-webkit-keyframes swal2-toast-animate-success-line-tip {
871 871 0% {
872 872 top: 0.5625em;
873 873 left: 0.0625em;
874 874 width: 0;
875 875 }
876 876 54% {
877 877 top: 0.125em;
878 878 left: 0.125em;
879 879 width: 0;
880 880 }
881 881 70% {
882 882 top: 0.625em;
883 883 left: -0.25em;
884 884 width: 1.625em;
885 885 }
886 886 84% {
887 887 top: 1.0625em;
888 888 left: 0.75em;
889 889 width: 0.5em;
890 890 }
891 891 100% {
892 892 top: 1.125em;
893 893 left: 0.1875em;
894 894 width: 0.75em;
895 895 }
896 896 }
897 897 @keyframes swal2-toast-animate-success-line-tip {
898 898 0% {
899 899 top: 0.5625em;
900 900 left: 0.0625em;
901 901 width: 0;
902 902 }
903 903 54% {
904 904 top: 0.125em;
905 905 left: 0.125em;
906 906 width: 0;
907 907 }
908 908 70% {
909 909 top: 0.625em;
910 910 left: -0.25em;
911 911 width: 1.625em;
912 912 }
913 913 84% {
914 914 top: 1.0625em;
915 915 left: 0.75em;
916 916 width: 0.5em;
917 917 }
918 918 100% {
919 919 top: 1.125em;
920 920 left: 0.1875em;
921 921 width: 0.75em;
922 922 }
923 923 }
924 924 @-webkit-keyframes swal2-toast-animate-success-line-long {
925 925 0% {
926 926 top: 1.625em;
927 927 right: 1.375em;
928 928 width: 0;
929 929 }
930 930 65% {
931 931 top: 1.25em;
932 932 right: 0.9375em;
933 933 width: 0;
934 934 }
935 935 84% {
936 936 top: 0.9375em;
937 937 right: 0;
938 938 width: 1.125em;
939 939 }
940 940 100% {
941 941 top: 0.9375em;
942 942 right: 0.1875em;
943 943 width: 1.375em;
944 944 }
945 945 }
946 946 @keyframes swal2-toast-animate-success-line-long {
947 947 0% {
948 948 top: 1.625em;
949 949 right: 1.375em;
950 950 width: 0;
951 951 }
952 952 65% {
953 953 top: 1.25em;
954 954 right: 0.9375em;
955 955 width: 0;
956 956 }
957 957 84% {
958 958 top: 0.9375em;
959 959 right: 0;
960 960 width: 1.125em;
961 961 }
962 962 100% {
963 963 top: 0.9375em;
964 964 right: 0.1875em;
965 965 width: 1.375em;
966 966 }
967 967 }
968 968 @-webkit-keyframes swal2-show {
969 969 0% {
970 970 transform: scale(0.7);
971 971 }
972 972 45% {
973 973 transform: scale(1.05);
974 974 }
975 975 80% {
976 976 transform: scale(0.95);
977 977 }
978 978 100% {
979 979 transform: scale(1);
980 980 }
981 981 }
982 982 @keyframes swal2-show {
983 983 0% {
984 984 transform: scale(0.7);
985 985 }
986 986 45% {
987 987 transform: scale(1.05);
988 988 }
989 989 80% {
990 990 transform: scale(0.95);
991 991 }
992 992 100% {
993 993 transform: scale(1);
994 994 }
995 995 }
996 996 @-webkit-keyframes swal2-hide {
997 997 0% {
998 998 transform: scale(1);
999 999 opacity: 1;
1000 1000 }
1001 1001 100% {
1002 1002 transform: scale(0.5);
1003 1003 opacity: 0;
1004 1004 }
1005 1005 }
1006 1006 @keyframes swal2-hide {
1007 1007 0% {
1008 1008 transform: scale(1);
1009 1009 opacity: 1;
1010 1010 }
1011 1011 100% {
1012 1012 transform: scale(0.5);
1013 1013 opacity: 0;
1014 1014 }
1015 1015 }
1016 1016 @-webkit-keyframes swal2-animate-success-line-tip {
1017 1017 0% {
1018 1018 top: 1.1875em;
1019 1019 left: 0.0625em;
1020 1020 width: 0;
1021 1021 }
1022 1022 54% {
1023 1023 top: 1.0625em;
1024 1024 left: 0.125em;
1025 1025 width: 0;
1026 1026 }
1027 1027 70% {
1028 1028 top: 2.1875em;
1029 1029 left: -0.375em;
1030 1030 width: 3.125em;
1031 1031 }
1032 1032 84% {
1033 1033 top: 3em;
1034 1034 left: 1.3125em;
1035 1035 width: 1.0625em;
1036 1036 }
1037 1037 100% {
1038 1038 top: 2.8125em;
1039 1039 left: 0.8125em;
1040 1040 width: 1.5625em;
1041 1041 }
1042 1042 }
1043 1043 @keyframes swal2-animate-success-line-tip {
1044 1044 0% {
1045 1045 top: 1.1875em;
1046 1046 left: 0.0625em;
1047 1047 width: 0;
1048 1048 }
1049 1049 54% {
1050 1050 top: 1.0625em;
1051 1051 left: 0.125em;
1052 1052 width: 0;
1053 1053 }
1054 1054 70% {
1055 1055 top: 2.1875em;
1056 1056 left: -0.375em;
1057 1057 width: 3.125em;
1058 1058 }
1059 1059 84% {
1060 1060 top: 3em;
1061 1061 left: 1.3125em;
1062 1062 width: 1.0625em;
1063 1063 }
1064 1064 100% {
1065 1065 top: 2.8125em;
1066 1066 left: 0.8125em;
1067 1067 width: 1.5625em;
1068 1068 }
1069 1069 }
1070 1070 @-webkit-keyframes swal2-animate-success-line-long {
1071 1071 0% {
1072 1072 top: 3.375em;
1073 1073 right: 2.875em;
1074 1074 width: 0;
1075 1075 }
1076 1076 65% {
1077 1077 top: 3.375em;
1078 1078 right: 2.875em;
1079 1079 width: 0;
1080 1080 }
1081 1081 84% {
1082 1082 top: 2.1875em;
1083 1083 right: 0;
1084 1084 width: 3.4375em;
1085 1085 }
1086 1086 100% {
1087 1087 top: 2.375em;
1088 1088 right: 0.5em;
1089 1089 width: 2.9375em;
1090 1090 }
1091 1091 }
1092 1092 @keyframes swal2-animate-success-line-long {
1093 1093 0% {
1094 1094 top: 3.375em;
1095 1095 right: 2.875em;
1096 1096 width: 0;
1097 1097 }
1098 1098 65% {
1099 1099 top: 3.375em;
1100 1100 right: 2.875em;
1101 1101 width: 0;
1102 1102 }
1103 1103 84% {
1104 1104 top: 2.1875em;
1105 1105 right: 0;
1106 1106 width: 3.4375em;
1107 1107 }
1108 1108 100% {
1109 1109 top: 2.375em;
1110 1110 right: 0.5em;
1111 1111 width: 2.9375em;
1112 1112 }
1113 1113 }
1114 1114 @-webkit-keyframes swal2-rotate-success-circular-line {
1115 1115 0% {
1116 1116 transform: rotate(-45deg);
1117 1117 }
1118 1118 5% {
1119 1119 transform: rotate(-45deg);
1120 1120 }
1121 1121 12% {
1122 1122 transform: rotate(-405deg);
1123 1123 }
1124 1124 100% {
1125 1125 transform: rotate(-405deg);
1126 1126 }
1127 1127 }
1128 1128 @keyframes swal2-rotate-success-circular-line {
1129 1129 0% {
1130 1130 transform: rotate(-45deg);
1131 1131 }
1132 1132 5% {
1133 1133 transform: rotate(-45deg);
1134 1134 }
1135 1135 12% {
1136 1136 transform: rotate(-405deg);
1137 1137 }
1138 1138 100% {
1139 1139 transform: rotate(-405deg);
1140 1140 }
1141 1141 }
1142 1142 @-webkit-keyframes swal2-animate-error-x-mark {
1143 1143 0% {
1144 1144 margin-top: 1.625em;
1145 1145 transform: scale(0.4);
1146 1146 opacity: 0;
1147 1147 }
1148 1148 50% {
1149 1149 margin-top: 1.625em;
1150 1150 transform: scale(0.4);
1151 1151 opacity: 0;
1152 1152 }
1153 1153 80% {
1154 1154 margin-top: -0.375em;
1155 1155 transform: scale(1.15);
1156 1156 }
1157 1157 100% {
1158 1158 margin-top: 0;
1159 1159 transform: scale(1);
1160 1160 opacity: 1;
1161 1161 }
1162 1162 }
1163 1163 @keyframes swal2-animate-error-x-mark {
1164 1164 0% {
1165 1165 margin-top: 1.625em;
1166 1166 transform: scale(0.4);
1167 1167 opacity: 0;
1168 1168 }
1169 1169 50% {
1170 1170 margin-top: 1.625em;
1171 1171 transform: scale(0.4);
1172 1172 opacity: 0;
1173 1173 }
1174 1174 80% {
1175 1175 margin-top: -0.375em;
1176 1176 transform: scale(1.15);
1177 1177 }
1178 1178 100% {
1179 1179 margin-top: 0;
1180 1180 transform: scale(1);
1181 1181 opacity: 1;
1182 1182 }
1183 1183 }
1184 1184 @-webkit-keyframes swal2-animate-error-icon {
1185 1185 0% {
1186 1186 transform: rotateX(100deg);
1187 1187 opacity: 0;
1188 1188 }
1189 1189 100% {
1190 1190 transform: rotateX(0deg);
1191 1191 opacity: 1;
1192 1192 }
1193 1193 }
1194 1194 @keyframes swal2-animate-error-icon {
1195 1195 0% {
1196 1196 transform: rotateX(100deg);
1197 1197 opacity: 0;
1198 1198 }
1199 1199 100% {
1200 1200 transform: rotateX(0deg);
1201 1201 opacity: 1;
1202 1202 }
1203 1203 }
1204 1204 @-webkit-keyframes swal2-rotate-loading {
1205 1205 0% {
1206 1206 transform: rotate(0deg);
1207 1207 }
1208 1208 100% {
1209 1209 transform: rotate(360deg);
1210 1210 }
1211 1211 }
1212 1212 @keyframes swal2-rotate-loading {
1213 1213 0% {
1214 1214 transform: rotate(0deg);
1215 1215 }
1216 1216 100% {
1217 1217 transform: rotate(360deg);
1218 1218 }
1219 1219 }
1220 1220 body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) {
1221 1221 overflow: hidden;
1222 1222 }
1223 1223 body.swal2-height-auto {
1224 1224 height: auto !important;
1225 1225 }
1226 1226 body.swal2-no-backdrop .swal2-container {
1227 1227 top: auto;
1228 1228 right: auto;
1229 1229 bottom: auto;
1230 1230 left: auto;
1231 1231 max-width: calc(100% - 0.625em * 2);
1232 1232 background-color: transparent !important;
1233 1233 }
1234 1234 body.swal2-no-backdrop .swal2-container > .swal2-modal {
1235 1235 box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
1236 1236 }
1237 1237 body.swal2-no-backdrop .swal2-container.swal2-top {
1238 1238 top: 0;
1239 1239 left: 50%;
1240 1240 transform: translateX(-50%);
1241 1241 }
1242 1242 body.swal2-no-backdrop .swal2-container.swal2-top-start, body.swal2-no-backdrop .swal2-container.swal2-top-left {
1243 1243 top: 0;
1244 1244 left: 0;
1245 1245 }
1246 1246 body.swal2-no-backdrop .swal2-container.swal2-top-end, body.swal2-no-backdrop .swal2-container.swal2-top-right {
1247 1247 top: 0;
1248 1248 right: 0;
1249 1249 }
1250 1250 body.swal2-no-backdrop .swal2-container.swal2-center {
1251 1251 top: 50%;
1252 1252 left: 50%;
1253 1253 transform: translate(-50%, -50%);
1254 1254 }
1255 1255 body.swal2-no-backdrop .swal2-container.swal2-center-start, body.swal2-no-backdrop .swal2-container.swal2-center-left {
1256 1256 top: 50%;
1257 1257 left: 0;
1258 1258 transform: translateY(-50%);
1259 1259 }
1260 1260 body.swal2-no-backdrop .swal2-container.swal2-center-end, body.swal2-no-backdrop .swal2-container.swal2-center-right {
1261 1261 top: 50%;
1262 1262 right: 0;
1263 1263 transform: translateY(-50%);
1264 1264 }
1265 1265 body.swal2-no-backdrop .swal2-container.swal2-bottom {
1266 1266 bottom: 0;
1267 1267 left: 50%;
1268 1268 transform: translateX(-50%);
1269 1269 }
1270 1270 body.swal2-no-backdrop .swal2-container.swal2-bottom-start, body.swal2-no-backdrop .swal2-container.swal2-bottom-left {
1271 1271 bottom: 0;
1272 1272 left: 0;
1273 1273 }
1274 1274 body.swal2-no-backdrop .swal2-container.swal2-bottom-end, body.swal2-no-backdrop .swal2-container.swal2-bottom-right {
1275 1275 right: 0;
1276 1276 bottom: 0;
1277 1277 }
1278 1278 @media print {
1279 1279 body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) {
1280 1280 overflow-y: scroll !important;
1281 1281 }
1282 1282 body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) > [aria-hidden=true] {
1283 1283 display: none;
1284 1284 }
1285 1285 body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) .swal2-container {
1286 1286 position: static !important;
1287 1287 }
1288 1288 }
1289 1289 body.swal2-toast-shown .swal2-container {
1290 1290 background-color: transparent;
1291 1291 }
1292 1292 body.swal2-toast-shown .swal2-container.swal2-top {
1293 1293 top: 0;
1294 1294 right: auto;
1295 1295 bottom: auto;
1296 1296 left: 50%;
1297 1297 transform: translateX(-50%);
1298 1298 }
1299 1299 body.swal2-toast-shown .swal2-container.swal2-top-end, body.swal2-toast-shown .swal2-container.swal2-top-right {
1300 1300 top: 0;
1301 1301 right: 0;
1302 1302 bottom: auto;
1303 1303 left: auto;
1304 1304 }
1305 1305 body.swal2-toast-shown .swal2-container.swal2-top-start, body.swal2-toast-shown .swal2-container.swal2-top-left {
1306 1306 top: 0;
1307 1307 right: auto;
1308 1308 bottom: auto;
1309 1309 left: 0;
1310 1310 }
1311 1311 body.swal2-toast-shown .swal2-container.swal2-center-start, body.swal2-toast-shown .swal2-container.swal2-center-left {
1312 1312 top: 50%;
1313 1313 right: auto;
1314 1314 bottom: auto;
1315 1315 left: 0;
1316 1316 transform: translateY(-50%);
1317 1317 }
1318 1318 body.swal2-toast-shown .swal2-container.swal2-center {
1319 1319 top: 50%;
1320 1320 right: auto;
1321 1321 bottom: auto;
1322 1322 left: 50%;
1323 1323 transform: translate(-50%, -50%);
1324 1324 }
1325 1325 body.swal2-toast-shown .swal2-container.swal2-center-end, body.swal2-toast-shown .swal2-container.swal2-center-right {
1326 1326 top: 50%;
1327 1327 right: 0;
1328 1328 bottom: auto;
1329 1329 left: auto;
1330 1330 transform: translateY(-50%);
1331 1331 }
1332 1332 body.swal2-toast-shown .swal2-container.swal2-bottom-start, body.swal2-toast-shown .swal2-container.swal2-bottom-left {
1333 1333 top: auto;
1334 1334 right: auto;
1335 1335 bottom: 0;
1336 1336 left: 0;
1337 1337 }
1338 1338 body.swal2-toast-shown .swal2-container.swal2-bottom {
1339 1339 top: auto;
1340 1340 right: auto;
1341 1341 bottom: 0;
1342 1342 left: 50%;
1343 1343 transform: translateX(-50%);
1344 1344 }
1345 1345 body.swal2-toast-shown .swal2-container.swal2-bottom-end, body.swal2-toast-shown .swal2-container.swal2-bottom-right {
1346 1346 top: auto;
1347 1347 right: 0;
1348 1348 bottom: 0;
1349 1349 left: auto;
1350 1350 }
1351 1351 body.swal2-toast-column .swal2-toast {
1352 1352 flex-direction: column;
1353 1353 align-items: stretch;
1354 1354 }
1355 1355 body.swal2-toast-column .swal2-toast .swal2-actions {
1356 1356 flex: 1;
1357 1357 align-self: stretch;
1358 1358 height: 2.2em;
1359 1359 margin-top: 0.3125em;
1360 1360 }
1361 1361 body.swal2-toast-column .swal2-toast .swal2-loading {
1362 1362 justify-content: center;
1363 1363 }
1364 1364 body.swal2-toast-column .swal2-toast .swal2-input {
1365 1365 height: 2em;
1366 1366 margin: 0.3125em auto;
1367 1367 font-size: 1em;
1368 1368 }
1369 1369 body.swal2-toast-column .swal2-toast .swal2-validation-message {
1370 1370 font-size: 1em;
1371 1371 } No newline at end of file
@@ -1,947 +1,955 b''
1 1 // # Copyright (C) 2010-2020 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(jqXHR, textStatus, errorThrown) {
351 351 var prefix = "Error while submitting comment.\n"
352 352 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
353 353 ajaxErrorSwal(message);
354 354 self.resetCommentFormState(text);
355 355 };
356 356 self.submitAjaxPOST(
357 357 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
358 358 };
359 359
360 360 this.previewSuccessCallback = function(o) {
361 361 $(self.previewBoxSelector).html(o);
362 362 $(self.previewBoxSelector).removeClass('unloaded');
363 363
364 364 // swap buttons, making preview active
365 365 $(self.previewButton).parent().addClass('active');
366 366 $(self.editButton).parent().removeClass('active');
367 367
368 368 // unlock buttons
369 369 self.setActionButtonsDisabled(false);
370 370 };
371 371
372 372 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
373 373 excludeCancelBtn = excludeCancelBtn || false;
374 374 submitEvent = submitEvent || false;
375 375
376 376 $(this.editButton).prop('disabled', state);
377 377 $(this.previewButton).prop('disabled', state);
378 378
379 379 if (!excludeCancelBtn) {
380 380 $(this.cancelButton).prop('disabled', state);
381 381 }
382 382
383 383 var submitState = state;
384 384 if (!submitEvent && this.getCommentStatus() && !self.isInline()) {
385 385 // if the value of commit review status is set, we allow
386 386 // submit button, but only on Main form, isInline means inline
387 387 submitState = false
388 388 }
389 389
390 390 $(this.submitButton).prop('disabled', submitState);
391 391 if (submitEvent) {
392 392 $(this.submitButton).val(_gettext('Submitting...'));
393 393 } else {
394 394 $(this.submitButton).val(this.submitButtonText);
395 395 }
396 396
397 397 };
398 398
399 399 // lock preview/edit/submit buttons on load, but exclude cancel button
400 400 var excludeCancelBtn = true;
401 401 this.setActionButtonsDisabled(true, excludeCancelBtn);
402 402
403 403 // anonymous users don't have access to initialized CM instance
404 404 if (this.cm !== undefined){
405 405 this.cm.on('change', function(cMirror) {
406 406 if (cMirror.getValue() === "") {
407 407 self.setActionButtonsDisabled(true, excludeCancelBtn)
408 408 } else {
409 409 self.setActionButtonsDisabled(false, excludeCancelBtn)
410 410 }
411 411 });
412 412 }
413 413
414 414 $(this.editButton).on('click', function(e) {
415 415 e.preventDefault();
416 416
417 417 $(self.previewButton).parent().removeClass('active');
418 418 $(self.previewContainer).hide();
419 419
420 420 $(self.editButton).parent().addClass('active');
421 421 $(self.editContainer).show();
422 422
423 423 });
424 424
425 425 $(this.previewButton).on('click', function(e) {
426 426 e.preventDefault();
427 427 var text = self.cm.getValue();
428 428
429 429 if (text === "") {
430 430 return;
431 431 }
432 432
433 433 var postData = {
434 434 'text': text,
435 435 'renderer': templateContext.visual.default_renderer,
436 436 'csrf_token': CSRF_TOKEN
437 437 };
438 438
439 439 // lock ALL buttons on preview
440 440 self.setActionButtonsDisabled(true);
441 441
442 442 $(self.previewBoxSelector).addClass('unloaded');
443 443 $(self.previewBoxSelector).html(_gettext('Loading ...'));
444 444
445 445 $(self.editContainer).hide();
446 446 $(self.previewContainer).show();
447 447
448 448 // by default we reset state of comment preserving the text
449 449 var previewFailCallback = function(jqXHR, textStatus, errorThrown) {
450 450 var prefix = "Error while preview of comment.\n"
451 451 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
452 452 ajaxErrorSwal(message);
453 453
454 454 self.resetCommentFormState(text)
455 455 };
456 456 self.submitAjaxPOST(
457 457 self.previewUrl, postData, self.previewSuccessCallback,
458 458 previewFailCallback);
459 459
460 460 $(self.previewButton).parent().addClass('active');
461 461 $(self.editButton).parent().removeClass('active');
462 462 });
463 463
464 464 $(this.submitForm).submit(function(e) {
465 465 e.preventDefault();
466 466 var allowedToSubmit = self.isAllowedToSubmit();
467 467 if (!allowedToSubmit){
468 468 return false;
469 469 }
470 470 self.handleFormSubmit();
471 471 });
472 472
473 473 }
474 474
475 475 return CommentForm;
476 476 });
477 477
478 478 /* comments controller */
479 479 var CommentsController = function() {
480 480 var mainComment = '#text';
481 481 var self = this;
482 482
483 483 this.cancelComment = function(node) {
484 484 var $node = $(node);
485 485 var $td = $node.closest('td');
486 486 $node.closest('.comment-inline-form').remove();
487 487 return false;
488 488 };
489 489
490 490 this.getLineNumber = function(node) {
491 491 var $node = $(node);
492 492 var lineNo = $node.closest('td').attr('data-line-no');
493 493 if (lineNo === undefined && $node.data('commentInline')){
494 494 lineNo = $node.data('commentLineNo')
495 495 }
496 496
497 497 return lineNo
498 498 };
499 499
500 500 this.scrollToComment = function(node, offset, outdated) {
501 501 if (offset === undefined) {
502 502 offset = 0;
503 503 }
504 504 var outdated = outdated || false;
505 505 var klass = outdated ? 'div.comment-outdated' : 'div.comment-current';
506 506
507 507 if (!node) {
508 508 node = $('.comment-selected');
509 509 if (!node.length) {
510 510 node = $('comment-current')
511 511 }
512 512 }
513 513
514 514 $wrapper = $(node).closest('div.comment');
515 515
516 516 // show hidden comment when referenced.
517 517 if (!$wrapper.is(':visible')){
518 518 $wrapper.show();
519 519 }
520 520
521 521 $comment = $(node).closest(klass);
522 522 $comments = $(klass);
523 523
524 524 $('.comment-selected').removeClass('comment-selected');
525 525
526 526 var nextIdx = $(klass).index($comment) + offset;
527 527 if (nextIdx >= $comments.length) {
528 528 nextIdx = 0;
529 529 }
530 530 var $next = $(klass).eq(nextIdx);
531 531
532 532 var $cb = $next.closest('.cb');
533 533 $cb.removeClass('cb-collapsed');
534 534
535 535 var $filediffCollapseState = $cb.closest('.filediff').prev();
536 536 $filediffCollapseState.prop('checked', false);
537 537 $next.addClass('comment-selected');
538 538 scrollToElement($next);
539 539 return false;
540 540 };
541 541
542 542 this.nextComment = function(node) {
543 543 return self.scrollToComment(node, 1);
544 544 };
545 545
546 546 this.prevComment = function(node) {
547 547 return self.scrollToComment(node, -1);
548 548 };
549 549
550 550 this.nextOutdatedComment = function(node) {
551 551 return self.scrollToComment(node, 1, true);
552 552 };
553 553
554 554 this.prevOutdatedComment = function(node) {
555 555 return self.scrollToComment(node, -1, true);
556 556 };
557 557
558 558 this._deleteComment = function(node) {
559 559 var $node = $(node);
560 560 var $td = $node.closest('td');
561 561 var $comment = $node.closest('.comment');
562 562 var comment_id = $comment.attr('data-comment-id');
563 563 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
564 564 var postData = {
565 565 'csrf_token': CSRF_TOKEN
566 566 };
567 567
568 568 $comment.addClass('comment-deleting');
569 569 $comment.hide('fast');
570 570
571 571 var success = function(response) {
572 572 $comment.remove();
573 573 return false;
574 574 };
575 575 var failure = function(jqXHR, textStatus, errorThrown) {
576 576 var prefix = "Error while deleting this comment.\n"
577 577 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
578 578 ajaxErrorSwal(message);
579 579
580 580 $comment.show('fast');
581 581 $comment.removeClass('comment-deleting');
582 582 return false;
583 583 };
584 584 ajaxPOST(url, postData, success, failure);
585 585 }
586 586
587 587 this.deleteComment = function(node) {
588 588 var $comment = $(node).closest('.comment');
589 589 var comment_id = $comment.attr('data-comment-id');
590 590
591 591 Swal.fire({
592 592 title: 'Delete this comment?',
593 593 icon: 'warning',
594 594 showCancelButton: true,
595 595 confirmButtonColor: '#84a5d2',
596 596 cancelButtonColor: '#e85e4d',
597 confirmButtonText: _gettext('Yes, delete comment #{0}!').format(comment_id)
597 confirmButtonText: _gettext('Yes, delete comment #{0}!').format(comment_id),
598 showClass: {
599 popup: 'swal2-noanimation',
600 backdrop: 'swal2-noanimation'
601 },
602 hideClass: {
603 popup: '',
604 backdrop: ''
605 }
598 606 }).then(function(result) {
599 607 if (result.value) {
600 608 self._deleteComment(node);
601 609 }
602 610 })
603 611 };
604 612
605 613 this.toggleWideMode = function (node) {
606 614 if ($('#content').hasClass('wrapper')) {
607 615 $('#content').removeClass("wrapper");
608 616 $('#content').addClass("wide-mode-wrapper");
609 617 $(node).addClass('btn-success');
610 618 return true
611 619 } else {
612 620 $('#content').removeClass("wide-mode-wrapper");
613 621 $('#content').addClass("wrapper");
614 622 $(node).removeClass('btn-success');
615 623 return false
616 624 }
617 625
618 626 };
619 627
620 628 this.toggleComments = function(node, show) {
621 629 var $filediff = $(node).closest('.filediff');
622 630 if (show === true) {
623 631 $filediff.removeClass('hide-comments');
624 632 } else if (show === false) {
625 633 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
626 634 $filediff.addClass('hide-comments');
627 635 } else {
628 636 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
629 637 $filediff.toggleClass('hide-comments');
630 638 }
631 639 return false;
632 640 };
633 641
634 642 this.toggleLineComments = function(node) {
635 643 self.toggleComments(node, true);
636 644 var $node = $(node);
637 645 // mark outdated comments as visible before the toggle;
638 646 $(node.closest('tr')).find('.comment-outdated').show();
639 647 $node.closest('tr').toggleClass('hide-line-comments');
640 648 };
641 649
642 650 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId){
643 651 var pullRequestId = templateContext.pull_request_data.pull_request_id;
644 652 var commitId = templateContext.commit_data.commit_id;
645 653
646 654 var commentForm = new CommentForm(
647 655 formElement, commitId, pullRequestId, lineno, initAutocompleteActions, resolvesCommentId);
648 656 var cm = commentForm.getCmInstance();
649 657
650 658 if (resolvesCommentId){
651 659 var placeholderText = _gettext('Leave a resolution comment, or click resolve button to resolve TODO comment #{0}').format(resolvesCommentId);
652 660 }
653 661
654 662 setTimeout(function() {
655 663 // callbacks
656 664 if (cm !== undefined) {
657 665 commentForm.setPlaceholder(placeholderText);
658 666 if (commentForm.isInline()) {
659 667 cm.focus();
660 668 cm.refresh();
661 669 }
662 670 }
663 671 }, 10);
664 672
665 673 // trigger scrolldown to the resolve comment, since it might be away
666 674 // from the clicked
667 675 if (resolvesCommentId){
668 676 var actionNode = $(commentForm.resolvesActionId).offset();
669 677
670 678 setTimeout(function() {
671 679 if (actionNode) {
672 680 $('body, html').animate({scrollTop: actionNode.top}, 10);
673 681 }
674 682 }, 100);
675 683 }
676 684
677 685 // add dropzone support
678 686 var insertAttachmentText = function (cm, attachmentName, attachmentStoreUrl, isRendered) {
679 687 var renderer = templateContext.visual.default_renderer;
680 688 if (renderer == 'rst') {
681 689 var attachmentUrl = '`#{0} <{1}>`_'.format(attachmentName, attachmentStoreUrl);
682 690 if (isRendered){
683 691 attachmentUrl = '\n.. image:: {0}'.format(attachmentStoreUrl);
684 692 }
685 693 } else if (renderer == 'markdown') {
686 694 var attachmentUrl = '[{0}]({1})'.format(attachmentName, attachmentStoreUrl);
687 695 if (isRendered){
688 696 attachmentUrl = '!' + attachmentUrl;
689 697 }
690 698 } else {
691 699 var attachmentUrl = '{}'.format(attachmentStoreUrl);
692 700 }
693 701 cm.replaceRange(attachmentUrl+'\n', CodeMirror.Pos(cm.lastLine()));
694 702
695 703 return false;
696 704 };
697 705
698 706 //see: https://www.dropzonejs.com/#configuration
699 707 var storeUrl = pyroutes.url('repo_commit_comment_attachment_upload',
700 708 {'repo_name': templateContext.repo_name,
701 709 'commit_id': templateContext.commit_data.commit_id})
702 710
703 711 var previewTmpl = $(formElement).find('.comment-attachment-uploader-template').get(0);
704 712 if (previewTmpl !== undefined){
705 713 var selectLink = $(formElement).find('.pick-attachment').get(0);
706 714 $(formElement).find('.comment-attachment-uploader').dropzone({
707 715 url: storeUrl,
708 716 headers: {"X-CSRF-Token": CSRF_TOKEN},
709 717 paramName: function () {
710 718 return "attachment"
711 719 }, // The name that will be used to transfer the file
712 720 clickable: selectLink,
713 721 parallelUploads: 1,
714 722 maxFiles: 10,
715 723 maxFilesize: templateContext.attachment_store.max_file_size_mb,
716 724 uploadMultiple: false,
717 725 autoProcessQueue: true, // if false queue will not be processed automatically.
718 726 createImageThumbnails: false,
719 727 previewTemplate: previewTmpl.innerHTML,
720 728
721 729 accept: function (file, done) {
722 730 done();
723 731 },
724 732 init: function () {
725 733
726 734 this.on("sending", function (file, xhr, formData) {
727 735 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').hide();
728 736 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').show();
729 737 });
730 738
731 739 this.on("success", function (file, response) {
732 740 $(formElement).find('.comment-attachment-uploader').find('.dropzone-text').show();
733 741 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
734 742
735 743 var isRendered = false;
736 744 var ext = file.name.split('.').pop();
737 745 var imageExts = templateContext.attachment_store.image_ext;
738 746 if (imageExts.indexOf(ext) !== -1){
739 747 isRendered = true;
740 748 }
741 749
742 750 insertAttachmentText(cm, file.name, response.repo_fqn_access_path, isRendered)
743 751 });
744 752
745 753 this.on("error", function (file, errorMessage, xhr) {
746 754 $(formElement).find('.comment-attachment-uploader').find('.dropzone-upload').hide();
747 755
748 756 var error = null;
749 757
750 758 if (xhr !== undefined){
751 759 var httpStatus = xhr.status + " " + xhr.statusText;
752 760 if (xhr !== undefined && xhr.status >= 500) {
753 761 error = httpStatus;
754 762 }
755 763 }
756 764
757 765 if (error === null) {
758 766 error = errorMessage.error || errorMessage || httpStatus;
759 767 }
760 768 $(file.previewElement).find('.dz-error-message').html('ERROR: {0}'.format(error));
761 769
762 770 });
763 771 }
764 772 });
765 773 }
766 774 return commentForm;
767 775 };
768 776
769 777 this.createGeneralComment = function (lineNo, placeholderText, resolvesCommentId) {
770 778
771 779 var tmpl = $('#cb-comment-general-form-template').html();
772 780 tmpl = tmpl.format(null, 'general');
773 781 var $form = $(tmpl);
774 782
775 783 var $formPlaceholder = $('#cb-comment-general-form-placeholder');
776 784 var curForm = $formPlaceholder.find('form');
777 785 if (curForm){
778 786 curForm.remove();
779 787 }
780 788 $formPlaceholder.append($form);
781 789
782 790 var _form = $($form[0]);
783 791 var autocompleteActions = ['approve', 'reject', 'as_note', 'as_todo'];
784 792 var commentForm = this.createCommentForm(
785 793 _form, lineNo, placeholderText, autocompleteActions, resolvesCommentId);
786 794 commentForm.initStatusChangeSelector();
787 795
788 796 return commentForm;
789 797 };
790 798
791 799 this.createComment = function(node, resolutionComment) {
792 800 var resolvesCommentId = resolutionComment || null;
793 801 var $node = $(node);
794 802 var $td = $node.closest('td');
795 803 var $form = $td.find('.comment-inline-form');
796 804
797 805 if (!$form.length) {
798 806
799 807 var $filediff = $node.closest('.filediff');
800 808 $filediff.removeClass('hide-comments');
801 809 var f_path = $filediff.attr('data-f-path');
802 810 var lineno = self.getLineNumber(node);
803 811 // create a new HTML from template
804 812 var tmpl = $('#cb-comment-inline-form-template').html();
805 813 tmpl = tmpl.format(escapeHtml(f_path), lineno);
806 814 $form = $(tmpl);
807 815
808 816 var $comments = $td.find('.inline-comments');
809 817 if (!$comments.length) {
810 818 $comments = $(
811 819 $('#cb-comments-inline-container-template').html());
812 820 $td.append($comments);
813 821 }
814 822
815 823 $td.find('.cb-comment-add-button').before($form);
816 824
817 825 var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno);
818 826 var _form = $($form[0]).find('form');
819 827 var autocompleteActions = ['as_note', 'as_todo'];
820 828 var commentForm = this.createCommentForm(
821 829 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId);
822 830
823 831 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
824 832 form: _form,
825 833 parent: $td[0],
826 834 lineno: lineno,
827 835 f_path: f_path}
828 836 );
829 837
830 838 // set a CUSTOM submit handler for inline comments.
831 839 commentForm.setHandleFormSubmit(function(o) {
832 840 var text = commentForm.cm.getValue();
833 841 var commentType = commentForm.getCommentType();
834 842 var resolvesCommentId = commentForm.getResolvesId();
835 843
836 844 if (text === "") {
837 845 return;
838 846 }
839 847
840 848 if (lineno === undefined) {
841 849 alert('missing line !');
842 850 return;
843 851 }
844 852 if (f_path === undefined) {
845 853 alert('missing file path !');
846 854 return;
847 855 }
848 856
849 857 var excludeCancelBtn = false;
850 858 var submitEvent = true;
851 859 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
852 860 commentForm.cm.setOption("readOnly", true);
853 861 var postData = {
854 862 'text': text,
855 863 'f_path': f_path,
856 864 'line': lineno,
857 865 'comment_type': commentType,
858 866 'csrf_token': CSRF_TOKEN
859 867 };
860 868 if (resolvesCommentId){
861 869 postData['resolves_comment_id'] = resolvesCommentId;
862 870 }
863 871
864 872 var submitSuccessCallback = function(json_data) {
865 873 $form.remove();
866 874 try {
867 875 var html = json_data.rendered_text;
868 876 var lineno = json_data.line_no;
869 877 var target_id = json_data.target_id;
870 878
871 879 $comments.find('.cb-comment-add-button').before(html);
872 880
873 881 //mark visually which comment was resolved
874 882 if (resolvesCommentId) {
875 883 commentForm.markCommentResolved(resolvesCommentId);
876 884 }
877 885
878 886 // run global callback on submit
879 887 commentForm.globalSubmitSuccessCallback();
880 888
881 889 } catch (e) {
882 890 console.error(e);
883 891 }
884 892
885 893 // re trigger the linkification of next/prev navigation
886 894 linkifyComments($('.inline-comment-injected'));
887 895 timeagoActivate();
888 896 tooltipActivate();
889 897
890 898 if (window.updateSticky !== undefined) {
891 899 // potentially our comments change the active window size, so we
892 900 // notify sticky elements
893 901 updateSticky()
894 902 }
895 903
896 904 commentForm.setActionButtonsDisabled(false);
897 905
898 906 };
899 907 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
900 908 var prefix = "Error while submitting comment.\n"
901 909 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
902 910 ajaxErrorSwal(message);
903 911 commentForm.resetCommentFormState(text)
904 912 };
905 913 commentForm.submitAjaxPOST(
906 914 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
907 915 });
908 916 }
909 917
910 918 $form.addClass('comment-inline-form-open');
911 919 };
912 920
913 921 this.createResolutionComment = function(commentId){
914 922 // hide the trigger text
915 923 $('#resolve-comment-{0}'.format(commentId)).hide();
916 924
917 925 var comment = $('#comment-'+commentId);
918 926 var commentData = comment.data();
919 927 if (commentData.commentInline) {
920 928 this.createComment(comment, commentId)
921 929 } else {
922 930 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
923 931 }
924 932
925 933 return false;
926 934 };
927 935
928 936 this.submitResolution = function(commentId){
929 937 var form = $('#resolve_comment_{0}'.format(commentId)).closest('form');
930 938 var commentForm = form.get(0).CommentForm;
931 939
932 940 var cm = commentForm.getCmInstance();
933 941 var renderer = templateContext.visual.default_renderer;
934 942 if (renderer == 'rst'){
935 943 var commentUrl = '`#{0} <{1}#comment-{0}>`_'.format(commentId, commentForm.selfUrl);
936 944 } else if (renderer == 'markdown') {
937 945 var commentUrl = '[#{0}]({1}#comment-{0})'.format(commentId, commentForm.selfUrl);
938 946 } else {
939 947 var commentUrl = '{1}#comment-{0}'.format(commentId, commentForm.selfUrl);
940 948 }
941 949
942 950 cm.setValue(_gettext('TODO from comment {0} was fixed.').format(commentUrl));
943 951 form.submit();
944 952 return false;
945 953 };
946 954
947 955 };
@@ -1,123 +1,159 b''
1 1 // # Copyright (C) 2010-2020 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 /**
20 20 * turns objects into GET query string
21 21 */
22 22 var toQueryString = function(o) {
23 23 if(typeof o === 'string') {
24 24 return o;
25 25 }
26 26 if(typeof o !== 'object') {
27 27 return false;
28 28 }
29 29 var _p, _qs = [];
30 30 for(_p in o) {
31 31 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
32 32 }
33 33 return _qs.join('&');
34 34 };
35 35
36 36 /**
37 37 * ajax call wrappers
38 38 */
39 39
40 40 var ajaxGET = function (url, success, failure) {
41 41 var sUrl = url;
42 42 var request = $.ajax({
43 43 url: sUrl,
44 44 headers: {'X-PARTIAL-XHR': true}
45 45 })
46 46 .done(function (data) {
47 47 success(data);
48 48 })
49 49 .fail(function (jqXHR, textStatus, errorThrown) {
50 50 if (failure) {
51 51 failure(jqXHR, textStatus, errorThrown);
52 52 } else {
53 53 var message = formatErrorMessage(jqXHR, textStatus, errorThrown);
54 54 ajaxErrorSwal(message);
55 55 }
56 56 });
57 57 return request;
58 58 };
59 59
60 60 var ajaxPOST = function (url, postData, success, failure) {
61 61 var sUrl = url;
62 62 var postData = toQueryString(postData);
63 63 var request = $.ajax({
64 64 type: 'POST',
65 65 url: sUrl,
66 66 data: postData,
67 67 headers: {'X-PARTIAL-XHR': true}
68 68 })
69 69 .done(function (data) {
70 70 success(data);
71 71 })
72 72 .fail(function (jqXHR, textStatus, errorThrown) {
73 73 if (failure) {
74 74 failure(jqXHR, textStatus, errorThrown);
75 75 } else {
76 76 var message = formatErrorMessage(jqXHR, textStatus, errorThrown);
77 77 ajaxErrorSwal(message);
78 78 }
79 79 });
80 80 return request;
81 81 };
82 82
83 83 function formatErrorMessage(jqXHR, textStatus, errorThrown, prefix) {
84 84 if(typeof prefix === "undefined") {
85 85 prefix = ''
86 86 }
87 87
88 88 if (jqXHR.status === 0) {
89 89 return (prefix + 'Not connected.\nPlease verify your network connection.');
90 90 } else if (jqXHR.status == 401) {
91 91 return (prefix + 'Unauthorized access. [401]');
92 92 } else if (jqXHR.status == 404) {
93 93 return (prefix + 'The requested page not found. [404]');
94 94 } else if (jqXHR.status == 500) {
95 95 return (prefix + 'Internal Server Error [500].');
96 96 } else if (jqXHR.status == 503) {
97 97 return (prefix + 'Service unavailable [503].');
98 98 } else if (errorThrown === 'parsererror') {
99 99 return (prefix + 'Requested JSON parse failed.');
100 100 } else if (errorThrown === 'timeout') {
101 101 return (prefix + 'Time out error.');
102 102 } else if (errorThrown === 'abort') {
103 103 return (prefix + 'Ajax request aborted.');
104 104 } else {
105 105 return (prefix + 'Uncaught Error.\n' + jqXHR.responseText);
106 106 }
107 107 }
108 108
109 109 function ajaxErrorSwal(message) {
110 110 Swal.fire({
111 111 icon: 'error',
112 112 title: _gettext('Ajax Error'),
113 113 html: '<span style="white-space: pre-line">{0}</span>'.format(message),
114 114 showClass: {
115 115 popup: 'swal2-noanimation',
116 116 backdrop: 'swal2-noanimation'
117 117 },
118 118 hideClass: {
119 119 popup: '',
120 120 backdrop: ''
121 121 }
122 122 })
123 }
124
125 /*
126 * use in onclick attributes e.g
127 * onclick="submitConfirm(event, this, _gettext('Confirm to delete '), _gettext('Confirm Delete'), 'what we delete')">
128 * */
129 function submitConfirm(event, self, question, confirmText, htmlText) {
130 if (htmlText === "undefined") {
131 htmlText = null;
132 }
133 if (confirmText === "undefined") {
134 confirmText = _gettext('Delete')
135 }
136 event.preventDefault();
137
138 Swal.fire({
139 title: question,
140 icon: 'warning',
141 html: htmlText,
142 showClass: {
143 popup: 'swal2-noanimation',
144 backdrop: 'swal2-noanimation'
145 },
146 hideClass: {
147 popup: '',
148 backdrop: ''
149 },
150 showCancelButton: true,
151 confirmButtonColor: '#84a5d2',
152 cancelButtonColor: '#e85e4d',
153 confirmButtonText: confirmText
154 }).then(function(result) {
155 if (result.value) {
156 $(self).closest("form").submit();
157 }
158 })
123 159 } No newline at end of file
@@ -1,110 +1,113 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.mako"/>
3 3 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
4 4
5 5 <%def name="robots()">
6 6 %if c.gist.gist_type != 'public':
7 7 <meta name="robots" content="noindex, nofollow">
8 8 %else:
9 9 ${parent.robots()}
10 10 %endif
11 11 </%def>
12 12
13 13 <%def name="title()">
14 14 ${_('Gist')} &middot; ${c.gist.gist_access_id}
15 15 %if c.rhodecode_name:
16 16 &middot; ${h.branding(c.rhodecode_name)}
17 17 %endif
18 18 </%def>
19 19
20 20 <%def name="breadcrumbs_links()">
21 21 ${_('Gist')} &middot; ${c.gist.gist_access_id}
22 22 </%def>
23 23
24 24 <%def name="menu_bar_nav()">
25 25 ${self.menu_items(active='gists')}
26 26 </%def>
27 27
28 28 <%def name="main()">
29 29 <div class="box">
30 30 <!-- box / title -->
31 31
32 32 <div class="table">
33 33 <div id="files_data">
34 34 <div id="codeblock" class="codeblock">
35 35 <div class="code-header">
36 36 <div class="gist_url">
37 37 <div class="pull-left">
38 38 <code>
39 39 ${dt.gist_type(c.gist.gist_type)}
40 40 <span class="tag disabled">${c.gist.gist_access_id}</span>
41 41 ${c.gist.gist_url()} <span class="icon-clipboard clipboard-action" data-clipboard-text="${c.gist.gist_url()}" title="${_('Copy the url')}"></span>
42 42 </code>
43 43 </div>
44 44
45 45 <div class="pull-right buttons">
46 46 ## only owner should see that
47 47 <a href="#copySource" onclick="return false;" class="btn btn-mini icon-clipboard no-grey clipboard-action" data-clipboard-text="${c.files[0].content}">${_('Copy content')}</a>
48 48
49 49 %if c.is_super_admin or c.gist.gist_owner == c.rhodecode_user.user_id:
50 50 ${h.link_to(_('Edit'), h.route_path('gist_edit', gist_id=c.gist.gist_access_id), class_="btn btn-mini")}
51 51 %endif
52 52 ${h.link_to(_('Show as Raw'), h.route_path('gist_show_formatted', gist_id=c.gist.gist_access_id, revision='tip', format='raw'), class_="btn btn-mini")}
53 53
54 54 %if c.is_super_admin or c.gist.gist_owner == c.rhodecode_user.user_id:
55 55 <div class="pull-right remove_gist">
56 56 ${h.secure_form(h.route_path('gist_delete', gist_id=c.gist.gist_access_id), request=request)}
57 ${h.submit('remove_gist', _('Delete'),class_="btn btn-mini btn-danger",onclick="return confirm('"+_('Confirm to delete this Gist')+"');")}
57 <input class="btn btn-mini btn-danger" id="remove_gist" name="remove_gist"
58 onclick="submitConfirm(event, this, _gettext('Confirm to delete this gist'), _gettext('Delete'), '${c.gist.gist_access_id}')"
59 type="submit" value="${_('Delete')}"
60 >
58 61 ${h.end_form()}
59 62 </div>
60 63 %endif
61 64 </div>
62 65 </div>
63 66
64 67 <div class="gist-desc">
65 68 <code>${c.gist.gist_description}</code>
66 69 </div>
67 70
68 71 <div class="author">
69 72 <div title="${h.tooltip(c.file_last_commit.author)}">
70 73 ${self.gravatar_with_user(c.file_last_commit.author, 16, tooltip=True)} - ${_('created')} ${h.age_component(c.file_last_commit.date)},
71 74 ${_('expires')}:
72 75 %if c.gist.gist_expires == -1:
73 76 ${_('never')}
74 77 %else:
75 78 ${h.age_component(h.time_to_utcdatetime(c.gist.gist_expires))}
76 79 %endif
77 80 </span>
78 81 </div>
79 82
80 83 </div>
81 84 <div class="commit">${h.urlify_commit_message(c.file_last_commit.message, None)}</div>
82 85 </div>
83 86
84 87 ## iterate over the files
85 88 % for gist_file in c.files:
86 89 <% renderer = c.render and h.renderer_from_filename(gist_file.path, exclude=['.txt', '.TXT'])%>
87 90 <!--
88 91 <div id="${h.FID('G', gist_file.path)}" class="stats" >
89 92 <a href="${c.gist.gist_url()}">ΒΆ</a>
90 93 <b >${gist_file.path}</b>
91 94 <div>
92 95 ${h.link_to(_('Show as raw'), h.route_path('gist_show_formatted_path', gist_id=c.gist.gist_access_id, revision=gist_file.commit.raw_id, format='raw', f_path=gist_file.path), class_="btn btn-mini")}
93 96 </div>
94 97 </div>
95 98 -->
96 99 <div class="code-body textarea text-area editor">
97 100 %if renderer:
98 101 ${h.render(gist_file.content, renderer=renderer)}
99 102 %else:
100 103 ${h.pygmentize(gist_file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
101 104 %endif
102 105 </div>
103 106 %endfor
104 107 </div>
105 108 </div>
106 109 </div>
107 110
108 111
109 112 </div>
110 113 </%def>
@@ -1,190 +1,191 b''
1 1 <div class="panel panel-default">
2 2 <div class="panel-heading">
3 3 <h3 class="panel-title">${_('Authentication Tokens')}</h3>
4 4 </div>
5 5 <div class="panel-body">
6 6 <div class="apikeys_wrap">
7 7 <p>
8 8 ${_('Authentication tokens can be used to interact with the API, or VCS-over-http. '
9 9 'Each token can have a role. Token with a role can be used only in given context, '
10 10 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')}
11 11 </p>
12 12 <table class="rctable auth_tokens">
13 13 <tr>
14 14 <th>${_('Token')}</th>
15 15 <th>${_('Description')}</th>
16 16 <th>${_('Role')}</th>
17 17 <th>${_('Repository Scope')}</th>
18 18 <th>${_('Expiration')}</th>
19 19 <th>${_('Action')}</th>
20 20 </tr>
21 21 %if c.user_auth_tokens:
22 22 %for auth_token in c.user_auth_tokens:
23 23 <tr class="${('expired' if auth_token.expired else '')}">
24 24 <td class="truncate-wrap td-authtoken">
25 25 <div class="user_auth_tokens truncate autoexpand">
26 26 <code>${auth_token.api_key}</code>
27 27 </div>
28 28 </td>
29 29 <td class="td-wrap">${auth_token.description}</td>
30 30 <td class="td-tags">
31 31 <span class="tag disabled">${auth_token.role_humanized}</span>
32 32 </td>
33 33 <td class="td">${auth_token.scope_humanized}</td>
34 34 <td class="td-exp">
35 35 %if auth_token.expires == -1:
36 36 ${_('never')}
37 37 %else:
38 38 %if auth_token.expired:
39 39 <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span>
40 40 %else:
41 41 ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
42 42 %endif
43 43 %endif
44 44 </td>
45 45 <td class="td-action">
46 46 ${h.secure_form(h.route_path('my_account_auth_tokens_delete'), request=request)}
47 47 ${h.hidden('del_auth_token', auth_token.user_api_key_id)}
48 48 <button class="btn btn-link btn-danger" type="submit"
49 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.token_obfuscated}');">
49 onclick="submitConfirm(event, this, _gettext('Confirm to delete this auth token'), _gettext('Delete'), '${auth_token.token_obfuscated}')"
50 >
50 51 ${_('Delete')}
51 52 </button>
52 53 ${h.end_form()}
53 54 </td>
54 55 </tr>
55 56 %endfor
56 57 %else:
57 58 <tr><td><div class="ip">${_('No additional auth tokens specified')}</div></td></tr>
58 59 %endif
59 60 </table>
60 61 </div>
61 62
62 63 <div class="user_auth_tokens">
63 64 ${h.secure_form(h.route_path('my_account_auth_tokens_add'), request=request)}
64 65 <div class="form form-vertical">
65 66 <!-- fields -->
66 67 <div class="fields">
67 68 <div class="field">
68 69 <div class="label">
69 70 <label for="new_email">${_('New authentication token')}:</label>
70 71 </div>
71 72 <div class="input">
72 73 ${h.text('description', class_='medium', placeholder=_('Description'))}
73 74 ${h.hidden('lifetime')}
74 75 ${h.select('role', request.GET.get('token_role', ''), c.role_options)}
75 76
76 77 % if c.allow_scoped_tokens:
77 78 ${h.hidden('scope_repo_id')}
78 79 % else:
79 80 ${h.select('scope_repo_id_disabled', '', ['Scopes available in EE edition'], disabled='disabled')}
80 81 % endif
81 82 </div>
82 83 <p class="help-block">
83 84 ${_('Repository scope works only with tokens with VCS type.')}
84 85 </p>
85 86 </div>
86 87 <div class="buttons">
87 88 ${h.submit('save',_('Add'),class_="btn")}
88 89 ${h.reset('reset',_('Reset'),class_="btn")}
89 90 </div>
90 91 </div>
91 92 </div>
92 93 ${h.end_form()}
93 94 </div>
94 95 </div>
95 96 </div>
96 97
97 98 <script>
98 99 $(document).ready(function(){
99 100
100 101 var select2Options = {
101 102 'containerCssClass': "drop-menu",
102 103 'dropdownCssClass': "drop-menu-dropdown",
103 104 'dropdownAutoWidth': true
104 105 };
105 106 $("#role").select2(select2Options);
106 107
107 108 var preloadData = {
108 109 results: [
109 110 % for entry in c.lifetime_values:
110 111 {id:${entry[0]}, text:"${entry[1]}"}${'' if loop.last else ','}
111 112 % endfor
112 113 ]
113 114 };
114 115
115 116 $("#lifetime").select2({
116 117 containerCssClass: "drop-menu",
117 118 dropdownCssClass: "drop-menu-dropdown",
118 119 dropdownAutoWidth: true,
119 120 data: preloadData,
120 121 placeholder: "${_('Select or enter expiration date')}",
121 122 query: function(query) {
122 123 feedLifetimeOptions(query, preloadData);
123 124 }
124 125 });
125 126
126 127
127 128 var repoFilter = function(data) {
128 129 var results = [];
129 130
130 131 if (!data.results[0]) {
131 132 return data
132 133 }
133 134
134 135 $.each(data.results[0].children, function() {
135 136 // replace name to ID for submision
136 137 this.id = this.repo_id;
137 138 results.push(this);
138 139 });
139 140
140 141 data.results[0].children = results;
141 142 return data;
142 143 };
143 144
144 145 $("#scope_repo_id_disabled").select2(select2Options);
145 146
146 147 var selectVcsScope = function() {
147 148 // select vcs scope and disable input
148 149 $("#role").select2("val", "${c.role_vcs}").trigger('change');
149 150 $("#role").select2("readonly", true)
150 151 };
151 152
152 153 $("#scope_repo_id").select2({
153 154 cachedDataSource: {},
154 155 minimumInputLength: 2,
155 156 placeholder: "${_('repository scope')}",
156 157 dropdownAutoWidth: true,
157 158 containerCssClass: "drop-menu",
158 159 dropdownCssClass: "drop-menu-dropdown",
159 160 formatResult: formatRepoResult,
160 161 query: $.debounce(250, function(query){
161 162 self = this;
162 163 var cacheKey = query.term;
163 164 var cachedData = self.cachedDataSource[cacheKey];
164 165
165 166 if (cachedData) {
166 167 query.callback({results: cachedData.results});
167 168 } else {
168 169 $.ajax({
169 170 url: pyroutes.url('repo_list_data'),
170 171 data: {'query': query.term},
171 172 dataType: 'json',
172 173 type: 'GET',
173 174 success: function(data) {
174 175 data = repoFilter(data);
175 176 self.cachedDataSource[cacheKey] = data;
176 177 query.callback({results: data.results});
177 178 },
178 179 error: function(data, textStatus, errorThrown) {
179 180 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
180 181 }
181 182 })
182 183 }
183 184 })
184 185 });
185 186 $("#scope_repo_id").on('select2-selecting', function(e){
186 187 selectVcsScope()
187 188 });
188 189
189 190 });
190 191 </script>
@@ -1,60 +1,61 b''
1 1 <%namespace name="base" file="/base/base.mako"/>
2 2
3 3 <div class="panel panel-default">
4 4 <div class="panel-heading">
5 5 <h3 class="panel-title">${_('Account Emails')}</h3>
6 6 </div>
7 7
8 8 <div class="panel-body">
9 9 <div class="emails_wrap">
10 10 <table class="rctable account_emails">
11 11 <tr>
12 12 <td class="td-user">
13 13 ${base.gravatar(c.user.email, 16)}
14 14 <span class="user email">${c.user.email}</span>
15 15 </td>
16 16 <td class="td-tags">
17 17 <span class="tag tag1">${_('Primary')}</span>
18 18 </td>
19 19 </tr>
20 20 %if c.user_email_map:
21 21 %for em in c.user_email_map:
22 22 <tr>
23 23 <td class="td-user">
24 24 ${base.gravatar(em.email, 16)}
25 25 <span class="user email">${em.email}</span>
26 26 </td>
27 27 <td class="td-action">
28 28 ${h.secure_form(h.route_path('my_account_emails_delete'), request=request)}
29 29 ${h.hidden('del_email_id',em.email_id)}
30 30 <button class="btn btn-link btn-danger" type="submit" id="${'remove_email_%s'.format(em.email_id)}"
31 onclick="return confirm('${_('Confirm to delete this email: {}').format(em.email)}');">
31 onclick="submitConfirm(event, this, _gettext('Confirm to delete this email'), _gettext('Delete'), '${em.email}')"
32 >
32 33 ${_('Delete')}
33 34 </button>
34 35 ${h.end_form()}
35 36 </td>
36 37 </tr>
37 38 %endfor
38 39 %else:
39 40 <tr class="noborder">
40 41 <td colspan="3">
41 42 <div class="td-email">
42 43 ${_('No additional emails specified')}
43 44 </div>
44 45 </td>
45 46 </tr>
46 47 %endif
47 48 </table>
48 49 </div>
49 50
50 51 % if c.user.extern_type != 'rhodecode':
51 52 <p>${_('Your user account details are managed by an external source. Details cannot be managed here.')}
52 53 <br/>${_('Source type')}: <strong>${c.user.extern_type}</strong>
53 54 </p>
54 55 % else:
55 56 <div>
56 57 ${c.form.render() | n}
57 58 </div>
58 59 % endif
59 60 </div>
60 61 </div>
@@ -1,91 +1,92 b''
1 1 <div class="panel panel-default">
2 2 <div class="panel-heading">
3 3 <h3 class="panel-title">${_('SSH Keys')}</h3>
4 4 </div>
5 5 <div class="panel-body">
6 6 <div class="sshkeys_wrap">
7 7 <table class="rctable ssh_keys">
8 8 <tr>
9 9 <th>${_('Fingerprint')}</th>
10 10 <th>${_('Description')}</th>
11 11 <th>${_('Created on')}</th>
12 12 <th>${_('Accessed on')}</th>
13 13 <th>${_('Action')}</th>
14 14 </tr>
15 15 % if not c.ssh_enabled:
16 16 <tr><td colspan="4"><div class="">${_('SSH Keys usage is currently disabled, please ask your administrator to enable them.')}</div></td></tr>
17 17 % else:
18 18 %if c.user_ssh_keys:
19 19 %for ssh_key in c.user_ssh_keys:
20 20 <tr class="">
21 21 <td class="">
22 22 <code>${ssh_key.ssh_key_fingerprint}</code>
23 23 </td>
24 24 <td class="td-wrap">${ssh_key.description}</td>
25 25 <td class="td-tags">${h.format_date(ssh_key.created_on)}</td>
26 26 <td class="td-tags">${h.format_date(ssh_key.accessed_on)}</td>
27 27
28 28 <td class="td-action">
29 29 ${h.secure_form(h.route_path('my_account_ssh_keys_delete'), request=request)}
30 30 ${h.hidden('del_ssh_key', ssh_key.ssh_key_id)}
31 31 <button class="btn btn-link btn-danger" type="submit"
32 onclick="return confirm('${_('Confirm to remove ssh key %s') % ssh_key.ssh_key_fingerprint}');">
32 onclick="submitConfirm(event, this, _gettext('Confirm to delete this ssh key'), _gettext('Delete'), '${ssh_key.ssh_key_fingerprint}')"
33 >
33 34 ${_('Delete')}
34 35 </button>
35 36 ${h.end_form()}
36 37 </td>
37 38 </tr>
38 39 %endfor
39 40 %else:
40 41 <tr><td colspan="4"><div class="">${_('No additional ssh keys specified')}</div></td></tr>
41 42 %endif
42 43 % endif
43 44 </table>
44 45 </div>
45 46
46 47 % if c.ssh_enabled:
47 48 <div class="user_ssh_keys">
48 49 ${h.secure_form(h.route_path('my_account_ssh_keys_add'), request=request)}
49 50 <div class="form form-vertical">
50 51 <!-- fields -->
51 52 <div class="fields">
52 53 <div class="field">
53 54 <div class="label">
54 55 <label for="new_email">${_('New ssh key')}:</label>
55 56 </div>
56 57 <div class="input">
57 58 ${h.text('description', class_='medium', placeholder=_('Description'))}
58 59 % if c.ssh_key_generator_enabled:
59 60 <a href="${h.route_path('my_account_ssh_keys_generate')}">${_('Generate random RSA key')}</a>
60 61 % endif
61 62 </div>
62 63 </div>
63 64
64 65 <div class="field">
65 66 <div class="textarea text-area editor">
66 67 ${h.textarea('key_data',c.default_key, size=30, placeholder=_("Public key, begins with 'ssh-rsa', 'ssh-dss', 'ssh-ed25519', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', or 'ecdsa-sha2-nistp521'"))}
67 68 </div>
68 69 </div>
69 70
70 71 <div class="buttons">
71 72 ${h.submit('save',_('Add'),class_="btn")}
72 73 ${h.reset('reset',_('Reset'),class_="btn")}
73 74 </div>
74 75 % if c.default_key:
75 76 ${_('Click add to use this generated SSH key')}
76 77 % endif
77 78 </div>
78 79 </div>
79 80 ${h.end_form()}
80 81 </div>
81 82 % endif
82 83 </div>
83 84 </div>
84 85
85 86 <script>
86 87
87 88 $(document).ready(function(){
88 89
89 90
90 91 });
91 92 </script>
@@ -1,295 +1,299 b''
1 1 <%namespace name="base" file="/base/base.mako"/>
2 2
3 3 <%
4 4 elems = [
5 5 (_('Repository ID'), c.rhodecode_db_repo.repo_id, '', ''),
6 6 (_('Owner'), lambda:base.gravatar_with_user(c.rhodecode_db_repo.user.email, tooltip=True), '', ''),
7 7 (_('Created on'), h.format_date(c.rhodecode_db_repo.created_on), '', ''),
8 8 (_('Updated on'), h.format_date(c.rhodecode_db_repo.updated_on), '', ''),
9 9 (_('Cached Commit id'), lambda: h.link_to(c.rhodecode_db_repo.changeset_cache.get('short_id'), h.route_path('repo_commit',repo_name=c.repo_name,commit_id=c.rhodecode_db_repo.changeset_cache.get('raw_id'))), '', ''),
10 10 (_('Cached Commit date'), c.rhodecode_db_repo.changeset_cache.get('date'), '', ''),
11 11 (_('Cached Commit data'), lambda: h.link_to('refresh now', h.current_route_path(request, update_commit_cache=1)), '', ''),
12 12 (_('Attached scoped tokens'), len(c.rhodecode_db_repo.scoped_tokens), '', [x.user for x in c.rhodecode_db_repo.scoped_tokens]),
13 13 (_('Pull requests source'), len(c.rhodecode_db_repo.pull_requests_source), '', ['pr_id:{}, repo:{}'.format(x.pull_request_id,x.source_repo.repo_name) for x in c.rhodecode_db_repo.pull_requests_source]),
14 14 (_('Pull requests target'), len(c.rhodecode_db_repo.pull_requests_target), '', ['pr_id:{}, repo:{}'.format(x.pull_request_id,x.target_repo.repo_name) for x in c.rhodecode_db_repo.pull_requests_target]),
15 15 (_('Attached Artifacts'), len(c.rhodecode_db_repo.artifacts), '', ''),
16 16 ]
17 17 %>
18 18
19 19 <div class="panel panel-default">
20 20 <div class="panel-heading" id="advanced-info" >
21 21 <h3 class="panel-title">${_('Repository: %s') % c.rhodecode_db_repo.repo_name} <a class="permalink" href="#advanced-info"> ΒΆ</a></h3>
22 22 </div>
23 23 <div class="panel-body">
24 24 ${base.dt_info_panel(elems)}
25 25 </div>
26 26 </div>
27 27
28 28
29 29 <div class="panel panel-default">
30 30 <div class="panel-heading" id="advanced-fork">
31 31 <h3 class="panel-title">${_('Fork Reference')} <a class="permalink" href="#advanced-fork"> ΒΆ</a></h3>
32 32 </div>
33 33 <div class="panel-body">
34 34 ${h.secure_form(h.route_path('edit_repo_advanced_fork', repo_name=c.rhodecode_db_repo.repo_name), request=request)}
35 35
36 36 % if c.rhodecode_db_repo.fork:
37 37 <div class="panel-body-title-text">${h.literal(_('This repository is a fork of %(repo_link)s') % {'repo_link': h.link_to_if(c.has_origin_repo_read_perm,c.rhodecode_db_repo.fork.repo_name, h.route_path('repo_summary', repo_name=c.rhodecode_db_repo.fork.repo_name))})}
38 38 | <button class="btn btn-link btn-danger" type="submit">Remove fork reference</button></div>
39 39 % endif
40 40
41 41 <div class="field">
42 42 ${h.hidden('id_fork_of')}
43 43 ${h.submit('set_as_fork_%s' % c.rhodecode_db_repo.repo_name,_('Set'),class_="btn btn-small",)}
44 44 </div>
45 45 <div class="field">
46 46 <span class="help-block">${_('Manually set this repository as a fork of another from the list')}</span>
47 47 </div>
48 48 ${h.end_form()}
49 49 </div>
50 50 </div>
51 51
52 52
53 53 <div class="panel panel-default">
54 54 <div class="panel-heading" id="advanced-journal">
55 55 <h3 class="panel-title">${_('Public Journal Visibility')} <a class="permalink" href="#advanced-journal"> ΒΆ</a></h3>
56 56 </div>
57 57 <div class="panel-body">
58 58 ${h.secure_form(h.route_path('edit_repo_advanced_journal', repo_name=c.rhodecode_db_repo.repo_name), request=request)}
59 59 <div class="field">
60 60 %if c.in_public_journal:
61 61 <button class="btn btn-small" type="submit">
62 62 ${_('Remove from Public Journal')}
63 63 </button>
64 64 %else:
65 65 <button class="btn btn-small" type="submit">
66 66 ${_('Add to Public Journal')}
67 67 </button>
68 68 %endif
69 69 </div>
70 70 <div class="field" >
71 71 <span class="help-block">${_('All actions made on this repository will be visible to everyone following the public journal.')}</span>
72 72 </div>
73 73 ${h.end_form()}
74 74 </div>
75 75 </div>
76 76
77 77
78 78 <div class="panel panel-default">
79 79 <div class="panel-heading" id="advanced-locking">
80 80 <h3 class="panel-title">${_('Locking state')} <a class="permalink" href="#advanced-locking"> ΒΆ</a></h3>
81 81 </div>
82 82 <div class="panel-body">
83 83 ${h.secure_form(h.route_path('edit_repo_advanced_locking', repo_name=c.rhodecode_db_repo.repo_name), request=request)}
84 84
85 85 %if c.rhodecode_db_repo.locked[0]:
86 86 <div class="panel-body-title-text">${'Locked by %s on %s. Lock reason: %s' % (h.person_by_id(c.rhodecode_db_repo.locked[0]),
87 87 h.format_date(h. time_to_datetime(c.rhodecode_db_repo.locked[1])), c.rhodecode_db_repo.locked[2])}</div>
88 88 %else:
89 89 <div class="panel-body-title-text">${_('This Repository is not currently locked.')}</div>
90 90 %endif
91 91
92 92 <div class="field" >
93 93 %if c.rhodecode_db_repo.locked[0]:
94 94 ${h.hidden('set_unlock', '1')}
95 95 <button class="btn btn-small" type="submit"
96 onclick="return confirm('${_('Confirm to unlock repository.')}');">
96 onclick="submitConfirm(event, this, _gettext('Confirm to unlock this repository'), _gettext('Unlock'), '${c.rhodecode_db_repo.repo_name}')"
97 >
97 98 <i class="icon-unlock"></i>
98 99 ${_('Unlock repository')}
99 100 </button>
100 101 %else:
101 102 ${h.hidden('set_lock', '1')}
102 103 <button class="btn btn-small" type="submit"
103 onclick="return confirm('${_('Confirm to lock repository.')}');">
104 onclick="submitConfirm(event, this, _gettext('Confirm to lock this repository'), _gettext('lock'), '${c.rhodecode_db_repo.repo_name}')"
105 >
104 106 <i class="icon-lock"></i>
105 107 ${_('Lock repository')}
106 108 </button>
107 109 %endif
108 110 </div>
109 111 <div class="field" >
110 112 <span class="help-block">
111 113 ${_('Force repository locking. This only works when anonymous access is disabled. Pulling from the repository locks the repository to that user until the same user pushes to that repository again.')}
112 114 </span>
113 115 </div>
114 116 ${h.end_form()}
115 117 </div>
116 118 </div>
117 119
118 120
119 121 <div class="panel panel-default">
120 122 <div class="panel-heading" id="advanced-hooks">
121 123 <h3 class="panel-title">${_('Hooks')} <a class="permalink" href="#advanced-hooks"> ΒΆ</a></h3>
122 124 </div>
123 125 <div class="panel-body">
124 126 <table class="rctable">
125 127 <th>${_('Hook type')}</th>
126 128 <th>${_('Hook version')}</th>
127 129 <th>${_('Current version')}</th>
128 130 % if c.ver_info_dict:
129 131 <tr>
130 132 <td>${_('PRE HOOK')}</td>
131 133 <td>${c.ver_info_dict['pre_version']}</td>
132 134 <td>${c.rhodecode_version}</td>
133 135 </tr>
134 136 <tr>
135 137 <td>${_('POST HOOK')}</td>
136 138 <td>${c.ver_info_dict['post_version']}</td>
137 139 <td>${c.rhodecode_version}</td>
138 140 </tr>
139 141 % else:
140 142 <tr>
141 143 <td>${_('Unable to read hook information from VCS Server')}</td>
142 144 </tr>
143 145 % endif
144 146 </table>
145 147
146 148 <a class="btn btn-primary" href="${h.route_path('edit_repo_advanced_hooks', repo_name=c.repo_name)}"
147 149 onclick="return confirm('${_('Confirm to reinstall hooks for this repository.')}');">
148 150 ${_('Update Hooks')}
149 151 </a>
150 152 % if c.hooks_outdated:
151 153 <span class="alert-error" style="padding: 10px">
152 154 ${_('Outdated hooks detected, please update hooks using `Update Hooks` action.')}
153 155 </span>
154 156 % endif
155 157 </div>
156 158 </div>
157 159
158 160 <div class="panel panel-warning">
159 161 <div class="panel-heading" id="advanced-archive">
160 162 <h3 class="panel-title">${_('Archive repository')} <a class="permalink" href="#advanced-archive"> ΒΆ</a></h3>
161 163 </div>
162 164 <div class="panel-body">
163 165 ${h.secure_form(h.route_path('edit_repo_advanced_archive', repo_name=c.repo_name), request=request)}
164 166
165 167 <div style="margin: 0 0 20px 0" class="fake-space"></div>
166 168
167 169 <div class="field">
168 170 <button class="btn btn-small btn-warning" type="submit"
169 onclick="return confirm('${_('Confirm to archive this repository: %s') % c.repo_name}');">
171 onclick="submitConfirm(event, this, _gettext('Confirm to archive this repository'), _gettext('Archive'), '${c.rhodecode_db_repo.repo_name}')"
172 >
170 173 ${_('Archive this repository')}
171 174 </button>
172 175 </div>
173 176 <div class="field">
174 177 <span class="help-block">
175 178 ${_('Archiving the repository will make it entirely read-only. The repository cannot be committed to.'
176 179 'It is hidden from the search results and dashboard. ')}
177 180 </span>
178 181 </div>
179 182
180 183 ${h.end_form()}
181 184 </div>
182 185 </div>
183 186
184 187
185 188 <div class="panel panel-danger">
186 189 <div class="panel-heading" id="advanced-delete">
187 190 <h3 class="panel-title">${_('Delete repository')} <a class="permalink" href="#advanced-delete"> ΒΆ</a></h3>
188 191 </div>
189 192 <div class="panel-body">
190 193 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=c.repo_name), request=request)}
191 194 <table class="display">
192 195 <tr>
193 196 <td>
194 197 ${_ungettext('This repository has %s fork.', 'This repository has %s forks.', c.rhodecode_db_repo.forks.count()) % c.rhodecode_db_repo.forks.count()}
195 198 </td>
196 199 <td>
197 200 %if c.rhodecode_db_repo.forks.count():
198 201 <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label>
199 202 %endif
200 203 </td>
201 204 <td>
202 205 %if c.rhodecode_db_repo.forks.count():
203 206 <input type="radio" name="forks" value="delete_forks"/> <label for="forks">${_('Delete forks')}</label>
204 207 %endif
205 208 </td>
206 209 </tr>
207 210 <% attached_prs = len(c.rhodecode_db_repo.pull_requests_source + c.rhodecode_db_repo.pull_requests_target) %>
208 211 % if c.rhodecode_db_repo.pull_requests_source or c.rhodecode_db_repo.pull_requests_target:
209 212 <tr>
210 213 <td>
211 214 ${_ungettext('This repository has %s attached pull request.', 'This repository has %s attached pull requests.', attached_prs) % attached_prs}
212 215 <br/>
213 216 ${_('Consider to archive this repository instead.')}
214 217 </td>
215 218 <td></td>
216 219 <td></td>
217 220 </tr>
218 221 % endif
219 222 </table>
220 223 <div style="margin: 0 0 20px 0" class="fake-space"></div>
221 224
222 225 <div class="field">
223 226 <button class="btn btn-small btn-danger" type="submit"
224 onclick="return confirm('${_('Confirm to delete this repository: %s') % c.repo_name}');">
227 onclick="submitConfirm(event, this, _gettext('Confirm to delete this repository'), _gettext('Delete'), '${c.rhodecode_db_repo.repo_name}')"
228 >
225 229 ${_('Delete this repository')}
226 230 </button>
227 231 </div>
228 232 <div class="field">
229 233 <span class="help-block">
230 234 ${_('This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command available in rhodecode-tools.')}
231 235 </span>
232 236 </div>
233 237
234 238 ${h.end_form()}
235 239 </div>
236 240 </div>
237 241
238 242
239 243 <script>
240 244
241 245 var currentRepoId = ${c.rhodecode_db_repo.repo_id};
242 246
243 247 var repoTypeFilter = function(data) {
244 248 var results = [];
245 249
246 250 if (!data.results[0]) {
247 251 return data
248 252 }
249 253
250 254 $.each(data.results[0].children, function() {
251 255 // filter out the SAME repo, it cannot be used as fork of itself
252 256 if (this.repo_id != currentRepoId) {
253 257 this.id = this.repo_id;
254 258 results.push(this)
255 259 }
256 260 });
257 261 data.results[0].children = results;
258 262 return data;
259 263 };
260 264
261 265 $("#id_fork_of").select2({
262 266 cachedDataSource: {},
263 267 minimumInputLength: 2,
264 268 placeholder: "${_('Change repository') if c.rhodecode_db_repo.fork else _('Pick repository')}",
265 269 dropdownAutoWidth: true,
266 270 containerCssClass: "drop-menu",
267 271 dropdownCssClass: "drop-menu-dropdown",
268 272 formatResult: formatRepoResult,
269 273 query: $.debounce(250, function(query){
270 274 self = this;
271 275 var cacheKey = query.term;
272 276 var cachedData = self.cachedDataSource[cacheKey];
273 277
274 278 if (cachedData) {
275 279 query.callback({results: cachedData.results});
276 280 } else {
277 281 $.ajax({
278 282 url: pyroutes.url('repo_list_data'),
279 283 data: {'query': query.term, repo_type: '${c.rhodecode_db_repo.repo_type}'},
280 284 dataType: 'json',
281 285 type: 'GET',
282 286 success: function(data) {
283 287 data = repoTypeFilter(data);
284 288 self.cachedDataSource[cacheKey] = data;
285 289 query.callback({results: data.results});
286 290 },
287 291 error: function(data, textStatus, errorThrown) {
288 292 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
289 293 }
290 294 })
291 295 }
292 296 })
293 297 });
294 298 </script>
295 299
@@ -1,154 +1,158 b''
1 1 <div class="panel panel-default">
2 2 <div class="panel-heading">
3 3 <h3 class="panel-title">${_('Invalidate Cache for Repository')}</h3>
4 4 </div>
5 5 <div class="panel-body">
6 6
7 7 <h4>${_('Manually invalidate the repository cache. On the next access a repository cache will be recreated.')}</h4>
8 8
9 9 <p>
10 10 ${_('Cache purge can be automated by such api call. Can be called periodically in crontab etc.')}
11 11 <br/>
12 12 <code>
13 13 ${h.api_call_example(method='invalidate_cache', args={"repoid": c.rhodecode_db_repo.repo_name})}
14 14 </code>
15 15 </p>
16 16
17 17 ${h.secure_form(h.route_path('edit_repo_caches', repo_name=c.repo_name), request=request)}
18 18 <div class="form">
19 19 <div class="fields">
20 ${h.submit('reset_cache_%s' % c.rhodecode_db_repo.repo_name,_('Invalidate repository cache'),class_="btn btn-small",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
20 <input class="btn btn-small" id="reset_cache_${c.rhodecode_db_repo.repo_id}"
21 name="reset_cache_${c.rhodecode_db_repo.repo_id}"
22 onclick="submitConfirm(event, this, _gettext('Confirm to invalidate repository cache'), _gettext('Invalidate Cache'), '${c.rhodecode_db_repo.repo_name}')"
23 type="submit" value="${_('Invalidate repository cache')}"
24 >
21 25 </div>
22 26 </div>
23 27 ${h.end_form()}
24 28
25 29 </div>
26 30 </div>
27 31
28 32
29 33 <div class="panel panel-default">
30 34 <div class="panel-heading">
31 35 <h3 class="panel-title">
32 36 ${_('Invalidation keys')}
33 37 </h3>
34 38 </div>
35 39 <div class="panel-body">
36 40 <p>
37 41 Cache keys used to signal repository state changes after operations such as push, strip etc.
38 42 </p>
39 43 <div class="field">
40 44 <a href="#showKeys" onclick="$('#signal-keys').toggle()">${_('Show all')} ${len(c.rhodecode_db_repo.cache_keys)}</a>
41 45
42 46 <table class="rctable edit_cache" id="signal-keys" style="display: none">
43 47 <tr>
44 48 <th>${_('Key')}</th>
45 49 <th>${_('State UID')}</th>
46 50 <th>${_('Namespace')}</th>
47 51 <th>${_('Active')}</th>
48 52 </tr>
49 53 %for cache in c.rhodecode_db_repo.cache_keys:
50 54 <tr>
51 55 <td class="td-prefix"><code>${cache.cache_key}</code></td>
52 56 <td class="td-cachekey"><code>${cache.cache_state_uid}</code></td>
53 57 <td class="td-cachekey"><code>${cache.cache_args}</code></td>
54 58 <td class="td-active">${h.bool2icon(cache.cache_active)}</td>
55 59 </tr>
56 60 %endfor
57 61 </table>
58 62 </div>
59 63 </div>
60 64 </div>
61 65
62 66 <div class="panel panel-default">
63 67 <div class="panel-heading">
64 68 <h3 class="panel-title">
65 69 ${_('Cache keys')}
66 70 </h3>
67 71 </div>
68 72 <div class="panel-body">
69 73 <p>
70 74 Cache keys used for storing cached values of repository stats,
71 75 file tree history and file tree search.
72 76 Invalidating the cache will remove those entries.
73 77 </p>
74 78 <pre>
75 79 backend: ${c.region.actual_backend.__class__}
76 80 % if c.rhodecode_user.is_admin:
77 81 store: ${c.region.actual_backend.get_store()}
78 82 % else:
79 83 store: ${c.region.actual_backend.get_store().__class__}
80 84 % endif
81 85 </pre>
82 86
83 87 <div class="field">
84 88 <a href="#showKeys" onclick="$('#cache-keys').toggle()">${_('Show all')} ${len(c.repo_keys)}</a>
85 89
86 90 <table class="rctable edit_cache" id="cache-keys" style="display: none">
87 91 <tr>
88 92 <th>${_('Key')}</th>
89 93 <th>${_('Region')}</th>
90 94 </tr>
91 95 %for cache_key in c.repo_keys:
92 96 <tr>
93 97 <td class="td-prefix"><code>${cache_key}</code></td>
94 98 <td class="td-cachekey">${c.region.name}</td>
95 99 </tr>
96 100 %endfor
97 101 </table>
98 102 </div>
99 103
100 104 </div>
101 105 </div>
102 106
103 107
104 108 <div class="panel panel-default">
105 109 <div class="panel-heading">
106 110 <h3 class="panel-title">${_('Shadow Repositories')}</h3>
107 111 </div>
108 112 <div class="panel-body">
109 113 <table class="rctable edit_cache">
110 114 % if c.shadow_repos:
111 115 % for shadow_repo in c.shadow_repos:
112 116 <tr>
113 117 <td>${shadow_repo}</td>
114 118 </tr>
115 119 % endfor
116 120 % else:
117 121 <tr>
118 122 <td>${_('No Shadow repositories exist for this repository.')}</td>
119 123 </tr>
120 124 % endif
121 125
122 126 </table>
123 127 </div>
124 128 </div>
125 129
126 130
127 131 <div class="panel panel-default">
128 132 <div class="panel-heading">
129 133 <h3 class="panel-title">${_('Diff Caches')}</h3>
130 134 </div>
131 135 <div class="panel-body">
132 136 <p>
133 137 Number and size of stored cached diff for commits and pull requests.
134 138 </p>
135 139 <table class="rctable edit_cache">
136 140 <tr>
137 141 <td>${_('Cached diff name')}:</td>
138 142 % if c.rhodecode_user.is_admin:
139 143 <td>${c.rhodecode_db_repo.cached_diffs_dir}</td>
140 144 % else:
141 145 <td>${c.rhodecode_db_repo.cached_diffs_relative_dir}</td>
142 146 % endif
143 147 </tr>
144 148 <tr>
145 149 <td>${_('Cached diff files')}:</td>
146 150 <td>${c.cached_diff_count}</td>
147 151 </tr>
148 152 <tr>
149 153 <td>${_('Cached diff size')}:</td>
150 154 <td>${h.format_byte_size(c.cached_diff_size)}</td>
151 155 </tr>
152 156 </table>
153 157 </div>
154 158 </div>
@@ -1,68 +1,73 b''
1 1 <div class="panel panel-default">
2 2 <div class="panel-heading">
3 3 <h3 class="panel-title">${_('Remote Sync')}</h3>
4 4 </div>
5 5 <div class="panel-body">
6 6
7 7 <h4>${_('Manually pull/push changes from/to external URLs.')}</h4>
8 8
9 9 <table>
10 10 <tr>
11 11 <td><div style="min-width: 80px"><strong>${_('Pull url')}</strong></div></td>
12 12 <td>
13 13 % if c.rhodecode_db_repo.clone_uri:
14 14 <a href="${c.rhodecode_db_repo.clone_uri}">${c.rhodecode_db_repo.clone_uri_hidden}</a>
15 15 % else:
16 16 ${_('This repository does not have any pull url set.')}
17 17 <a href="${h.route_path('edit_repo', repo_name=c.rhodecode_db_repo.repo_name)}">${_('Set remote url.')}</a>
18 18 % endif
19 19 </td>
20 20 </tr>
21 21 % if c.rhodecode_db_repo.clone_uri:
22 22 <tr>
23 23 <td></td>
24 24 <td>
25 25 <p>
26 26 ${_('Pull can be automated by such api call. Can be called periodically in crontab etc.')}
27 27 <br/>
28 28 <code>
29 29 ${h.api_call_example(method='pull', args={"repoid": c.rhodecode_db_repo.repo_name})}
30 30 </code>
31 31 </p>
32 32 </td>
33 33 </tr>
34 34 <tr>
35 35 <td></td>
36 36 <td>
37 37 ${h.secure_form(h.route_path('edit_repo_remote_pull', repo_name=c.repo_name), request=request)}
38 38 <div class="form">
39 39 <div class="fields">
40 ${h.submit('remote_pull_%s' % c.rhodecode_db_repo.repo_name,_('Pull changes from remote location'),class_="btn btn-small",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")}
40 <input class="btn btn-small"
41 id="remote_pull_${c.rhodecode_db_repo.repo_id}"
42 name="remote_pull_${c.rhodecode_db_repo.repo_id}"
43 onclick="submitConfirm(event, this, _gettext('Confirm pull changes from remote side'), _gettext('Pull changes'), '${c.rhodecode_db_repo.clone_uri_hidden}')"
44 type="submit" value="${_('Pull changes from remote location')}"
45 >
41 46 </div>
42 47 </div>
43 48 ${h.end_form()}
44 49 </td>
45 50 </tr>
46 51 % endif
47 52
48 53 <tr>
49 54 <td><div style="min-width: 80px"><strong>${_('Push url')}</strong></div></td>
50 55 <td>
51 56 % if c.rhodecode_db_repo.push_uri:
52 57 <a href="${c.rhodecode_db_repo.push_uri_hidden}">${c.rhodecode_db_repo.push_uri_hidden}</a>
53 58 % else:
54 59 ${_('This repository does not have any push url set.')}
55 60 <a href="${h.route_path('edit_repo', repo_name=c.rhodecode_db_repo.repo_name)}">${_('Set remote url.')}</a>
56 61 % endif
57 62 </td>
58 63 </tr>
59 64 <tr>
60 65 <td></td>
61 66 <td>
62 67 ${_('This feature is available in RhodeCode EE edition only. Contact {sales_email} to obtain a trial license.').format(sales_email='<a href="mailto:sales@rhodecode.com">sales@rhodecode.com</a>')|n}
63 68 </td>
64 69 </tr>
65 70
66 71 </table>
67 72 </div>
68 73 </div>
@@ -1,47 +1,47 b''
1 1 <div class="panel panel-default">
2 2 <div class="panel-heading">
3 3 <h3 class="panel-title">${_('Exceptions Tracker - Exception ID')}: ${c.exception_id}</h3>
4 4 </div>
5 5 <div class="panel-body">
6 6 % if c.traceback:
7 7
8 8 <h4>${_('Exception `{}` generated on UTC date: {}').format(c.traceback.get('exc_type', 'NO_TYPE'), c.traceback.get('exc_utc_date', 'NO_DATE'))}</h4>
9 9 % if c.traceback.get('url'):
10 10 Request:
11 11 <code>${c.traceback.get('method')} ${c.traceback.get('url')}</code><br/>
12 12 <code>${c.traceback.get('client_address')} ${c.traceback.get('user_agent')}</code>
13 13 <br/>
14 14 <br/>
15 15 % endif
16 16
17 17 <pre>${c.traceback.get('exc_message', 'NO_MESSAGE')}</pre>
18 18
19 19
20 20 % else:
21 21 ${_('Unable to Read Exception. It might be removed or non-existing.')}
22 22 % endif
23 23 </div>
24 24 </div>
25 25
26 26
27 27 % if c.traceback:
28 28 <div class="panel panel-danger">
29 29 <div class="panel-heading" id="advanced-delete">
30 30 <h3 class="panel-title">${_('Delete this Exception')}</h3>
31 31 </div>
32 32 <div class="panel-body">
33 33 ${h.secure_form(h.route_path('admin_settings_exception_tracker_delete', exception_id=c.exception_id), request=request)}
34 34 <div style="margin: 0 0 20px 0" class="fake-space"></div>
35 35
36 36 <div class="field">
37 37 <button class="btn btn-small btn-danger" type="submit"
38 onclick="return confirm('${_('Confirm to delete this exception')}');">
38 onclick="submitConfirm(event, this, _gettext('Confirm to delete this exception'), _gettext('Delete'), ${c.exception_id})">
39 39 <i class="icon-remove"></i>
40 40 ${_('Delete This Exception')}
41 41 </button>
42 42 </div>
43 43
44 44 ${h.end_form()}
45 45 </div>
46 46 </div>
47 47 % endif
@@ -1,65 +1,66 b''
1 1 <div class="panel panel-default">
2 2 <div class="panel-heading">
3 3 <h3 class="panel-title">${_('Exceptions Tracker ')}</h3>
4 4 </div>
5 5 <div class="panel-body">
6 6 % if c.exception_list_count == 1:
7 7 ${_('There is {} stored exception.').format(c.exception_list_count)}
8 8 % else:
9 9 ${_('There are total {} stored exceptions.').format(c.exception_list_count)}
10 10 % endif
11 11 <br/>
12 12 ${_('Store directory')}: ${c.exception_store_dir}
13 13
14 14 ${h.secure_form(h.route_path('admin_settings_exception_tracker_delete_all'), request=request)}
15 15 <div style="margin: 0 0 20px 0" class="fake-space"></div>
16 16 <input type="hidden" name="type_filter", value="${c.type_filter}">
17 17 <div class="field">
18 18 <button class="btn btn-small btn-danger" type="submit"
19 onclick="return confirm('${_('Confirm to delete all exceptions')}');">
19 onclick="submitConfirm(event, this, _gettext('Confirm to delete all exceptions'), _gettext('Delete'), '${'total:{}'.format(c.exception_list_count)}')"
20 >
20 21 <i class="icon-remove"></i>
21 22 % if c.type_filter:
22 23 ${_('Delete All `{}`').format(c.type_filter)}
23 24 % else:
24 25 ${_('Delete All')}
25 26 % endif
26 27
27 28 </button>
28 29 </div>
29 30
30 31 ${h.end_form()}
31 32
32 33 </div>
33 34 </div>
34 35
35 36
36 37 <div class="panel panel-default">
37 38 <div class="panel-heading">
38 39 <h3 class="panel-title">${_('Exceptions Tracker - Showing the last {} Exceptions').format(c.limit)}.</h3>
39 40 <a class="panel-edit" href="${h.current_route_path(request, limit=c.next_limit)}">${_('Show more')}</a>
40 41 </div>
41 42 <div class="panel-body">
42 43 <table class="rctable">
43 44 <tr>
44 45 <th>#</th>
45 46 <th>Exception ID</th>
46 47 <th>Date</th>
47 48 <th>App Type</th>
48 49 <th>Exc Type</th>
49 50 </tr>
50 51 <% cnt = len(c.exception_list)%>
51 52 % for tb in c.exception_list:
52 53 <tr>
53 54 <td>${cnt}</td>
54 55 <td><a href="${h.route_path('admin_settings_exception_tracker_show', exception_id=tb['exc_id'])}"><code>${tb['exc_id']}</code></a></td>
55 56 <td>${h.format_date(tb['exc_utc_date'])}</td>
56 57 <td>${tb['app_type']}</td>
57 58 <td>
58 59 <a href="${h.current_route_path(request, type_filter=tb['exc_type'])}">${tb['exc_type']}</a>
59 60 </td>
60 61 </tr>
61 62 <% cnt -=1 %>
62 63 % endfor
63 64 </table>
64 65 </div>
65 66 </div>
@@ -1,479 +1,489 b''
1 1 ## DATA TABLE RE USABLE ELEMENTS
2 2 ## usage:
3 3 ## <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
4 4 <%namespace name="base" file="/base/base.mako"/>
5 5
6 6 <%def name="metatags_help()">
7 7 <table>
8 8 <%
9 9 example_tags = [
10 10 ('state','[stable]'),
11 11 ('state','[stale]'),
12 12 ('state','[featured]'),
13 13 ('state','[dev]'),
14 14 ('state','[dead]'),
15 15 ('state','[deprecated]'),
16 16
17 17 ('label','[personal]'),
18 18 ('generic','[v2.0.0]'),
19 19
20 20 ('lang','[lang =&gt; JavaScript]'),
21 21 ('license','[license =&gt; LicenseName]'),
22 22
23 23 ('ref','[requires =&gt; RepoName]'),
24 24 ('ref','[recommends =&gt; GroupName]'),
25 25 ('ref','[conflicts =&gt; SomeName]'),
26 26 ('ref','[base =&gt; SomeName]'),
27 27 ('url','[url =&gt; [linkName](https://rhodecode.com)]'),
28 28 ('see','[see =&gt; http://rhodecode.com]'),
29 29 ]
30 30 %>
31 31 % for tag_type, tag in example_tags:
32 32 <tr>
33 33 <td>${tag|n}</td>
34 34 <td>${h.style_metatag(tag_type, tag)|n}</td>
35 35 </tr>
36 36 % endfor
37 37 </table>
38 38 </%def>
39 39
40 40 <%def name="render_description(description, stylify_metatags)">
41 41 <%
42 42 tags = []
43 43 if stylify_metatags:
44 44 tags, description = h.extract_metatags(description)
45 45 %>
46 46 % for tag_type, tag in tags:
47 47 ${h.style_metatag(tag_type, tag)|n,trim}
48 48 % endfor
49 49 <code style="white-space: pre-wrap">${description}</code>
50 50 </%def>
51 51
52 52 ## REPOSITORY RENDERERS
53 53 <%def name="quick_menu(repo_name)">
54 54 <i class="icon-more"></i>
55 55 <div class="menu_items_container hidden">
56 56 <ul class="menu_items">
57 57 <li>
58 58 <a title="${_('Summary')}" href="${h.route_path('repo_summary',repo_name=repo_name)}">
59 59 <span>${_('Summary')}</span>
60 60 </a>
61 61 </li>
62 62 <li>
63 63 <a title="${_('Commits')}" href="${h.route_path('repo_commits',repo_name=repo_name)}">
64 64 <span>${_('Commits')}</span>
65 65 </a>
66 66 </li>
67 67 <li>
68 68 <a title="${_('Files')}" href="${h.route_path('repo_files:default_commit',repo_name=repo_name)}">
69 69 <span>${_('Files')}</span>
70 70 </a>
71 71 </li>
72 72 <li>
73 73 <a title="${_('Fork')}" href="${h.route_path('repo_fork_new',repo_name=repo_name)}">
74 74 <span>${_('Fork')}</span>
75 75 </a>
76 76 </li>
77 77 </ul>
78 78 </div>
79 79 </%def>
80 80
81 81 <%def name="repo_name(name,rtype,rstate,private,archived,fork_of,short_name=False,admin=False)">
82 82 <%
83 83 def get_name(name,short_name=short_name):
84 84 if short_name:
85 85 return name.split('/')[-1]
86 86 else:
87 87 return name
88 88 %>
89 89 <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate">
90 90 ##NAME
91 91 <a href="${h.route_path('edit_repo',repo_name=name) if admin else h.route_path('repo_summary',repo_name=name)}">
92 92
93 93 ##TYPE OF REPO
94 94 %if h.is_hg(rtype):
95 95 <span title="${_('Mercurial repository')}"><i class="icon-hg" style="font-size: 14px;"></i></span>
96 96 %elif h.is_git(rtype):
97 97 <span title="${_('Git repository')}"><i class="icon-git" style="font-size: 14px"></i></span>
98 98 %elif h.is_svn(rtype):
99 99 <span title="${_('Subversion repository')}"><i class="icon-svn" style="font-size: 14px"></i></span>
100 100 %endif
101 101
102 102 ##PRIVATE/PUBLIC
103 103 %if private is True and c.visual.show_private_icon:
104 104 <i class="icon-lock" title="${_('Private repository')}"></i>
105 105 %elif private is False and c.visual.show_public_icon:
106 106 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
107 107 %else:
108 108 <span></span>
109 109 %endif
110 110 ${get_name(name)}
111 111 </a>
112 112 %if fork_of:
113 113 <a href="${h.route_path('repo_summary',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
114 114 %endif
115 115 %if rstate == 'repo_state_pending':
116 116 <span class="creation_in_progress tooltip" title="${_('This repository is being created in a background task')}">
117 117 (${_('creating...')})
118 118 </span>
119 119 %endif
120 120
121 121 </div>
122 122 </%def>
123 123
124 124 <%def name="repo_desc(description, stylify_metatags)">
125 125 <%
126 126 tags, description = h.extract_metatags(description)
127 127 %>
128 128
129 129 <div class="truncate-wrap">
130 130 % if stylify_metatags:
131 131 % for tag_type, tag in tags:
132 132 ${h.style_metatag(tag_type, tag)|n}
133 133 % endfor
134 134 % endif
135 135 ${description}
136 136 </div>
137 137
138 138 </%def>
139 139
140 140 <%def name="last_change(last_change)">
141 141 ${h.age_component(last_change, time_is_local=True)}
142 142 </%def>
143 143
144 144 <%def name="revision(repo_name, rev, commit_id, author, last_msg, commit_date)">
145 145 <div>
146 146 %if rev >= 0:
147 147 <code><a class="tooltip-hovercard" data-hovercard-alt=${h.tooltip(last_msg)} data-hovercard-url="${h.route_path('hovercard_repo_commit', repo_name=repo_name, commit_id=commit_id)}" href="${h.route_path('repo_commit',repo_name=repo_name,commit_id=commit_id)}">${'r{}:{}'.format(rev,h.short_id(commit_id))}</a></code>
148 148 %else:
149 149 ${_('No commits yet')}
150 150 %endif
151 151 </div>
152 152 </%def>
153 153
154 154 <%def name="rss(name)">
155 155 %if c.rhodecode_user.username != h.DEFAULT_USER:
156 156 <a title="${h.tooltip(_('Subscribe to %s rss feed')% name)}" href="${h.route_path('rss_feed_home', repo_name=name, _query=dict(auth_token=c.rhodecode_user.feed_token))}"><i class="icon-rss-sign"></i></a>
157 157 %else:
158 158 <a title="${h.tooltip(_('Subscribe to %s rss feed')% name)}" href="${h.route_path('rss_feed_home', repo_name=name)}"><i class="icon-rss-sign"></i></a>
159 159 %endif
160 160 </%def>
161 161
162 162 <%def name="atom(name)">
163 163 %if c.rhodecode_user.username != h.DEFAULT_USER:
164 164 <a title="${h.tooltip(_('Subscribe to %s atom feed')% name)}" href="${h.route_path('atom_feed_home', repo_name=name, _query=dict(auth_token=c.rhodecode_user.feed_token))}"><i class="icon-rss-sign"></i></a>
165 165 %else:
166 166 <a title="${h.tooltip(_('Subscribe to %s atom feed')% name)}" href="${h.route_path('atom_feed_home', repo_name=name)}"><i class="icon-rss-sign"></i></a>
167 167 %endif
168 168 </%def>
169 169
170 170 <%def name="repo_actions(repo_name, super_user=True)">
171 171 <div>
172 172 <div class="grid_edit">
173 173 <a href="${h.route_path('edit_repo',repo_name=repo_name)}" title="${_('Edit')}">
174 174 Edit
175 175 </a>
176 176 </div>
177 177 <div class="grid_delete">
178 178 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=repo_name), request=request)}
179 ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger",
180 onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
179 <input class="btn btn-link btn-danger" id="remove_${repo_name}" name="remove_${repo_name}"
180 onclick="submitConfirm(event, this, _gettext('Confirm to delete this repository'), _gettext('Delete'), '${repo_name}')"
181 type="submit" value="Delete"
182 >
181 183 ${h.end_form()}
182 184 </div>
183 185 </div>
184 186 </%def>
185 187
186 188 <%def name="repo_state(repo_state)">
187 189 <div>
188 190 %if repo_state == 'repo_state_pending':
189 191 <div class="tag tag4">${_('Creating')}</div>
190 192 %elif repo_state == 'repo_state_created':
191 193 <div class="tag tag1">${_('Created')}</div>
192 194 %else:
193 195 <div class="tag alert2" title="${h.tooltip(repo_state)}">invalid</div>
194 196 %endif
195 197 </div>
196 198 </%def>
197 199
198 200
199 201 ## REPO GROUP RENDERERS
200 202 <%def name="quick_repo_group_menu(repo_group_name)">
201 203 <i class="icon-more"></i>
202 204 <div class="menu_items_container hidden">
203 205 <ul class="menu_items">
204 206 <li>
205 207 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">${_('Summary')}</a>
206 208 </li>
207 209
208 210 </ul>
209 211 </div>
210 212 </%def>
211 213
212 214 <%def name="repo_group_name(repo_group_name, children_groups=None)">
213 215 <div>
214 216 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">
215 217 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
216 218 %if children_groups:
217 219 ${h.literal(' &raquo; '.join(children_groups))}
218 220 %else:
219 221 ${repo_group_name}
220 222 %endif
221 223 </a>
222 224 </div>
223 225 </%def>
224 226
225 227 <%def name="repo_group_desc(description, personal, stylify_metatags)">
226 228
227 229 <%
228 230 if stylify_metatags:
229 231 tags, description = h.extract_metatags(description)
230 232 %>
231 233
232 234 <div class="truncate-wrap">
233 235 % if personal:
234 236 <div class="metatag" tag="personal">${_('personal')}</div>
235 237 % endif
236 238
237 239 % if stylify_metatags:
238 240 % for tag_type, tag in tags:
239 241 ${h.style_metatag(tag_type, tag)|n}
240 242 % endfor
241 243 % endif
242 244 ${description}
243 245 </div>
244 246
245 247 </%def>
246 248
247 249 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
248 250 <div class="grid_edit">
249 251 <a href="${h.route_path('edit_repo_group',repo_group_name=repo_group_name)}" title="${_('Edit')}">Edit</a>
250 252 </div>
251 253 <div class="grid_delete">
252 254 ${h.secure_form(h.route_path('edit_repo_group_advanced_delete', repo_group_name=repo_group_name), request=request)}
253 ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger",
254 onclick="return confirm('"+_ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")}
255 <input class="btn btn-link btn-danger" id="remove_${repo_group_name}" name="remove_${repo_group_name}"
256 onclick="submitConfirm(event, this, _gettext('Confirm to delete this repository group'), _gettext('Delete'), '${_ungettext('`{}` with {} repository','`{}` with {} repositories',gr_count).format(repo_group_name, gr_count)}')"
257 type="submit" value="Delete"
258 >
255 259 ${h.end_form()}
256 260 </div>
257 261 </%def>
258 262
259 263
260 264 <%def name="user_actions(user_id, username)">
261 265 <div class="grid_edit">
262 266 <a href="${h.route_path('user_edit',user_id=user_id)}" title="${_('Edit')}">
263 267 ${_('Edit')}
264 268 </a>
265 269 </div>
266 270 <div class="grid_delete">
267 271 ${h.secure_form(h.route_path('user_delete', user_id=user_id), request=request)}
268 ${h.submit('remove_',_('Delete'),id="remove_user_%s" % user_id, class_="btn btn-link btn-danger",
269 onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")}
272 <input class="btn btn-link btn-danger" id="remove_user_${user_id}" name="remove_user_${user_id}"
273 onclick="submitConfirm(event, this, _gettext('Confirm to delete this user'), _gettext('Delete'), '${username}')"
274 type="submit" value="Delete"
275 >
270 276 ${h.end_form()}
271 277 </div>
272 278 </%def>
273 279
274 280 <%def name="user_group_actions(user_group_id, user_group_name)">
275 281 <div class="grid_edit">
276 282 <a href="${h.route_path('edit_user_group', user_group_id=user_group_id)}" title="${_('Edit')}">Edit</a>
277 283 </div>
278 284 <div class="grid_delete">
279 285 ${h.secure_form(h.route_path('user_groups_delete', user_group_id=user_group_id), request=request)}
280 ${h.submit('remove_',_('Delete'),id="remove_group_%s" % user_group_id, class_="btn btn-link btn-danger",
281 onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")}
286 <input class="btn btn-link btn-danger" id="remove_group_${user_group_id}" name="remove_group_${user_group_id}"
287 onclick="submitConfirm(event, this, _gettext('Confirm to delete this user group'), _gettext('Delete'), '${user_group_name}')"
288 type="submit" value="Delete"
289 >
282 290 ${h.end_form()}
283 291 </div>
284 292 </%def>
285 293
286 294
287 295 <%def name="user_name(user_id, username)">
288 296 ${h.link_to(h.person(username, 'username_or_name_or_email'), h.route_path('user_edit', user_id=user_id))}
289 297 </%def>
290 298
291 299 <%def name="user_profile(username)">
292 300 ${base.gravatar_with_user(username, 16, tooltip=True)}
293 301 </%def>
294 302
295 303 <%def name="user_group_name(user_group_name)">
296 304 <div>
297 305 <i class="icon-user-group" title="${_('User group')}"></i>
298 306 ${h.link_to_group(user_group_name)}
299 307 </div>
300 308 </%def>
301 309
302 310
303 311 ## GISTS
304 312
305 313 <%def name="gist_gravatar(full_contact)">
306 314 <div class="gist_gravatar">
307 315 ${base.gravatar(full_contact, 30)}
308 316 </div>
309 317 </%def>
310 318
311 319 <%def name="gist_access_id(gist_access_id, full_contact)">
312 320 <div>
313 321 <code>
314 322 <a href="${h.route_path('gist_show', gist_id=gist_access_id)}">${gist_access_id}</a>
315 323 </code>
316 324 </div>
317 325 </%def>
318 326
319 327 <%def name="gist_author(full_contact, created_on, expires)">
320 328 ${base.gravatar_with_user(full_contact, 16, tooltip=True)}
321 329 </%def>
322 330
323 331
324 332 <%def name="gist_created(created_on)">
325 333 <div class="created">
326 334 ${h.age_component(created_on, time_is_local=True)}
327 335 </div>
328 336 </%def>
329 337
330 338 <%def name="gist_expires(expires)">
331 339 <div class="created">
332 340 %if expires == -1:
333 341 ${_('never')}
334 342 %else:
335 343 ${h.age_component(h.time_to_utcdatetime(expires))}
336 344 %endif
337 345 </div>
338 346 </%def>
339 347
340 348 <%def name="gist_type(gist_type)">
341 349 %if gist_type == 'public':
342 350 <span class="tag tag-gist-public disabled">${_('Public Gist')}</span>
343 351 %else:
344 352 <span class="tag tag-gist-private disabled">${_('Private Gist')}</span>
345 353 %endif
346 354 </%def>
347 355
348 356 <%def name="gist_description(gist_description)">
349 357 ${gist_description}
350 358 </%def>
351 359
352 360
353 361 ## PULL REQUESTS GRID RENDERERS
354 362
355 363 <%def name="pullrequest_target_repo(repo_name)">
356 364 <div class="truncate">
357 365 ${h.link_to(repo_name,h.route_path('repo_summary',repo_name=repo_name))}
358 366 </div>
359 367 </%def>
360 368
361 369 <%def name="pullrequest_status(status)">
362 370 <i class="icon-circle review-status-${status}"></i>
363 371 </%def>
364 372
365 373 <%def name="pullrequest_title(title, description)">
366 374 ${title}
367 375 </%def>
368 376
369 377 <%def name="pullrequest_comments(comments_nr)">
370 378 <i class="icon-comment"></i> ${comments_nr}
371 379 </%def>
372 380
373 381 <%def name="pullrequest_name(pull_request_id, state, is_wip, target_repo_name, short=False)">
374 382 <a href="${h.route_path('pullrequest_show',repo_name=target_repo_name,pull_request_id=pull_request_id)}">
375 383
376 384 % if short:
377 385 !${pull_request_id}
378 386 % else:
379 387 ${_('Pull request !{}').format(pull_request_id)}
380 388 % endif
381 389
382 390 % if state not in ['created']:
383 391 <span class="tag tag-merge-state-${state} tooltip" title="Pull request state is changing">${state}</span>
384 392 % endif
385 393
386 394 % if is_wip:
387 395 <span class="tag tooltip" title="${_('Work in progress')}">wip</span>
388 396 % endif
389 397 </a>
390 398 </%def>
391 399
392 400 <%def name="pullrequest_updated_on(updated_on)">
393 401 ${h.age_component(h.time_to_utcdatetime(updated_on))}
394 402 </%def>
395 403
396 404 <%def name="pullrequest_author(full_contact)">
397 405 ${base.gravatar_with_user(full_contact, 16, tooltip=True)}
398 406 </%def>
399 407
400 408
401 409 ## ARTIFACT RENDERERS
402 410 <%def name="repo_artifact_name(repo_name, file_uid, artifact_display_name)">
403 411 <a href="${h.route_path('repo_artifacts_get', repo_name=repo_name, uid=file_uid)}">
404 412 ${artifact_display_name or '_EMPTY_NAME_'}
405 413 </a>
406 414 </%def>
407 415
408 416 <%def name="repo_artifact_uid(repo_name, file_uid)">
409 417 <code>${h.shorter(file_uid, size=24, prefix=True)}</code>
410 418 </%def>
411 419
412 420 <%def name="repo_artifact_sha256(artifact_sha256)">
413 421 <div class="code">${h.shorter(artifact_sha256, 12)}</div>
414 422 </%def>
415 423
416 424 <%def name="repo_artifact_actions(repo_name, file_store_id, file_uid)">
417 425 ## <div class="grid_edit">
418 426 ## <a href="#Edit" title="${_('Edit')}">${_('Edit')}</a>
419 427 ## </div>
420 428 <div class="grid_edit">
421 429 <a href="${h.route_path('repo_artifacts_info', repo_name=repo_name, uid=file_store_id)}" title="${_('Info')}">${_('Info')}</a>
422 430 </div>
423 431 % if h.HasRepoPermissionAny('repository.admin')(c.repo_name):
424 432 <div class="grid_delete">
425 433 ${h.secure_form(h.route_path('repo_artifacts_delete', repo_name=repo_name, uid=file_store_id), request=request)}
426 ${h.submit('remove_',_('Delete'),id="remove_artifact_%s" % file_store_id, class_="btn btn-link btn-danger",
427 onclick="return confirm('"+_('Confirm to delete this artifact: %s') % file_uid+"');")}
434 <input class="btn btn-link btn-danger" id="remove_artifact_${file_store_id}" name="remove_artifact_${file_store_id}"
435 onclick="submitConfirm(event, this, _gettext('Confirm to delete this artifact'), _gettext('Delete'), '${file_uid}')"
436 type="submit" value="${_('Delete')}"
437 >
428 438 ${h.end_form()}
429 439 </div>
430 440 % endif
431 441 </%def>
432 442
433 443 <%def name="markup_form(form_id, form_text='', help_text=None)">
434 444
435 445 <div class="markup-form">
436 446 <div class="markup-form-area">
437 447 <div class="markup-form-area-header">
438 448 <ul class="nav-links clearfix">
439 449 <li class="active">
440 450 <a href="#edit-text" tabindex="-1" id="edit-btn_${form_id}">${_('Write')}</a>
441 451 </li>
442 452 <li class="">
443 453 <a href="#preview-text" tabindex="-1" id="preview-btn_${form_id}">${_('Preview')}</a>
444 454 </li>
445 455 </ul>
446 456 </div>
447 457
448 458 <div class="markup-form-area-write" style="display: block;">
449 459 <div id="edit-container_${form_id}">
450 460 <textarea id="${form_id}" name="${form_id}" class="comment-block-ta ac-input">${form_text if form_text else ''}</textarea>
451 461 </div>
452 462 <div id="preview-container_${form_id}" class="clearfix" style="display: none;">
453 463 <div id="preview-box_${form_id}" class="preview-box"></div>
454 464 </div>
455 465 </div>
456 466
457 467 <div class="markup-form-area-footer">
458 468 <div class="toolbar">
459 469 <div class="toolbar-text">
460 470 ${(_('Parsed using %s syntax') % (
461 471 ('<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
462 472 )
463 473 )|n}
464 474 </div>
465 475 </div>
466 476 </div>
467 477 </div>
468 478
469 479 <div class="markup-form-footer">
470 480 % if help_text:
471 481 <span class="help-block">${help_text}</span>
472 482 % endif
473 483 </div>
474 484 </div>
475 485 <script type="text/javascript">
476 486 new MarkupForm('${form_id}');
477 487 </script>
478 488
479 489 </%def>
@@ -1,911 +1,912 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 </%def>
15 15
16 16 <%def name="menu_bar_nav()">
17 17 ${self.menu_items(active='repositories')}
18 18 </%def>
19 19
20 20 <%def name="menu_bar_subnav()">
21 21 ${self.repo_menu(active='showpullrequest')}
22 22 </%def>
23 23
24 24 <%def name="main()">
25 25
26 26 <script type="text/javascript">
27 27 // TODO: marcink switch this to pyroutes
28 28 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__')}";
29 29 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
30 30 </script>
31 31
32 32 <div class="box">
33 33
34 34 <div class="box pr-summary">
35 35
36 36 <div class="summary-details block-left">
37 37 <div id="pr-title">
38 38 % if c.pull_request.is_closed():
39 39 <span class="pr-title-closed-tag tag">${_('Closed')}</span>
40 40 % endif
41 41 <input class="pr-title-input large disabled" disabled="disabled" name="pullrequest_title" type="text" value="${c.pull_request.title}">
42 42 </div>
43 43 <div id="pr-title-edit" class="input" style="display: none;">
44 44 <input class="pr-title-input large" id="pr-title-input" name="pullrequest_title" type="text" value="${c.pull_request.title}">
45 45 </div>
46 46
47 47 <% summary = lambda n:{False:'summary-short'}.get(n) %>
48 48 <div class="pr-details-title">
49 49 <div class="pull-left">
50 50 <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>
51 51 ${_('Created on')}
52 52 <span class="tooltip" title="${_('Last updated on')} ${h.format_date(c.pull_request.updated_on)}">${h.format_date(c.pull_request.created_on)},</span>
53 53 <span class="pr-details-title-author-pref">${_('by')}</span>
54 54 </div>
55 55
56 56 <div class="pull-left">
57 57 ${self.gravatar_with_user(c.pull_request.author.email, 16, tooltip=True)}
58 58 </div>
59 59
60 60 %if c.allowed_to_update:
61 61 <div class="pull-right">
62 62 <div id="edit_pull_request" class="action_button pr-save" style="display: none;">${_('Update title & description')}</div>
63 63 <div id="delete_pullrequest" class="action_button pr-save ${('' if c.allowed_to_delete else 'disabled' )}" style="display: none;">
64 64 % if c.allowed_to_delete:
65 65 ${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)}
66 ${h.submit('remove_%s' % c.pull_request.pull_request_id, _('Delete pull request'),
67 class_="btn btn-link btn-danger no-margin",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")}
66 <input class="btn btn-link btn-danger no-margin" id="remove_${c.pull_request.pull_request_id}" name="remove_${c.pull_request.pull_request_id}"
67 onclick="submitConfirm(event, this, _gettext('Confirm to delete this pull request'), _gettext('Delete'), '${'!{}'.format(c.pull_request.pull_request_id)}')"
68 type="submit" value="${_('Delete pull request')}">
68 69 ${h.end_form()}
69 70 % else:
70 71 <span class="tooltip" title="${_('Not allowed to delete this pull request')}">${_('Delete pull request')}</span>
71 72 % endif
72 73 </div>
73 74 <div id="open_edit_pullrequest" class="action_button">${_('Edit')}</div>
74 75 <div id="close_edit_pullrequest" class="action_button" style="display: none;">${_('Cancel')}</div>
75 76 </div>
76 77
77 78 %endif
78 79 </div>
79 80
80 81 <div id="pr-desc" class="input" title="${_('Rendered using {} renderer').format(c.renderer)}">
81 82 ${h.render(c.pull_request.description, renderer=c.renderer, repo_name=c.repo_name)}
82 83 </div>
83 84
84 85 <div id="pr-desc-edit" class="input textarea" style="display: none;">
85 86 <input id="pr-renderer-input" type="hidden" name="description_renderer" value="${c.visual.default_renderer}">
86 87 ${dt.markup_form('pr-description-input', form_text=c.pull_request.description)}
87 88 </div>
88 89
89 90 <div id="summary" class="fields pr-details-content">
90 91
91 92 ## review
92 93 <div class="field">
93 94 <div class="label-pr-detail">
94 95 <label>${_('Review status')}:</label>
95 96 </div>
96 97 <div class="input">
97 98 %if c.pull_request_review_status:
98 99 <div class="tag status-tag-${c.pull_request_review_status}">
99 100 <i class="icon-circle review-status-${c.pull_request_review_status}"></i>
100 101 <span class="changeset-status-lbl">
101 102 %if c.pull_request.is_closed():
102 103 ${_('Closed')},
103 104 %endif
104 105
105 106 ${h.commit_status_lbl(c.pull_request_review_status)}
106 107
107 108 </span>
108 109 </div>
109 110 - ${_ungettext('calculated based on {} reviewer vote', 'calculated based on {} reviewers votes', len(c.pull_request_reviewers)).format(len(c.pull_request_reviewers))}
110 111 %endif
111 112 </div>
112 113 </div>
113 114
114 115 ## source
115 116 <div class="field">
116 117 <div class="label-pr-detail">
117 118 <label>${_('Commit flow')}:</label>
118 119 </div>
119 120 <div class="input">
120 121 <div class="pr-commit-flow">
121 122 ## Source
122 123 %if c.pull_request.source_ref_parts.type == 'branch':
123 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>
124 125 %else:
125 126 <code class="pr-source-info">${'{}:{}'.format(c.pull_request.source_ref_parts.type, c.pull_request.source_ref_parts.name)}</code>
126 127 %endif
127 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>
128 129 &rarr;
129 130 ## Target
130 131 %if c.pull_request.target_ref_parts.type == 'branch':
131 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>
132 133 %else:
133 134 <code class="pr-target-info">${'{}:{}'.format(c.pull_request.target_ref_parts.type, c.pull_request.target_ref_parts.name)}</code>
134 135 %endif
135 136
136 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>
137 138
138 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>'>
139 140 <i class="icon-angle-down">more details</i>
140 141 </a>
141 142
142 143 </div>
143 144
144 145 <div class="source-details" style="display: none">
145 146
146 147 <ul>
147 148
148 149 ## common ancestor
149 150 <li>
150 151 ${_('Common ancestor')}:
151 152 % if c.ancestor_commit:
152 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>
153 154 % else:
154 155 ${_('not available')}
155 156 % endif
156 157 </li>
157 158
158 159 ## pull url
159 160 <li>
160 161 %if h.is_hg(c.pull_request.source_repo):
161 162 <% clone_url = 'hg pull -r {} {}'.format(h.short_id(c.source_ref), c.pull_request.source_repo.clone_url()) %>
162 163 %elif h.is_git(c.pull_request.source_repo):
163 164 <% clone_url = 'git pull {} {}'.format(c.pull_request.source_repo.clone_url(), c.pull_request.source_ref_parts.name) %>
164 165 %endif
165 166
166 167 <span>${_('Pull changes from source')}</span>: <input type="text" class="input-monospace pr-pullinfo" value="${clone_url}" readonly="readonly">
167 168 <i class="tooltip icon-clipboard clipboard-action pull-right pr-pullinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the pull url')}"></i>
168 169 </li>
169 170
170 171 ## Shadow repo
171 172 <li>
172 173 % if not c.pull_request.is_closed() and c.pull_request.shadow_merge_ref:
173 174 %if h.is_hg(c.pull_request.target_repo):
174 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) %>
175 176 %elif h.is_git(c.pull_request.target_repo):
176 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) %>
177 178 %endif
178 179
179 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">
180 181 <i class="tooltip icon-clipboard clipboard-action pull-right pr-mergeinfo-copy" data-clipboard-text="${clone_url}" title="${_('Copy the clone url')}"></i>
181 182
182 183 % else:
183 184 <div class="">
184 185 ${_('Shadow repository data not available')}.
185 186 </div>
186 187 % endif
187 188 </li>
188 189
189 190 </ul>
190 191
191 192 </div>
192 193
193 194 </div>
194 195
195 196 </div>
196 197
197 198 ## versions
198 199 <div class="field">
199 200 <div class="label-pr-detail">
200 201 <label>${_('Versions')}:</label>
201 202 </div>
202 203
203 204 <% outdated_comm_count_ver = len(c.inline_versions[None]['outdated']) %>
204 205 <% general_outdated_comm_count_ver = len(c.comment_versions[None]['outdated']) %>
205 206
206 207 <div class="pr-versions">
207 208 % if c.show_version_changes:
208 209 <% outdated_comm_count_ver = len(c.inline_versions[c.at_version_num]['outdated']) %>
209 210 <% general_outdated_comm_count_ver = len(c.comment_versions[c.at_version_num]['outdated']) %>
210 211 ${_ungettext('{} version available for this pull request, ', '{} versions available for this pull request, ', len(c.versions)).format(len(c.versions))}
211 212 <a id="show-pr-versions" onclick="return versionController.toggleVersionView(this)" href="#show-pr-versions"
212 213 data-toggle-on="${_('show versions')}."
213 214 data-toggle-off="${_('hide versions')}.">
214 215 ${_('show versions')}.
215 216 </a>
216 217 <table>
217 218 ## SHOW ALL VERSIONS OF PR
218 219 <% ver_pr = None %>
219 220
220 221 % for data in reversed(list(enumerate(c.versions, 1))):
221 222 <% ver_pos = data[0] %>
222 223 <% ver = data[1] %>
223 224 <% ver_pr = ver.pull_request_version_id %>
224 225 <% display_row = '' if c.at_version and (c.at_version_num == ver_pr or c.from_version_num == ver_pr) else 'none' %>
225 226
226 227 <tr class="version-pr" style="display: ${display_row}">
227 228 <td>
228 229 <code>
229 230 <a href="${request.current_route_path(_query=dict(version=ver_pr or 'latest'))}">v${ver_pos}</a>
230 231 </code>
231 232 </td>
232 233 <td>
233 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}"/>
234 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}"/>
235 236 </td>
236 237 <td>
237 238 <% review_status = c.review_versions[ver_pr].status if ver_pr in c.review_versions else 'not_reviewed' %>
238 239 <i class="tooltip icon-circle review-status-${review_status}" title="${_('Your review status at this version')}"></i>
239 240
240 241 </td>
241 242 <td>
242 243 % if c.at_version_num != ver_pr:
243 244 <i class="tooltip icon-comment" title="${_('Comments from pull request version v{0}').format(ver_pos)}"></i>
244 245 <code>
245 246 General:${len(c.comment_versions[ver_pr]['at'])} / Inline:${len(c.inline_versions[ver_pr]['at'])}
246 247 </code>
247 248 % endif
248 249 </td>
249 250 <td>
250 251 ##<code>${ver.source_ref_parts.commit_id[:6]}</code>
251 252 </td>
252 253 <td>
253 254 <code>${h.age_component(ver.updated_on, time_is_local=True, tooltip=False)}</code>
254 255 </td>
255 256 </tr>
256 257 % endfor
257 258
258 259 <tr>
259 260 <td colspan="6">
260 261 <button id="show-version-diff" onclick="return versionController.showVersionDiff()" class="btn btn-sm" style="display: none"
261 262 data-label-text-locked="${_('select versions to show changes')}"
262 263 data-label-text-diff="${_('show changes between versions')}"
263 264 data-label-text-show="${_('show pull request for this version')}"
264 265 >
265 266 ${_('select versions to show changes')}
266 267 </button>
267 268 </td>
268 269 </tr>
269 270 </table>
270 271 % else:
271 272 <div>
272 273 ${_('Pull request versions not available')}.
273 274 </div>
274 275 % endif
275 276 </div>
276 277 </div>
277 278
278 279 </div>
279 280
280 281 </div>
281 282
282 283 ## REVIEW RULES
283 284 <div id="review_rules" style="display: none" class="reviewers-title block-right">
284 285 <div class="pr-details-title">
285 286 ${_('Reviewer rules')}
286 287 %if c.allowed_to_update:
287 288 <span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
288 289 %endif
289 290 </div>
290 291 <div class="pr-reviewer-rules">
291 292 ## review rules will be appended here, by default reviewers logic
292 293 </div>
293 294 <input id="review_data" type="hidden" name="review_data" value="">
294 295 </div>
295 296
296 297 ## REVIEWERS
297 298 <div class="reviewers-title first-panel block-right">
298 299 <div class="pr-details-title">
299 300 ${_('Pull request reviewers')}
300 301 %if c.allowed_to_update:
301 302 <span id="open_edit_reviewers" class="block-right action_button last-item">${_('Edit')}</span>
302 303 %endif
303 304 </div>
304 305 </div>
305 306 <div id="reviewers" class="block-right pr-details-content reviewers">
306 307
307 308 ## members redering block
308 309 <input type="hidden" name="__start__" value="review_members:sequence">
309 310 <ul id="review_members" class="group_members">
310 311
311 312 % for review_obj, member, reasons, mandatory, status in c.pull_request_reviewers:
312 313 <script>
313 314 var member = ${h.json.dumps(h.reviewer_as_json(member, reasons=reasons, mandatory=mandatory, user_group=review_obj.rule_user_group_data()))|n};
314 315 var status = "${(status[0][1].status if status else 'not_reviewed')}";
315 316 var status_lbl = "${h.commit_status_lbl(status[0][1].status if status else 'not_reviewed')}";
316 317 var allowed_to_update = ${h.json.dumps(c.allowed_to_update)};
317 318
318 319 var entry = renderTemplate('reviewMemberEntry', {
319 320 'member': member,
320 321 'mandatory': member.mandatory,
321 322 'reasons': member.reasons,
322 323 'allowed_to_update': allowed_to_update,
323 324 'review_status': status,
324 325 'review_status_label': status_lbl,
325 326 'user_group': member.user_group,
326 327 'create': false
327 328 });
328 329 $('#review_members').append(entry)
329 330 </script>
330 331
331 332 % endfor
332 333
333 334 </ul>
334 335
335 336 <input type="hidden" name="__end__" value="review_members:sequence">
336 337 ## end members redering block
337 338
338 339 %if not c.pull_request.is_closed():
339 340 <div id="add_reviewer" class="ac" style="display: none;">
340 341 %if c.allowed_to_update:
341 342 % if not c.forbid_adding_reviewers:
342 343 <div id="add_reviewer_input" class="reviewer_ac">
343 344 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
344 345 <div id="reviewers_container"></div>
345 346 </div>
346 347 % endif
347 348 <div class="pull-right">
348 349 <button id="update_pull_request" class="btn btn-small no-margin">${_('Save Changes')}</button>
349 350 </div>
350 351 %endif
351 352 </div>
352 353 %endif
353 354 </div>
354 355
355 356 ## TODOs will be listed here
356 357 <div class="reviewers-title block-right">
357 358 <div class="pr-details-title">
358 359 ## Only show unresolved, that is only what matters
359 360 TODO Comments - ${len(c.unresolved_comments)} / ${(len(c.unresolved_comments) + len(c.resolved_comments))}
360 361
361 362 % if not c.at_version:
362 363 % if c.resolved_comments:
363 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>
364 365 % else:
365 366 <span class="block-right last-item noselect">Show resolved</span>
366 367 % endif
367 368 % endif
368 369 </div>
369 370 </div>
370 371 <div class="block-right pr-details-content reviewers">
371 372
372 373 <table class="todo-table">
373 374 <%
374 375 def sorter(entry):
375 376 user_id = entry.author.user_id
376 377 resolved = '1' if entry.resolved else '0'
377 378 if user_id == c.rhodecode_user.user_id:
378 379 # own comments first
379 380 user_id = 0
380 381 return '{}_{}_{}'.format(resolved, user_id, str(entry.comment_id).zfill(100))
381 382 %>
382 383
383 384 % if c.at_version:
384 385 <tr>
385 386 <td class="unresolved-todo-text">${_('unresolved TODOs unavailable in this view')}.</td>
386 387 </tr>
387 388 % else:
388 389 % for todo_comment in sorted(c.unresolved_comments + c.resolved_comments, key=sorter):
389 390 <% resolved = todo_comment.resolved %>
390 391 % if inline:
391 392 <% outdated_at_ver = todo_comment.outdated_at_version(getattr(c, 'at_version_num', None)) %>
392 393 % else:
393 394 <% outdated_at_ver = todo_comment.older_than_version(getattr(c, 'at_version_num', None)) %>
394 395 % endif
395 396
396 397 <tr ${('class="unresolved-todo" style="display: none"' if resolved else '') |n}>
397 398
398 399 <td class="td-todo-number">
399 400 % if resolved:
400 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)})">
401 402 <i class="icon-flag-filled"></i> ${todo_comment.comment_id}</a>
402 403 % else:
403 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)})">
404 405 <i class="icon-flag-filled"></i> ${todo_comment.comment_id}</a>
405 406 % endif
406 407 </td>
407 408 <td class="td-todo-gravatar">
408 409 ${base.gravatar(todo_comment.author.email, 16, user=todo_comment.author, tooltip=True, extra_class=['no-margin'])}
409 410 </td>
410 411 <td class="todo-comment-text-wrapper">
411 412 <div class="todo-comment-text">
412 413 <code>${h.chop_at_smart(todo_comment.text, '\n', suffix_if_chopped='...')}</code>
413 414 </div>
414 415 </td>
415 416
416 417 </tr>
417 418 % endfor
418 419
419 420 % if len(c.unresolved_comments) == 0:
420 421 <tr>
421 422 <td class="unresolved-todo-text">${_('No unresolved TODOs')}.</td>
422 423 </tr>
423 424 % endif
424 425
425 426 % endif
426 427
427 428 </table>
428 429
429 430 </div>
430 431 </div>
431 432
432 433 </div>
433 434
434 435 <div class="box">
435 436
436 437 % if c.state_progressing:
437 438
438 439 <h2 style="text-align: center">
439 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>
440 441
441 442 % if c.is_super_admin:
442 443 <br/>
443 444 If you think this is an error try <a href="${h.current_route_path(request, force_state='created')}">forced state reset</a> to <span class="tag tag-merge-state-created">created</span> state.
444 445 % endif
445 446 </h2>
446 447
447 448 % else:
448 449
449 450 ## Diffs rendered here
450 451 <div class="table" >
451 452 <div id="changeset_compare_view_content">
452 453 ##CS
453 454 % if c.missing_requirements:
454 455 <div class="box">
455 456 <div class="alert alert-warning">
456 457 <div>
457 458 <strong>${_('Missing requirements:')}</strong>
458 459 ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')}
459 460 </div>
460 461 </div>
461 462 </div>
462 463 % elif c.missing_commits:
463 464 <div class="box">
464 465 <div class="alert alert-warning">
465 466 <div>
466 467 <strong>${_('Missing commits')}:</strong>
467 468 ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}
468 469 ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}
469 470 ${_('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}
470 471 </div>
471 472 </div>
472 473 </div>
473 474 % endif
474 475
475 476 <div class="compare_view_commits_title">
476 477 % if not c.compare_mode:
477 478
478 479 % if c.at_version_pos:
479 480 <h4>
480 481 ${_('Showing changes at v%d, commenting is disabled.') % c.at_version_pos}
481 482 </h4>
482 483 % endif
483 484
484 485 <div class="pull-left">
485 486 <div class="btn-group">
486 487 <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)} >
487 488 % if c.collapse_all_commits:
488 489 <i class="icon-plus-squared-alt icon-no-margin"></i>
489 490 ${_ungettext('Expand {} commit', 'Expand {} commits', len(c.commit_ranges)).format(len(c.commit_ranges))}
490 491 % else:
491 492 <i class="icon-minus-squared-alt icon-no-margin"></i>
492 493 ${_ungettext('Collapse {} commit', 'Collapse {} commits', len(c.commit_ranges)).format(len(c.commit_ranges))}
493 494 % endif
494 495 </a>
495 496 </div>
496 497 </div>
497 498
498 499 <div class="pull-right">
499 500 % if c.allowed_to_update and not c.pull_request.is_closed():
500 501
501 502 <div class="btn-group btn-group-actions">
502 503 <a id="update_commits" class="btn btn-primary no-margin" onclick="updateController.updateCommits(this); return false">
503 504 ${_('Update commits')}
504 505 </a>
505 506
506 507 <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')}">
507 508 <i class="icon-down"></i>
508 509 </a>
509 510
510 511 <div class="btn-action-switcher-container" id="update-commits-switcher">
511 512 <ul class="btn-action-switcher" role="menu">
512 513 <li>
513 514 <a href="#forceUpdate" onclick="updateController.forceUpdateCommits(this); return false">
514 515 ${_('Force update commits')}
515 516 </a>
516 517 <div class="action-help-block">
517 518 ${_('Update commits and force refresh this pull request.')}
518 519 </div>
519 520 </li>
520 521 </ul>
521 522 </div>
522 523 </div>
523 524
524 525 % else:
525 526 <a class="tooltip btn disabled pull-right" disabled="disabled" title="${_('Update is disabled for current view')}">${_('Update commits')}</a>
526 527 % endif
527 528
528 529 </div>
529 530 % endif
530 531 </div>
531 532
532 533 % if not c.missing_commits:
533 534 % if c.compare_mode:
534 535 % if c.at_version:
535 536 <h4>
536 537 ${_('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')}:
537 538 </h4>
538 539
539 540 <div class="subtitle-compare">
540 541 ${_('commits added: {}, removed: {}').format(len(c.commit_changes_summary.added), len(c.commit_changes_summary.removed))}
541 542 </div>
542 543
543 544 <div class="container">
544 545 <table class="rctable compare_view_commits">
545 546 <tr>
546 547 <th></th>
547 548 <th>${_('Time')}</th>
548 549 <th>${_('Author')}</th>
549 550 <th>${_('Commit')}</th>
550 551 <th></th>
551 552 <th>${_('Description')}</th>
552 553 </tr>
553 554
554 555 % for c_type, commit in c.commit_changes:
555 556 % if c_type in ['a', 'r']:
556 557 <%
557 558 if c_type == 'a':
558 559 cc_title = _('Commit added in displayed changes')
559 560 elif c_type == 'r':
560 561 cc_title = _('Commit removed in displayed changes')
561 562 else:
562 563 cc_title = ''
563 564 %>
564 565 <tr id="row-${commit.raw_id}" commit_id="${commit.raw_id}" class="compare_select">
565 566 <td>
566 567 <div class="commit-change-indicator color-${c_type}-border">
567 568 <div class="commit-change-content color-${c_type} tooltip" title="${h.tooltip(cc_title)}">
568 569 ${c_type.upper()}
569 570 </div>
570 571 </div>
571 572 </td>
572 573 <td class="td-time">
573 574 ${h.age_component(commit.date)}
574 575 </td>
575 576 <td class="td-user">
576 577 ${base.gravatar_with_user(commit.author, 16, tooltip=True)}
577 578 </td>
578 579 <td class="td-hash">
579 580 <code>
580 581 <a href="${h.route_path('repo_commit', repo_name=c.target_repo.repo_name, commit_id=commit.raw_id)}">
581 582 r${commit.idx}:${h.short_id(commit.raw_id)}
582 583 </a>
583 584 ${h.hidden('revisions', commit.raw_id)}
584 585 </code>
585 586 </td>
586 587 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_( 'Expand commit message')}" onclick="commitsController.expandCommit(this); return false">
587 588 <i class="icon-expand-linked"></i>
588 589 </td>
589 590 <td class="mid td-description">
590 591 <div class="log-container truncate-wrap">
591 592 <div class="message truncate" id="c-${commit.raw_id}" data-message-raw="${commit.message}">${h.urlify_commit_message(commit.message, c.repo_name)}</div>
592 593 </div>
593 594 </td>
594 595 </tr>
595 596 % endif
596 597 % endfor
597 598 </table>
598 599 </div>
599 600
600 601 % endif
601 602
602 603 % else:
603 604 <%include file="/compare/compare_commits.mako" />
604 605 % endif
605 606
606 607 <div class="cs_files">
607 608 <%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/>
608 609 % if c.at_version:
609 610 <% c.inline_cnt = len(c.inline_versions[c.at_version_num]['display']) %>
610 611 <% c.comments = c.comment_versions[c.at_version_num]['display'] %>
611 612 % else:
612 613 <% c.inline_cnt = len(c.inline_versions[c.at_version_num]['until']) %>
613 614 <% c.comments = c.comment_versions[c.at_version_num]['until'] %>
614 615 % endif
615 616
616 617 <%
617 618 pr_menu_data = {
618 619 'outdated_comm_count_ver': outdated_comm_count_ver
619 620 }
620 621 %>
621 622
622 623 ${cbdiffs.render_diffset_menu(c.diffset, range_diff_on=c.range_diff_on)}
623 624
624 625 % if c.range_diff_on:
625 626 % for commit in c.commit_ranges:
626 627 ${cbdiffs.render_diffset(
627 628 c.changes[commit.raw_id],
628 629 commit=commit, use_comments=True,
629 630 collapse_when_files_over=5,
630 631 disable_new_comments=True,
631 632 deleted_files_comments=c.deleted_files_comments,
632 633 inline_comments=c.inline_comments,
633 634 pull_request_menu=pr_menu_data, show_todos=False)}
634 635 % endfor
635 636 % else:
636 637 ${cbdiffs.render_diffset(
637 638 c.diffset, use_comments=True,
638 639 collapse_when_files_over=30,
639 640 disable_new_comments=not c.allowed_to_comment,
640 641 deleted_files_comments=c.deleted_files_comments,
641 642 inline_comments=c.inline_comments,
642 643 pull_request_menu=pr_menu_data, show_todos=False)}
643 644 % endif
644 645
645 646 </div>
646 647 % else:
647 648 ## skipping commits we need to clear the view for missing commits
648 649 <div style="clear:both;"></div>
649 650 % endif
650 651
651 652 </div>
652 653 </div>
653 654
654 655 ## template for inline comment form
655 656 <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
656 657
657 658 ## comments heading with count
658 659 <div class="comments-heading">
659 660 <i class="icon-comment"></i>
660 661 ${_('Comments')} ${len(c.comments)}
661 662 </div>
662 663
663 664 ## render general comments
664 665 <div id="comment-tr-show">
665 666 % if general_outdated_comm_count_ver:
666 667 <div class="info-box">
667 668 % if general_outdated_comm_count_ver == 1:
668 669 ${_('there is {num} general comment from older versions').format(num=general_outdated_comm_count_ver)},
669 670 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show it')}</a>
670 671 % else:
671 672 ${_('there are {num} general comments from older versions').format(num=general_outdated_comm_count_ver)},
672 673 <a href="#show-hidden-comments" onclick="$('.comment-general.comment-outdated').show(); $(this).parent().hide(); return false;">${_('show them')}</a>
673 674 % endif
674 675 </div>
675 676 % endif
676 677 </div>
677 678
678 679 ${comment.generate_comments(c.comments, include_pull_request=True, is_pull_request=True)}
679 680
680 681 % if not c.pull_request.is_closed():
681 682 ## main comment form and it status
682 683 ${comment.comments(h.route_path('pullrequest_comment_create', repo_name=c.repo_name,
683 684 pull_request_id=c.pull_request.pull_request_id),
684 685 c.pull_request_review_status,
685 686 is_pull_request=True, change_status=c.allowed_to_change_status)}
686 687
687 688 ## merge status, and merge action
688 689 <div class="pull-request-merge">
689 690 <%include file="/pullrequests/pullrequest_merge_checks.mako"/>
690 691 </div>
691 692
692 693 %endif
693 694
694 695 % endif
695 696 </div>
696 697
697 698 <script type="text/javascript">
698 699
699 700 versionController = new VersionController();
700 701 versionController.init();
701 702
702 703 reviewersController = new ReviewersController();
703 704 commitsController = new CommitsController();
704 705
705 706 updateController = new UpdatePrController();
706 707
707 708 $(function () {
708 709
709 710 // custom code mirror
710 711 var codeMirrorInstance = $('#pr-description-input').get(0).MarkupForm.cm;
711 712
712 713 var PRDetails = {
713 714 editButton: $('#open_edit_pullrequest'),
714 715 closeButton: $('#close_edit_pullrequest'),
715 716 deleteButton: $('#delete_pullrequest'),
716 717 viewFields: $('#pr-desc, #pr-title'),
717 718 editFields: $('#pr-desc-edit, #pr-title-edit, .pr-save'),
718 719
719 720 init: function () {
720 721 var that = this;
721 722 this.editButton.on('click', function (e) {
722 723 that.edit();
723 724 });
724 725 this.closeButton.on('click', function (e) {
725 726 that.view();
726 727 });
727 728 },
728 729
729 730 edit: function (event) {
730 731 this.viewFields.hide();
731 732 this.editButton.hide();
732 733 this.deleteButton.hide();
733 734 this.closeButton.show();
734 735 this.editFields.show();
735 736 codeMirrorInstance.refresh();
736 737 },
737 738
738 739 view: function (event) {
739 740 this.editButton.show();
740 741 this.deleteButton.show();
741 742 this.editFields.hide();
742 743 this.closeButton.hide();
743 744 this.viewFields.show();
744 745 }
745 746 };
746 747
747 748 var ReviewersPanel = {
748 749 editButton: $('#open_edit_reviewers'),
749 750 closeButton: $('#close_edit_reviewers'),
750 751 addButton: $('#add_reviewer'),
751 752 removeButtons: $('.reviewer_member_remove,.reviewer_member_mandatory_remove'),
752 753
753 754 init: function () {
754 755 var self = this;
755 756 this.editButton.on('click', function (e) {
756 757 self.edit();
757 758 });
758 759 this.closeButton.on('click', function (e) {
759 760 self.close();
760 761 });
761 762 },
762 763
763 764 edit: function (event) {
764 765 this.editButton.hide();
765 766 this.closeButton.show();
766 767 this.addButton.show();
767 768 this.removeButtons.css('visibility', 'visible');
768 769 // review rules
769 770 reviewersController.loadReviewRules(
770 771 ${c.pull_request.reviewer_data_json | n});
771 772 },
772 773
773 774 close: function (event) {
774 775 this.editButton.show();
775 776 this.closeButton.hide();
776 777 this.addButton.hide();
777 778 this.removeButtons.css('visibility', 'hidden');
778 779 // hide review rules
779 780 reviewersController.hideReviewRules()
780 781 }
781 782 };
782 783
783 784 PRDetails.init();
784 785 ReviewersPanel.init();
785 786
786 787 showOutdated = function (self) {
787 788 $('.comment-inline.comment-outdated').show();
788 789 $('.filediff-outdated').show();
789 790 $('.showOutdatedComments').hide();
790 791 $('.hideOutdatedComments').show();
791 792 };
792 793
793 794 hideOutdated = function (self) {
794 795 $('.comment-inline.comment-outdated').hide();
795 796 $('.filediff-outdated').hide();
796 797 $('.hideOutdatedComments').hide();
797 798 $('.showOutdatedComments').show();
798 799 };
799 800
800 801 refreshMergeChecks = function () {
801 802 var loadUrl = "${request.current_route_path(_query=dict(merge_checks=1))}";
802 803 $('.pull-request-merge').css('opacity', 0.3);
803 804 $('.action-buttons-extra').css('opacity', 0.3);
804 805
805 806 $('.pull-request-merge').load(
806 807 loadUrl, function () {
807 808 $('.pull-request-merge').css('opacity', 1);
808 809
809 810 $('.action-buttons-extra').css('opacity', 1);
810 811 }
811 812 );
812 813 };
813 814
814 815 closePullRequest = function (status) {
815 816 if (!confirm(_gettext('Are you sure to close this pull request without merging?'))) {
816 817 return false;
817 818 }
818 819 // inject closing flag
819 820 $('.action-buttons-extra').append('<input type="hidden" class="close-pr-input" id="close_pull_request" value="1">');
820 821 $(generalCommentForm.statusChange).select2("val", status).trigger('change');
821 822 $(generalCommentForm.submitForm).submit();
822 823 };
823 824
824 825 $('#show-outdated-comments').on('click', function (e) {
825 826 var button = $(this);
826 827 var outdated = $('.comment-outdated');
827 828
828 829 if (button.html() === "(Show)") {
829 830 button.html("(Hide)");
830 831 outdated.show();
831 832 } else {
832 833 button.html("(Show)");
833 834 outdated.hide();
834 835 }
835 836 });
836 837
837 838 $('.show-inline-comments').on('change', function (e) {
838 839 var show = 'none';
839 840 var target = e.currentTarget;
840 841 if (target.checked) {
841 842 show = ''
842 843 }
843 844 var boxid = $(target).attr('id_for');
844 845 var comments = $('#{0} .inline-comments'.format(boxid));
845 846 var fn_display = function (idx) {
846 847 $(this).css('display', show);
847 848 };
848 849 $(comments).each(fn_display);
849 850 var btns = $('#{0} .inline-comments-button'.format(boxid));
850 851 $(btns).each(fn_display);
851 852 });
852 853
853 854 $('#merge_pull_request_form').submit(function () {
854 855 if (!$('#merge_pull_request').attr('disabled')) {
855 856 $('#merge_pull_request').attr('disabled', 'disabled');
856 857 }
857 858 return true;
858 859 });
859 860
860 861 $('#edit_pull_request').on('click', function (e) {
861 862 var title = $('#pr-title-input').val();
862 863 var description = codeMirrorInstance.getValue();
863 864 var renderer = $('#pr-renderer-input').val();
864 865 editPullRequest(
865 866 "${c.repo_name}", "${c.pull_request.pull_request_id}",
866 867 title, description, renderer);
867 868 });
868 869
869 870 $('#update_pull_request').on('click', function (e) {
870 871 $(this).attr('disabled', 'disabled');
871 872 $(this).addClass('disabled');
872 873 $(this).html(_gettext('Saving...'));
873 874 reviewersController.updateReviewers(
874 875 "${c.repo_name}", "${c.pull_request.pull_request_id}");
875 876 });
876 877
877 878
878 879 // fixing issue with caches on firefox
879 880 $('#update_commits').removeAttr("disabled");
880 881
881 882 $('.show-inline-comments').on('click', function (e) {
882 883 var boxid = $(this).attr('data-comment-id');
883 884 var button = $(this);
884 885
885 886 if (button.hasClass("comments-visible")) {
886 887 $('#{0} .inline-comments'.format(boxid)).each(function (index) {
887 888 $(this).hide();
888 889 });
889 890 button.removeClass("comments-visible");
890 891 } else {
891 892 $('#{0} .inline-comments'.format(boxid)).each(function (index) {
892 893 $(this).show();
893 894 });
894 895 button.addClass("comments-visible");
895 896 }
896 897 });
897 898
898 899 // register submit callback on commentForm form to track TODOs
899 900 window.commentFormGlobalSubmitSuccessCallback = function () {
900 901 refreshMergeChecks();
901 902 };
902 903
903 904 ReviewerAutoComplete('#user');
904 905
905 906 })
906 907
907 908 </script>
908 909
909 910 </div>
910 911
911 912 </%def>
General Comments 0
You need to be logged in to leave comments. Login now