##// END OF EJS Templates
files: add pre-commit checks on file operations to prevent loosing content while editing when repositories change....
dan -
r4302:4c157b04 default
parent child Browse files
Show More
@@ -1,516 +1,520 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20 from rhodecode.apps._base import add_route_with_slash
21 21
22 22
23 23 def includeme(config):
24 24
25 25 # repo creating checks, special cases that aren't repo routes
26 26 config.add_route(
27 27 name='repo_creating',
28 28 pattern='/{repo_name:.*?[^/]}/repo_creating')
29 29
30 30 config.add_route(
31 31 name='repo_creating_check',
32 32 pattern='/{repo_name:.*?[^/]}/repo_creating_check')
33 33
34 34 # Summary
35 35 # NOTE(marcink): one additional route is defined in very bottom, catch
36 36 # all pattern
37 37 config.add_route(
38 38 name='repo_summary_explicit',
39 39 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
40 40 config.add_route(
41 41 name='repo_summary_commits',
42 42 pattern='/{repo_name:.*?[^/]}/summary-commits', repo_route=True)
43 43
44 44 # Commits
45 45 config.add_route(
46 46 name='repo_commit',
47 47 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}', repo_route=True)
48 48
49 49 config.add_route(
50 50 name='repo_commit_children',
51 51 pattern='/{repo_name:.*?[^/]}/changeset_children/{commit_id}', repo_route=True)
52 52
53 53 config.add_route(
54 54 name='repo_commit_parents',
55 55 pattern='/{repo_name:.*?[^/]}/changeset_parents/{commit_id}', repo_route=True)
56 56
57 57 config.add_route(
58 58 name='repo_commit_raw',
59 59 pattern='/{repo_name:.*?[^/]}/changeset-diff/{commit_id}', repo_route=True)
60 60
61 61 config.add_route(
62 62 name='repo_commit_patch',
63 63 pattern='/{repo_name:.*?[^/]}/changeset-patch/{commit_id}', repo_route=True)
64 64
65 65 config.add_route(
66 66 name='repo_commit_download',
67 67 pattern='/{repo_name:.*?[^/]}/changeset-download/{commit_id}', repo_route=True)
68 68
69 69 config.add_route(
70 70 name='repo_commit_data',
71 71 pattern='/{repo_name:.*?[^/]}/changeset-data/{commit_id}', repo_route=True)
72 72
73 73 config.add_route(
74 74 name='repo_commit_comment_create',
75 75 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/create', repo_route=True)
76 76
77 77 config.add_route(
78 78 name='repo_commit_comment_preview',
79 79 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/preview', repo_route=True)
80 80
81 81 config.add_route(
82 82 name='repo_commit_comment_attachment_upload',
83 83 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/attachment_upload', repo_route=True)
84 84
85 85 config.add_route(
86 86 name='repo_commit_comment_delete',
87 87 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/{comment_id}/delete', repo_route=True)
88 88
89 89 # still working url for backward compat.
90 90 config.add_route(
91 91 name='repo_commit_raw_deprecated',
92 92 pattern='/{repo_name:.*?[^/]}/raw-changeset/{commit_id}', repo_route=True)
93 93
94 94 # Files
95 95 config.add_route(
96 96 name='repo_archivefile',
97 97 pattern='/{repo_name:.*?[^/]}/archive/{fname:.*}', repo_route=True)
98 98
99 99 config.add_route(
100 100 name='repo_files_diff',
101 101 pattern='/{repo_name:.*?[^/]}/diff/{f_path:.*}', repo_route=True)
102 102 config.add_route( # legacy route to make old links work
103 103 name='repo_files_diff_2way_redirect',
104 104 pattern='/{repo_name:.*?[^/]}/diff-2way/{f_path:.*}', repo_route=True)
105 105
106 106 config.add_route(
107 107 name='repo_files',
108 108 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/{f_path:.*}', repo_route=True)
109 109 config.add_route(
110 110 name='repo_files:default_path',
111 111 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/', repo_route=True)
112 112 config.add_route(
113 113 name='repo_files:default_commit',
114 114 pattern='/{repo_name:.*?[^/]}/files', repo_route=True)
115 115
116 116 config.add_route(
117 117 name='repo_files:rendered',
118 118 pattern='/{repo_name:.*?[^/]}/render/{commit_id}/{f_path:.*}', repo_route=True)
119 119
120 120 config.add_route(
121 121 name='repo_files:annotated',
122 122 pattern='/{repo_name:.*?[^/]}/annotate/{commit_id}/{f_path:.*}', repo_route=True)
123 123 config.add_route(
124 124 name='repo_files:annotated_previous',
125 125 pattern='/{repo_name:.*?[^/]}/annotate-previous/{commit_id}/{f_path:.*}', repo_route=True)
126 126
127 127 config.add_route(
128 128 name='repo_nodetree_full',
129 129 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/{f_path:.*}', repo_route=True)
130 130 config.add_route(
131 131 name='repo_nodetree_full:default_path',
132 132 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/', repo_route=True)
133 133
134 134 config.add_route(
135 135 name='repo_files_nodelist',
136 136 pattern='/{repo_name:.*?[^/]}/nodelist/{commit_id}/{f_path:.*}', repo_route=True)
137 137
138 138 config.add_route(
139 139 name='repo_file_raw',
140 140 pattern='/{repo_name:.*?[^/]}/raw/{commit_id}/{f_path:.*}', repo_route=True)
141 141
142 142 config.add_route(
143 143 name='repo_file_download',
144 144 pattern='/{repo_name:.*?[^/]}/download/{commit_id}/{f_path:.*}', repo_route=True)
145 145 config.add_route( # backward compat to keep old links working
146 146 name='repo_file_download:legacy',
147 147 pattern='/{repo_name:.*?[^/]}/rawfile/{commit_id}/{f_path:.*}',
148 148 repo_route=True)
149 149
150 150 config.add_route(
151 151 name='repo_file_history',
152 152 pattern='/{repo_name:.*?[^/]}/history/{commit_id}/{f_path:.*}', repo_route=True)
153 153
154 154 config.add_route(
155 155 name='repo_file_authors',
156 156 pattern='/{repo_name:.*?[^/]}/authors/{commit_id}/{f_path:.*}', repo_route=True)
157 157
158 158 config.add_route(
159 name='repo_files_check_head',
160 pattern='/{repo_name:.*?[^/]}/check_head/{commit_id}/{f_path:.*}',
161 repo_route=True)
162 config.add_route(
159 163 name='repo_files_remove_file',
160 164 pattern='/{repo_name:.*?[^/]}/remove_file/{commit_id}/{f_path:.*}',
161 165 repo_route=True)
162 166 config.add_route(
163 167 name='repo_files_delete_file',
164 168 pattern='/{repo_name:.*?[^/]}/delete_file/{commit_id}/{f_path:.*}',
165 169 repo_route=True)
166 170 config.add_route(
167 171 name='repo_files_edit_file',
168 172 pattern='/{repo_name:.*?[^/]}/edit_file/{commit_id}/{f_path:.*}',
169 173 repo_route=True)
170 174 config.add_route(
171 175 name='repo_files_update_file',
172 176 pattern='/{repo_name:.*?[^/]}/update_file/{commit_id}/{f_path:.*}',
173 177 repo_route=True)
174 178 config.add_route(
175 179 name='repo_files_add_file',
176 180 pattern='/{repo_name:.*?[^/]}/add_file/{commit_id}/{f_path:.*}',
177 181 repo_route=True)
178 182 config.add_route(
179 183 name='repo_files_upload_file',
180 184 pattern='/{repo_name:.*?[^/]}/upload_file/{commit_id}/{f_path:.*}',
181 185 repo_route=True)
182 186 config.add_route(
183 187 name='repo_files_create_file',
184 188 pattern='/{repo_name:.*?[^/]}/create_file/{commit_id}/{f_path:.*}',
185 189 repo_route=True)
186 190
187 191 # Refs data
188 192 config.add_route(
189 193 name='repo_refs_data',
190 194 pattern='/{repo_name:.*?[^/]}/refs-data', repo_route=True)
191 195
192 196 config.add_route(
193 197 name='repo_refs_changelog_data',
194 198 pattern='/{repo_name:.*?[^/]}/refs-data-changelog', repo_route=True)
195 199
196 200 config.add_route(
197 201 name='repo_stats',
198 202 pattern='/{repo_name:.*?[^/]}/repo_stats/{commit_id}', repo_route=True)
199 203
200 204 # Commits
201 205 config.add_route(
202 206 name='repo_commits',
203 207 pattern='/{repo_name:.*?[^/]}/commits', repo_route=True)
204 208 config.add_route(
205 209 name='repo_commits_file',
206 210 pattern='/{repo_name:.*?[^/]}/commits/{commit_id}/{f_path:.*}', repo_route=True)
207 211 config.add_route(
208 212 name='repo_commits_elements',
209 213 pattern='/{repo_name:.*?[^/]}/commits_elements', repo_route=True)
210 214 config.add_route(
211 215 name='repo_commits_elements_file',
212 216 pattern='/{repo_name:.*?[^/]}/commits_elements/{commit_id}/{f_path:.*}', repo_route=True)
213 217
214 218 # Changelog (old deprecated name for commits page)
215 219 config.add_route(
216 220 name='repo_changelog',
217 221 pattern='/{repo_name:.*?[^/]}/changelog', repo_route=True)
218 222 config.add_route(
219 223 name='repo_changelog_file',
220 224 pattern='/{repo_name:.*?[^/]}/changelog/{commit_id}/{f_path:.*}', repo_route=True)
221 225
222 226 # Compare
223 227 config.add_route(
224 228 name='repo_compare_select',
225 229 pattern='/{repo_name:.*?[^/]}/compare', repo_route=True)
226 230
227 231 config.add_route(
228 232 name='repo_compare',
229 233 pattern='/{repo_name:.*?[^/]}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}', repo_route=True)
230 234
231 235 # Tags
232 236 config.add_route(
233 237 name='tags_home',
234 238 pattern='/{repo_name:.*?[^/]}/tags', repo_route=True)
235 239
236 240 # Branches
237 241 config.add_route(
238 242 name='branches_home',
239 243 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
240 244
241 245 # Bookmarks
242 246 config.add_route(
243 247 name='bookmarks_home',
244 248 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
245 249
246 250 # Forks
247 251 config.add_route(
248 252 name='repo_fork_new',
249 253 pattern='/{repo_name:.*?[^/]}/fork', repo_route=True,
250 254 repo_forbid_when_archived=True,
251 255 repo_accepted_types=['hg', 'git'])
252 256
253 257 config.add_route(
254 258 name='repo_fork_create',
255 259 pattern='/{repo_name:.*?[^/]}/fork/create', repo_route=True,
256 260 repo_forbid_when_archived=True,
257 261 repo_accepted_types=['hg', 'git'])
258 262
259 263 config.add_route(
260 264 name='repo_forks_show_all',
261 265 pattern='/{repo_name:.*?[^/]}/forks', repo_route=True,
262 266 repo_accepted_types=['hg', 'git'])
263 267 config.add_route(
264 268 name='repo_forks_data',
265 269 pattern='/{repo_name:.*?[^/]}/forks/data', repo_route=True,
266 270 repo_accepted_types=['hg', 'git'])
267 271
268 272 # Pull Requests
269 273 config.add_route(
270 274 name='pullrequest_show',
271 275 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}',
272 276 repo_route=True)
273 277
274 278 config.add_route(
275 279 name='pullrequest_show_all',
276 280 pattern='/{repo_name:.*?[^/]}/pull-request',
277 281 repo_route=True, repo_accepted_types=['hg', 'git'])
278 282
279 283 config.add_route(
280 284 name='pullrequest_show_all_data',
281 285 pattern='/{repo_name:.*?[^/]}/pull-request-data',
282 286 repo_route=True, repo_accepted_types=['hg', 'git'])
283 287
284 288 config.add_route(
285 289 name='pullrequest_repo_refs',
286 290 pattern='/{repo_name:.*?[^/]}/pull-request/refs/{target_repo_name:.*?[^/]}',
287 291 repo_route=True)
288 292
289 293 config.add_route(
290 294 name='pullrequest_repo_targets',
291 295 pattern='/{repo_name:.*?[^/]}/pull-request/repo-targets',
292 296 repo_route=True)
293 297
294 298 config.add_route(
295 299 name='pullrequest_new',
296 300 pattern='/{repo_name:.*?[^/]}/pull-request/new',
297 301 repo_route=True, repo_accepted_types=['hg', 'git'],
298 302 repo_forbid_when_archived=True)
299 303
300 304 config.add_route(
301 305 name='pullrequest_create',
302 306 pattern='/{repo_name:.*?[^/]}/pull-request/create',
303 307 repo_route=True, repo_accepted_types=['hg', 'git'],
304 308 repo_forbid_when_archived=True)
305 309
306 310 config.add_route(
307 311 name='pullrequest_update',
308 312 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/update',
309 313 repo_route=True, repo_forbid_when_archived=True)
310 314
311 315 config.add_route(
312 316 name='pullrequest_merge',
313 317 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/merge',
314 318 repo_route=True, repo_forbid_when_archived=True)
315 319
316 320 config.add_route(
317 321 name='pullrequest_delete',
318 322 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/delete',
319 323 repo_route=True, repo_forbid_when_archived=True)
320 324
321 325 config.add_route(
322 326 name='pullrequest_comment_create',
323 327 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment',
324 328 repo_route=True)
325 329
326 330 config.add_route(
327 331 name='pullrequest_comment_delete',
328 332 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment/{comment_id}/delete',
329 333 repo_route=True, repo_accepted_types=['hg', 'git'])
330 334
331 335 # Artifacts, (EE feature)
332 336 config.add_route(
333 337 name='repo_artifacts_list',
334 338 pattern='/{repo_name:.*?[^/]}/artifacts', repo_route=True)
335 339
336 340 # Settings
337 341 config.add_route(
338 342 name='edit_repo',
339 343 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
340 344 # update is POST on edit_repo
341 345
342 346 # Settings advanced
343 347 config.add_route(
344 348 name='edit_repo_advanced',
345 349 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
346 350 config.add_route(
347 351 name='edit_repo_advanced_archive',
348 352 pattern='/{repo_name:.*?[^/]}/settings/advanced/archive', repo_route=True)
349 353 config.add_route(
350 354 name='edit_repo_advanced_delete',
351 355 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
352 356 config.add_route(
353 357 name='edit_repo_advanced_locking',
354 358 pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True)
355 359 config.add_route(
356 360 name='edit_repo_advanced_journal',
357 361 pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True)
358 362 config.add_route(
359 363 name='edit_repo_advanced_fork',
360 364 pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True)
361 365
362 366 config.add_route(
363 367 name='edit_repo_advanced_hooks',
364 368 pattern='/{repo_name:.*?[^/]}/settings/advanced/hooks', repo_route=True)
365 369
366 370 # Caches
367 371 config.add_route(
368 372 name='edit_repo_caches',
369 373 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
370 374
371 375 # Permissions
372 376 config.add_route(
373 377 name='edit_repo_perms',
374 378 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
375 379
376 380 config.add_route(
377 381 name='edit_repo_perms_set_private',
378 382 pattern='/{repo_name:.*?[^/]}/settings/permissions/set_private', repo_route=True)
379 383
380 384 # Permissions Branch (EE feature)
381 385 config.add_route(
382 386 name='edit_repo_perms_branch',
383 387 pattern='/{repo_name:.*?[^/]}/settings/branch_permissions', repo_route=True)
384 388 config.add_route(
385 389 name='edit_repo_perms_branch_delete',
386 390 pattern='/{repo_name:.*?[^/]}/settings/branch_permissions/{rule_id}/delete',
387 391 repo_route=True)
388 392
389 393 # Maintenance
390 394 config.add_route(
391 395 name='edit_repo_maintenance',
392 396 pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
393 397
394 398 config.add_route(
395 399 name='edit_repo_maintenance_execute',
396 400 pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
397 401
398 402 # Fields
399 403 config.add_route(
400 404 name='edit_repo_fields',
401 405 pattern='/{repo_name:.*?[^/]}/settings/fields', repo_route=True)
402 406 config.add_route(
403 407 name='edit_repo_fields_create',
404 408 pattern='/{repo_name:.*?[^/]}/settings/fields/create', repo_route=True)
405 409 config.add_route(
406 410 name='edit_repo_fields_delete',
407 411 pattern='/{repo_name:.*?[^/]}/settings/fields/{field_id}/delete', repo_route=True)
408 412
409 413 # Locking
410 414 config.add_route(
411 415 name='repo_edit_toggle_locking',
412 416 pattern='/{repo_name:.*?[^/]}/settings/toggle_locking', repo_route=True)
413 417
414 418 # Remote
415 419 config.add_route(
416 420 name='edit_repo_remote',
417 421 pattern='/{repo_name:.*?[^/]}/settings/remote', repo_route=True)
418 422 config.add_route(
419 423 name='edit_repo_remote_pull',
420 424 pattern='/{repo_name:.*?[^/]}/settings/remote/pull', repo_route=True)
421 425 config.add_route(
422 426 name='edit_repo_remote_push',
423 427 pattern='/{repo_name:.*?[^/]}/settings/remote/push', repo_route=True)
424 428
425 429 # Statistics
426 430 config.add_route(
427 431 name='edit_repo_statistics',
428 432 pattern='/{repo_name:.*?[^/]}/settings/statistics', repo_route=True)
429 433 config.add_route(
430 434 name='edit_repo_statistics_reset',
431 435 pattern='/{repo_name:.*?[^/]}/settings/statistics/update', repo_route=True)
432 436
433 437 # Issue trackers
434 438 config.add_route(
435 439 name='edit_repo_issuetracker',
436 440 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers', repo_route=True)
437 441 config.add_route(
438 442 name='edit_repo_issuetracker_test',
439 443 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/test', repo_route=True)
440 444 config.add_route(
441 445 name='edit_repo_issuetracker_delete',
442 446 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/delete', repo_route=True)
443 447 config.add_route(
444 448 name='edit_repo_issuetracker_update',
445 449 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/update', repo_route=True)
446 450
447 451 # VCS Settings
448 452 config.add_route(
449 453 name='edit_repo_vcs',
450 454 pattern='/{repo_name:.*?[^/]}/settings/vcs', repo_route=True)
451 455 config.add_route(
452 456 name='edit_repo_vcs_update',
453 457 pattern='/{repo_name:.*?[^/]}/settings/vcs/update', repo_route=True)
454 458
455 459 # svn pattern
456 460 config.add_route(
457 461 name='edit_repo_vcs_svn_pattern_delete',
458 462 pattern='/{repo_name:.*?[^/]}/settings/vcs/svn_pattern/delete', repo_route=True)
459 463
460 464 # Repo Review Rules (EE feature)
461 465 config.add_route(
462 466 name='repo_reviewers',
463 467 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
464 468
465 469 config.add_route(
466 470 name='repo_default_reviewers_data',
467 471 pattern='/{repo_name:.*?[^/]}/settings/review/default-reviewers', repo_route=True)
468 472
469 473 # Repo Automation (EE feature)
470 474 config.add_route(
471 475 name='repo_automation',
472 476 pattern='/{repo_name:.*?[^/]}/settings/automation', repo_route=True)
473 477
474 478 # Strip
475 479 config.add_route(
476 480 name='edit_repo_strip',
477 481 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
478 482
479 483 config.add_route(
480 484 name='strip_check',
481 485 pattern='/{repo_name:.*?[^/]}/settings/strip_check', repo_route=True)
482 486
483 487 config.add_route(
484 488 name='strip_execute',
485 489 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
486 490
487 491 # Audit logs
488 492 config.add_route(
489 493 name='edit_repo_audit_logs',
490 494 pattern='/{repo_name:.*?[^/]}/settings/audit_logs', repo_route=True)
491 495
492 496 # ATOM/RSS Feed, shouldn't contain slashes for outlook compatibility
493 497 config.add_route(
494 498 name='rss_feed_home',
495 499 pattern='/{repo_name:.*?[^/]}/feed-rss', repo_route=True)
496 500
497 501 config.add_route(
498 502 name='atom_feed_home',
499 503 pattern='/{repo_name:.*?[^/]}/feed-atom', repo_route=True)
500 504
501 505 config.add_route(
502 506 name='rss_feed_home_old',
503 507 pattern='/{repo_name:.*?[^/]}/feed/rss', repo_route=True)
504 508
505 509 config.add_route(
506 510 name='atom_feed_home_old',
507 511 pattern='/{repo_name:.*?[^/]}/feed/atom', repo_route=True)
508 512
509 513 # NOTE(marcink): needs to be at the end for catch-all
510 514 add_route_with_slash(
511 515 config,
512 516 name='repo_summary',
513 517 pattern='/{repo_name:.*?[^/]}', repo_route=True)
514 518
515 519 # Scan module for configuration decorators.
516 520 config.scan('.views', ignore='.tests')
@@ -1,1568 +1,1604 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import itertools
22 22 import logging
23 23 import os
24 24 import shutil
25 25 import tempfile
26 26 import collections
27 27 import urllib
28 28 import pathlib2
29 29
30 30 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound
31 31 from pyramid.view import view_config
32 32 from pyramid.renderers import render
33 33 from pyramid.response import Response
34 34
35 35 import rhodecode
36 36 from rhodecode.apps._base import RepoAppView
37 37
38 38
39 39 from rhodecode.lib import diffs, helpers as h, rc_cache
40 40 from rhodecode.lib import audit_logger
41 41 from rhodecode.lib.view_utils import parse_path_ref
42 42 from rhodecode.lib.exceptions import NonRelativePathError
43 43 from rhodecode.lib.codeblocks import (
44 44 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
45 45 from rhodecode.lib.utils2 import (
46 46 convert_line_endings, detect_mode, safe_str, str2bool, safe_int, sha1, safe_unicode)
47 47 from rhodecode.lib.auth import (
48 48 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
49 49 from rhodecode.lib.vcs import path as vcspath
50 50 from rhodecode.lib.vcs.backends.base import EmptyCommit
51 51 from rhodecode.lib.vcs.conf import settings
52 52 from rhodecode.lib.vcs.nodes import FileNode
53 53 from rhodecode.lib.vcs.exceptions import (
54 54 RepositoryError, CommitDoesNotExistError, EmptyRepositoryError,
55 55 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,
56 56 NodeDoesNotExistError, CommitError, NodeError)
57 57
58 58 from rhodecode.model.scm import ScmModel
59 59 from rhodecode.model.db import Repository
60 60
61 61 log = logging.getLogger(__name__)
62 62
63 63
64 64 class RepoFilesView(RepoAppView):
65 65
66 66 @staticmethod
67 67 def adjust_file_path_for_svn(f_path, repo):
68 68 """
69 69 Computes the relative path of `f_path`.
70 70
71 71 This is mainly based on prefix matching of the recognized tags and
72 72 branches in the underlying repository.
73 73 """
74 74 tags_and_branches = itertools.chain(
75 75 repo.branches.iterkeys(),
76 76 repo.tags.iterkeys())
77 77 tags_and_branches = sorted(tags_and_branches, key=len, reverse=True)
78 78
79 79 for name in tags_and_branches:
80 80 if f_path.startswith('{}/'.format(name)):
81 81 f_path = vcspath.relpath(f_path, name)
82 82 break
83 83 return f_path
84 84
85 85 def load_default_context(self):
86 86 c = self._get_local_tmpl_context(include_app_defaults=True)
87 87 c.rhodecode_repo = self.rhodecode_vcs_repo
88 88 c.enable_downloads = self.db_repo.enable_downloads
89 89 return c
90 90
91 91 def _ensure_not_locked(self, commit_id='tip'):
92 92 _ = self.request.translate
93 93
94 94 repo = self.db_repo
95 95 if repo.enable_locking and repo.locked[0]:
96 96 h.flash(_('This repository has been locked by %s on %s')
97 97 % (h.person_by_id(repo.locked[0]),
98 98 h.format_date(h.time_to_datetime(repo.locked[1]))),
99 99 'warning')
100 100 files_url = h.route_path(
101 101 'repo_files:default_path',
102 102 repo_name=self.db_repo_name, commit_id=commit_id)
103 103 raise HTTPFound(files_url)
104 104
105 105 def forbid_non_head(self, is_head, f_path, commit_id='tip', json_mode=False):
106 106 _ = self.request.translate
107 107
108 108 if not is_head:
109 109 message = _('Cannot modify file. '
110 110 'Given commit `{}` is not head of a branch.').format(commit_id)
111 111 h.flash(message, category='warning')
112 112
113 113 if json_mode:
114 114 return message
115 115
116 116 files_url = h.route_path(
117 117 'repo_files', repo_name=self.db_repo_name, commit_id=commit_id,
118 118 f_path=f_path)
119 119 raise HTTPFound(files_url)
120 120
121 121 def check_branch_permission(self, branch_name, commit_id='tip', json_mode=False):
122 122 _ = self.request.translate
123 123
124 124 rule, branch_perm = self._rhodecode_user.get_rule_and_branch_permission(
125 125 self.db_repo_name, branch_name)
126 126 if branch_perm and branch_perm not in ['branch.push', 'branch.push_force']:
127 127 message = _('Branch `{}` changes forbidden by rule {}.').format(
128 128 branch_name, rule)
129 129 h.flash(message, 'warning')
130 130
131 131 if json_mode:
132 132 return message
133 133
134 134 files_url = h.route_path(
135 135 'repo_files:default_path', repo_name=self.db_repo_name, commit_id=commit_id)
136 136
137 137 raise HTTPFound(files_url)
138 138
139 139 def _get_commit_and_path(self):
140 140 default_commit_id = self.db_repo.landing_rev[1]
141 141 default_f_path = '/'
142 142
143 143 commit_id = self.request.matchdict.get(
144 144 'commit_id', default_commit_id)
145 145 f_path = self._get_f_path(self.request.matchdict, default_f_path)
146 146 return commit_id, f_path
147 147
148 148 def _get_default_encoding(self, c):
149 149 enc_list = getattr(c, 'default_encodings', [])
150 150 return enc_list[0] if enc_list else 'UTF-8'
151 151
152 152 def _get_commit_or_redirect(self, commit_id, redirect_after=True):
153 153 """
154 154 This is a safe way to get commit. If an error occurs it redirects to
155 155 tip with proper message
156 156
157 157 :param commit_id: id of commit to fetch
158 158 :param redirect_after: toggle redirection
159 159 """
160 160 _ = self.request.translate
161 161
162 162 try:
163 163 return self.rhodecode_vcs_repo.get_commit(commit_id)
164 164 except EmptyRepositoryError:
165 165 if not redirect_after:
166 166 return None
167 167
168 168 _url = h.route_path(
169 169 'repo_files_add_file',
170 170 repo_name=self.db_repo_name, commit_id=0, f_path='')
171 171
172 172 if h.HasRepoPermissionAny(
173 173 'repository.write', 'repository.admin')(self.db_repo_name):
174 174 add_new = h.link_to(
175 175 _('Click here to add a new file.'), _url, class_="alert-link")
176 176 else:
177 177 add_new = ""
178 178
179 179 h.flash(h.literal(
180 180 _('There are no files yet. %s') % add_new), category='warning')
181 181 raise HTTPFound(
182 182 h.route_path('repo_summary', repo_name=self.db_repo_name))
183 183
184 184 except (CommitDoesNotExistError, LookupError):
185 185 msg = _('No such commit exists for this repository')
186 186 h.flash(msg, category='error')
187 187 raise HTTPNotFound()
188 188 except RepositoryError as e:
189 189 h.flash(safe_str(h.escape(e)), category='error')
190 190 raise HTTPNotFound()
191 191
192 192 def _get_filenode_or_redirect(self, commit_obj, path):
193 193 """
194 194 Returns file_node, if error occurs or given path is directory,
195 195 it'll redirect to top level path
196 196 """
197 197 _ = self.request.translate
198 198
199 199 try:
200 200 file_node = commit_obj.get_node(path)
201 201 if file_node.is_dir():
202 202 raise RepositoryError('The given path is a directory')
203 203 except CommitDoesNotExistError:
204 204 log.exception('No such commit exists for this repository')
205 205 h.flash(_('No such commit exists for this repository'), category='error')
206 206 raise HTTPNotFound()
207 207 except RepositoryError as e:
208 208 log.warning('Repository error while fetching filenode `%s`. Err:%s', path, e)
209 209 h.flash(safe_str(h.escape(e)), category='error')
210 210 raise HTTPNotFound()
211 211
212 212 return file_node
213 213
214 214 def _is_valid_head(self, commit_id, repo):
215 215 branch_name = sha_commit_id = ''
216 216 is_head = False
217 217 log.debug('Checking if commit_id `%s` is a head for %s.', commit_id, repo)
218 218
219 219 for _branch_name, branch_commit_id in repo.branches.items():
220 220 # simple case we pass in branch name, it's a HEAD
221 221 if commit_id == _branch_name:
222 222 is_head = True
223 223 branch_name = _branch_name
224 224 sha_commit_id = branch_commit_id
225 225 break
226 226 # case when we pass in full sha commit_id, which is a head
227 227 elif commit_id == branch_commit_id:
228 228 is_head = True
229 229 branch_name = _branch_name
230 230 sha_commit_id = branch_commit_id
231 231 break
232 232
233 233 if h.is_svn(repo) and not repo.is_empty():
234 234 # Note: Subversion only has one head.
235 235 if commit_id == repo.get_commit(commit_idx=-1).raw_id:
236 236 is_head = True
237 237 return branch_name, sha_commit_id, is_head
238 238
239 239 # checked branches, means we only need to try to get the branch/commit_sha
240 240 if not repo.is_empty():
241 241 commit = repo.get_commit(commit_id=commit_id)
242 242 if commit:
243 243 branch_name = commit.branch
244 244 sha_commit_id = commit.raw_id
245 245
246 246 return branch_name, sha_commit_id, is_head
247 247
248 248 def _get_tree_at_commit(self, c, commit_id, f_path, full_load=False, at_rev=None):
249 249
250 250 repo_id = self.db_repo.repo_id
251 251 force_recache = self.get_recache_flag()
252 252
253 253 cache_seconds = safe_int(
254 254 rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
255 255 cache_on = not force_recache and cache_seconds > 0
256 256 log.debug(
257 257 'Computing FILE TREE for repo_id %s commit_id `%s` and path `%s`'
258 258 'with caching: %s[TTL: %ss]' % (
259 259 repo_id, commit_id, f_path, cache_on, cache_seconds or 0))
260 260
261 261 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
262 262 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
263 263
264 264 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
265 265 condition=cache_on)
266 266 def compute_file_tree(ver, repo_id, commit_id, f_path, full_load, at_rev):
267 267 log.debug('Generating cached file tree at ver:%s for repo_id: %s, %s, %s',
268 268 ver, repo_id, commit_id, f_path)
269 269
270 270 c.full_load = full_load
271 271 return render(
272 272 'rhodecode:templates/files/files_browser_tree.mako',
273 273 self._get_template_context(c), self.request, at_rev)
274 274
275 275 return compute_file_tree(
276 276 rc_cache.FILE_TREE_CACHE_VER, self.db_repo.repo_id, commit_id,
277 277 f_path, full_load, at_rev)
278 278
279 279 def _get_archive_spec(self, fname):
280 280 log.debug('Detecting archive spec for: `%s`', fname)
281 281
282 282 fileformat = None
283 283 ext = None
284 284 content_type = None
285 285 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
286 286
287 287 if fname.endswith(extension):
288 288 fileformat = a_type
289 289 log.debug('archive is of type: %s', fileformat)
290 290 ext = extension
291 291 break
292 292
293 293 if not fileformat:
294 294 raise ValueError()
295 295
296 296 # left over part of whole fname is the commit
297 297 commit_id = fname[:-len(ext)]
298 298
299 299 return commit_id, ext, fileformat, content_type
300 300
301 301 def create_pure_path(self, *parts):
302 302 # Split paths and sanitize them, removing any ../ etc
303 303 sanitized_path = [
304 304 x for x in pathlib2.PurePath(*parts).parts
305 305 if x not in ['.', '..']]
306 306
307 307 pure_path = pathlib2.PurePath(*sanitized_path)
308 308 return pure_path
309 309
310 310 def _is_lf_enabled(self, target_repo):
311 311 lf_enabled = False
312 312
313 313 lf_key_for_vcs_map = {
314 314 'hg': 'extensions_largefiles',
315 315 'git': 'vcs_git_lfs_enabled'
316 316 }
317 317
318 318 lf_key_for_vcs = lf_key_for_vcs_map.get(target_repo.repo_type)
319 319
320 320 if lf_key_for_vcs:
321 321 lf_enabled = self._get_repo_setting(target_repo, lf_key_for_vcs)
322 322
323 323 return lf_enabled
324 324
325 325 @LoginRequired()
326 326 @HasRepoPermissionAnyDecorator(
327 327 'repository.read', 'repository.write', 'repository.admin')
328 328 @view_config(
329 329 route_name='repo_archivefile', request_method='GET',
330 330 renderer=None)
331 331 def repo_archivefile(self):
332 332 # archive cache config
333 333 from rhodecode import CONFIG
334 334 _ = self.request.translate
335 335 self.load_default_context()
336 336 default_at_path = '/'
337 337 fname = self.request.matchdict['fname']
338 338 subrepos = self.request.GET.get('subrepos') == 'true'
339 339 at_path = self.request.GET.get('at_path') or default_at_path
340 340
341 341 if not self.db_repo.enable_downloads:
342 342 return Response(_('Downloads disabled'))
343 343
344 344 try:
345 345 commit_id, ext, fileformat, content_type = \
346 346 self._get_archive_spec(fname)
347 347 except ValueError:
348 348 return Response(_('Unknown archive type for: `{}`').format(
349 349 h.escape(fname)))
350 350
351 351 try:
352 352 commit = self.rhodecode_vcs_repo.get_commit(commit_id)
353 353 except CommitDoesNotExistError:
354 354 return Response(_('Unknown commit_id {}').format(
355 355 h.escape(commit_id)))
356 356 except EmptyRepositoryError:
357 357 return Response(_('Empty repository'))
358 358
359 359 try:
360 360 at_path = commit.get_node(at_path).path or default_at_path
361 361 except Exception:
362 362 return Response(_('No node at path {} for this repository').format(at_path))
363 363
364 364 path_sha = sha1(at_path)[:8]
365 365
366 366 # original backward compat name of archive
367 367 clean_name = safe_str(self.db_repo_name.replace('/', '_'))
368 368 short_sha = safe_str(commit.short_id)
369 369
370 370 if at_path == default_at_path:
371 371 archive_name = '{}-{}{}{}'.format(
372 372 clean_name,
373 373 '-sub' if subrepos else '',
374 374 short_sha,
375 375 ext)
376 376 # custom path and new name
377 377 else:
378 378 archive_name = '{}-{}{}-{}{}'.format(
379 379 clean_name,
380 380 '-sub' if subrepos else '',
381 381 short_sha,
382 382 path_sha,
383 383 ext)
384 384
385 385 use_cached_archive = False
386 386 archive_cache_enabled = CONFIG.get(
387 387 'archive_cache_dir') and not self.request.GET.get('no_cache')
388 388 cached_archive_path = None
389 389
390 390 if archive_cache_enabled:
391 391 # check if we it's ok to write
392 392 if not os.path.isdir(CONFIG['archive_cache_dir']):
393 393 os.makedirs(CONFIG['archive_cache_dir'])
394 394 cached_archive_path = os.path.join(
395 395 CONFIG['archive_cache_dir'], archive_name)
396 396 if os.path.isfile(cached_archive_path):
397 397 log.debug('Found cached archive in %s', cached_archive_path)
398 398 fd, archive = None, cached_archive_path
399 399 use_cached_archive = True
400 400 else:
401 401 log.debug('Archive %s is not yet cached', archive_name)
402 402
403 403 if not use_cached_archive:
404 404 # generate new archive
405 405 fd, archive = tempfile.mkstemp()
406 406 log.debug('Creating new temp archive in %s', archive)
407 407 try:
408 408 commit.archive_repo(archive, kind=fileformat, subrepos=subrepos,
409 409 archive_at_path=at_path)
410 410 except ImproperArchiveTypeError:
411 411 return _('Unknown archive type')
412 412 if archive_cache_enabled:
413 413 # if we generated the archive and we have cache enabled
414 414 # let's use this for future
415 415 log.debug('Storing new archive in %s', cached_archive_path)
416 416 shutil.move(archive, cached_archive_path)
417 417 archive = cached_archive_path
418 418
419 419 # store download action
420 420 audit_logger.store_web(
421 421 'repo.archive.download', action_data={
422 422 'user_agent': self.request.user_agent,
423 423 'archive_name': archive_name,
424 424 'archive_spec': fname,
425 425 'archive_cached': use_cached_archive},
426 426 user=self._rhodecode_user,
427 427 repo=self.db_repo,
428 428 commit=True
429 429 )
430 430
431 431 def get_chunked_archive(archive_path):
432 432 with open(archive_path, 'rb') as stream:
433 433 while True:
434 434 data = stream.read(16 * 1024)
435 435 if not data:
436 436 if fd: # fd means we used temporary file
437 437 os.close(fd)
438 438 if not archive_cache_enabled:
439 439 log.debug('Destroying temp archive %s', archive_path)
440 440 os.remove(archive_path)
441 441 break
442 442 yield data
443 443
444 444 response = Response(app_iter=get_chunked_archive(archive))
445 445 response.content_disposition = str(
446 446 'attachment; filename=%s' % archive_name)
447 447 response.content_type = str(content_type)
448 448
449 449 return response
450 450
451 451 def _get_file_node(self, commit_id, f_path):
452 452 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
453 453 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
454 454 try:
455 455 node = commit.get_node(f_path)
456 456 if node.is_dir():
457 457 raise NodeError('%s path is a %s not a file'
458 458 % (node, type(node)))
459 459 except NodeDoesNotExistError:
460 460 commit = EmptyCommit(
461 461 commit_id=commit_id,
462 462 idx=commit.idx,
463 463 repo=commit.repository,
464 464 alias=commit.repository.alias,
465 465 message=commit.message,
466 466 author=commit.author,
467 467 date=commit.date)
468 468 node = FileNode(f_path, '', commit=commit)
469 469 else:
470 470 commit = EmptyCommit(
471 471 repo=self.rhodecode_vcs_repo,
472 472 alias=self.rhodecode_vcs_repo.alias)
473 473 node = FileNode(f_path, '', commit=commit)
474 474 return node
475 475
476 476 @LoginRequired()
477 477 @HasRepoPermissionAnyDecorator(
478 478 'repository.read', 'repository.write', 'repository.admin')
479 479 @view_config(
480 480 route_name='repo_files_diff', request_method='GET',
481 481 renderer=None)
482 482 def repo_files_diff(self):
483 483 c = self.load_default_context()
484 484 f_path = self._get_f_path(self.request.matchdict)
485 485 diff1 = self.request.GET.get('diff1', '')
486 486 diff2 = self.request.GET.get('diff2', '')
487 487
488 488 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
489 489
490 490 ignore_whitespace = str2bool(self.request.GET.get('ignorews'))
491 491 line_context = self.request.GET.get('context', 3)
492 492
493 493 if not any((diff1, diff2)):
494 494 h.flash(
495 495 'Need query parameter "diff1" or "diff2" to generate a diff.',
496 496 category='error')
497 497 raise HTTPBadRequest()
498 498
499 499 c.action = self.request.GET.get('diff')
500 500 if c.action not in ['download', 'raw']:
501 501 compare_url = h.route_path(
502 502 'repo_compare',
503 503 repo_name=self.db_repo_name,
504 504 source_ref_type='rev',
505 505 source_ref=diff1,
506 506 target_repo=self.db_repo_name,
507 507 target_ref_type='rev',
508 508 target_ref=diff2,
509 509 _query=dict(f_path=f_path))
510 510 # redirect to new view if we render diff
511 511 raise HTTPFound(compare_url)
512 512
513 513 try:
514 514 node1 = self._get_file_node(diff1, path1)
515 515 node2 = self._get_file_node(diff2, f_path)
516 516 except (RepositoryError, NodeError):
517 517 log.exception("Exception while trying to get node from repository")
518 518 raise HTTPFound(
519 519 h.route_path('repo_files', repo_name=self.db_repo_name,
520 520 commit_id='tip', f_path=f_path))
521 521
522 522 if all(isinstance(node.commit, EmptyCommit)
523 523 for node in (node1, node2)):
524 524 raise HTTPNotFound()
525 525
526 526 c.commit_1 = node1.commit
527 527 c.commit_2 = node2.commit
528 528
529 529 if c.action == 'download':
530 530 _diff = diffs.get_gitdiff(node1, node2,
531 531 ignore_whitespace=ignore_whitespace,
532 532 context=line_context)
533 533 diff = diffs.DiffProcessor(_diff, format='gitdiff')
534 534
535 535 response = Response(self.path_filter.get_raw_patch(diff))
536 536 response.content_type = 'text/plain'
537 537 response.content_disposition = (
538 538 'attachment; filename=%s_%s_vs_%s.diff' % (f_path, diff1, diff2)
539 539 )
540 540 charset = self._get_default_encoding(c)
541 541 if charset:
542 542 response.charset = charset
543 543 return response
544 544
545 545 elif c.action == 'raw':
546 546 _diff = diffs.get_gitdiff(node1, node2,
547 547 ignore_whitespace=ignore_whitespace,
548 548 context=line_context)
549 549 diff = diffs.DiffProcessor(_diff, format='gitdiff')
550 550
551 551 response = Response(self.path_filter.get_raw_patch(diff))
552 552 response.content_type = 'text/plain'
553 553 charset = self._get_default_encoding(c)
554 554 if charset:
555 555 response.charset = charset
556 556 return response
557 557
558 558 # in case we ever end up here
559 559 raise HTTPNotFound()
560 560
561 561 @LoginRequired()
562 562 @HasRepoPermissionAnyDecorator(
563 563 'repository.read', 'repository.write', 'repository.admin')
564 564 @view_config(
565 565 route_name='repo_files_diff_2way_redirect', request_method='GET',
566 566 renderer=None)
567 567 def repo_files_diff_2way_redirect(self):
568 568 """
569 569 Kept only to make OLD links work
570 570 """
571 571 f_path = self._get_f_path_unchecked(self.request.matchdict)
572 572 diff1 = self.request.GET.get('diff1', '')
573 573 diff2 = self.request.GET.get('diff2', '')
574 574
575 575 if not any((diff1, diff2)):
576 576 h.flash(
577 577 'Need query parameter "diff1" or "diff2" to generate a diff.',
578 578 category='error')
579 579 raise HTTPBadRequest()
580 580
581 581 compare_url = h.route_path(
582 582 'repo_compare',
583 583 repo_name=self.db_repo_name,
584 584 source_ref_type='rev',
585 585 source_ref=diff1,
586 586 target_ref_type='rev',
587 587 target_ref=diff2,
588 588 _query=dict(f_path=f_path, diffmode='sideside',
589 589 target_repo=self.db_repo_name,))
590 590 raise HTTPFound(compare_url)
591 591
592 592 @LoginRequired()
593 593 @HasRepoPermissionAnyDecorator(
594 594 'repository.read', 'repository.write', 'repository.admin')
595 595 @view_config(
596 596 route_name='repo_files', request_method='GET',
597 597 renderer=None)
598 598 @view_config(
599 599 route_name='repo_files:default_path', request_method='GET',
600 600 renderer=None)
601 601 @view_config(
602 602 route_name='repo_files:default_commit', request_method='GET',
603 603 renderer=None)
604 604 @view_config(
605 605 route_name='repo_files:rendered', request_method='GET',
606 606 renderer=None)
607 607 @view_config(
608 608 route_name='repo_files:annotated', request_method='GET',
609 609 renderer=None)
610 610 def repo_files(self):
611 611 c = self.load_default_context()
612 612
613 613 view_name = getattr(self.request.matched_route, 'name', None)
614 614
615 615 c.annotate = view_name == 'repo_files:annotated'
616 616 # default is false, but .rst/.md files later are auto rendered, we can
617 617 # overwrite auto rendering by setting this GET flag
618 618 c.renderer = view_name == 'repo_files:rendered' or \
619 619 not self.request.GET.get('no-render', False)
620 620
621 621 commit_id, f_path = self._get_commit_and_path()
622 622
623 623 c.commit = self._get_commit_or_redirect(commit_id)
624 624 c.branch = self.request.GET.get('branch', None)
625 625 c.f_path = f_path
626 626 at_rev = self.request.GET.get('at')
627 627
628 628 # prev link
629 629 try:
630 630 prev_commit = c.commit.prev(c.branch)
631 631 c.prev_commit = prev_commit
632 632 c.url_prev = h.route_path(
633 633 'repo_files', repo_name=self.db_repo_name,
634 634 commit_id=prev_commit.raw_id, f_path=f_path)
635 635 if c.branch:
636 636 c.url_prev += '?branch=%s' % c.branch
637 637 except (CommitDoesNotExistError, VCSError):
638 638 c.url_prev = '#'
639 639 c.prev_commit = EmptyCommit()
640 640
641 641 # next link
642 642 try:
643 643 next_commit = c.commit.next(c.branch)
644 644 c.next_commit = next_commit
645 645 c.url_next = h.route_path(
646 646 'repo_files', repo_name=self.db_repo_name,
647 647 commit_id=next_commit.raw_id, f_path=f_path)
648 648 if c.branch:
649 649 c.url_next += '?branch=%s' % c.branch
650 650 except (CommitDoesNotExistError, VCSError):
651 651 c.url_next = '#'
652 652 c.next_commit = EmptyCommit()
653 653
654 654 # files or dirs
655 655 try:
656 656 c.file = c.commit.get_node(f_path)
657 657 c.file_author = True
658 658 c.file_tree = ''
659 659
660 660 # load file content
661 661 if c.file.is_file():
662 662 c.lf_node = {}
663 663
664 664 has_lf_enabled = self._is_lf_enabled(self.db_repo)
665 665 if has_lf_enabled:
666 666 c.lf_node = c.file.get_largefile_node()
667 667
668 668 c.file_source_page = 'true'
669 669 c.file_last_commit = c.file.last_commit
670 670
671 671 c.file_size_too_big = c.file.size > c.visual.cut_off_limit_file
672 672
673 673 if not (c.file_size_too_big or c.file.is_binary):
674 674 if c.annotate: # annotation has precedence over renderer
675 675 c.annotated_lines = filenode_as_annotated_lines_tokens(
676 676 c.file
677 677 )
678 678 else:
679 679 c.renderer = (
680 680 c.renderer and h.renderer_from_filename(c.file.path)
681 681 )
682 682 if not c.renderer:
683 683 c.lines = filenode_as_lines_tokens(c.file)
684 684
685 685 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
686 686 commit_id, self.rhodecode_vcs_repo)
687 687 c.on_branch_head = is_head
688 688
689 689 branch = c.commit.branch if (
690 690 c.commit.branch and '/' not in c.commit.branch) else None
691 691 c.branch_or_raw_id = branch or c.commit.raw_id
692 692 c.branch_name = c.commit.branch or h.short_id(c.commit.raw_id)
693 693
694 694 author = c.file_last_commit.author
695 695 c.authors = [[
696 696 h.email(author),
697 697 h.person(author, 'username_or_name_or_email'),
698 698 1
699 699 ]]
700 700
701 701 else: # load tree content at path
702 702 c.file_source_page = 'false'
703 703 c.authors = []
704 704 # this loads a simple tree without metadata to speed things up
705 705 # later via ajax we call repo_nodetree_full and fetch whole
706 706 c.file_tree = self._get_tree_at_commit(c, c.commit.raw_id, f_path, at_rev=at_rev)
707 707
708 708 c.readme_data, c.readme_file = \
709 709 self._get_readme_data(self.db_repo, c.visual.default_renderer,
710 710 c.commit.raw_id, f_path)
711 711
712 712 except RepositoryError as e:
713 713 h.flash(safe_str(h.escape(e)), category='error')
714 714 raise HTTPNotFound()
715 715
716 716 if self.request.environ.get('HTTP_X_PJAX'):
717 717 html = render('rhodecode:templates/files/files_pjax.mako',
718 718 self._get_template_context(c), self.request)
719 719 else:
720 720 html = render('rhodecode:templates/files/files.mako',
721 721 self._get_template_context(c), self.request)
722 722 return Response(html)
723 723
724 724 @HasRepoPermissionAnyDecorator(
725 725 'repository.read', 'repository.write', 'repository.admin')
726 726 @view_config(
727 727 route_name='repo_files:annotated_previous', request_method='GET',
728 728 renderer=None)
729 729 def repo_files_annotated_previous(self):
730 730 self.load_default_context()
731 731
732 732 commit_id, f_path = self._get_commit_and_path()
733 733 commit = self._get_commit_or_redirect(commit_id)
734 734 prev_commit_id = commit.raw_id
735 735 line_anchor = self.request.GET.get('line_anchor')
736 736 is_file = False
737 737 try:
738 738 _file = commit.get_node(f_path)
739 739 is_file = _file.is_file()
740 740 except (NodeDoesNotExistError, CommitDoesNotExistError, VCSError):
741 741 pass
742 742
743 743 if is_file:
744 744 history = commit.get_path_history(f_path)
745 745 prev_commit_id = history[1].raw_id \
746 746 if len(history) > 1 else prev_commit_id
747 747 prev_url = h.route_path(
748 748 'repo_files:annotated', repo_name=self.db_repo_name,
749 749 commit_id=prev_commit_id, f_path=f_path,
750 750 _anchor='L{}'.format(line_anchor))
751 751
752 752 raise HTTPFound(prev_url)
753 753
754 754 @LoginRequired()
755 755 @HasRepoPermissionAnyDecorator(
756 756 'repository.read', 'repository.write', 'repository.admin')
757 757 @view_config(
758 758 route_name='repo_nodetree_full', request_method='GET',
759 759 renderer=None, xhr=True)
760 760 @view_config(
761 761 route_name='repo_nodetree_full:default_path', request_method='GET',
762 762 renderer=None, xhr=True)
763 763 def repo_nodetree_full(self):
764 764 """
765 765 Returns rendered html of file tree that contains commit date,
766 766 author, commit_id for the specified combination of
767 767 repo, commit_id and file path
768 768 """
769 769 c = self.load_default_context()
770 770
771 771 commit_id, f_path = self._get_commit_and_path()
772 772 commit = self._get_commit_or_redirect(commit_id)
773 773 try:
774 774 dir_node = commit.get_node(f_path)
775 775 except RepositoryError as e:
776 776 return Response('error: {}'.format(h.escape(safe_str(e))))
777 777
778 778 if dir_node.is_file():
779 779 return Response('')
780 780
781 781 c.file = dir_node
782 782 c.commit = commit
783 783 at_rev = self.request.GET.get('at')
784 784
785 785 html = self._get_tree_at_commit(
786 786 c, commit.raw_id, dir_node.path, full_load=True, at_rev=at_rev)
787 787
788 788 return Response(html)
789 789
790 790 def _get_attachement_headers(self, f_path):
791 791 f_name = safe_str(f_path.split(Repository.NAME_SEP)[-1])
792 792 safe_path = f_name.replace('"', '\\"')
793 793 encoded_path = urllib.quote(f_name)
794 794
795 795 return "attachment; " \
796 796 "filename=\"{}\"; " \
797 797 "filename*=UTF-8\'\'{}".format(safe_path, encoded_path)
798 798
799 799 @LoginRequired()
800 800 @HasRepoPermissionAnyDecorator(
801 801 'repository.read', 'repository.write', 'repository.admin')
802 802 @view_config(
803 803 route_name='repo_file_raw', request_method='GET',
804 804 renderer=None)
805 805 def repo_file_raw(self):
806 806 """
807 807 Action for show as raw, some mimetypes are "rendered",
808 808 those include images, icons.
809 809 """
810 810 c = self.load_default_context()
811 811
812 812 commit_id, f_path = self._get_commit_and_path()
813 813 commit = self._get_commit_or_redirect(commit_id)
814 814 file_node = self._get_filenode_or_redirect(commit, f_path)
815 815
816 816 raw_mimetype_mapping = {
817 817 # map original mimetype to a mimetype used for "show as raw"
818 818 # you can also provide a content-disposition to override the
819 819 # default "attachment" disposition.
820 820 # orig_type: (new_type, new_dispo)
821 821
822 822 # show images inline:
823 823 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
824 824 # for example render an SVG with javascript inside or even render
825 825 # HTML.
826 826 'image/x-icon': ('image/x-icon', 'inline'),
827 827 'image/png': ('image/png', 'inline'),
828 828 'image/gif': ('image/gif', 'inline'),
829 829 'image/jpeg': ('image/jpeg', 'inline'),
830 830 'application/pdf': ('application/pdf', 'inline'),
831 831 }
832 832
833 833 mimetype = file_node.mimetype
834 834 try:
835 835 mimetype, disposition = raw_mimetype_mapping[mimetype]
836 836 except KeyError:
837 837 # we don't know anything special about this, handle it safely
838 838 if file_node.is_binary:
839 839 # do same as download raw for binary files
840 840 mimetype, disposition = 'application/octet-stream', 'attachment'
841 841 else:
842 842 # do not just use the original mimetype, but force text/plain,
843 843 # otherwise it would serve text/html and that might be unsafe.
844 844 # Note: underlying vcs library fakes text/plain mimetype if the
845 845 # mimetype can not be determined and it thinks it is not
846 846 # binary.This might lead to erroneous text display in some
847 847 # cases, but helps in other cases, like with text files
848 848 # without extension.
849 849 mimetype, disposition = 'text/plain', 'inline'
850 850
851 851 if disposition == 'attachment':
852 852 disposition = self._get_attachement_headers(f_path)
853 853
854 854 stream_content = file_node.stream_bytes()
855 855
856 856 response = Response(app_iter=stream_content)
857 857 response.content_disposition = disposition
858 858 response.content_type = mimetype
859 859
860 860 charset = self._get_default_encoding(c)
861 861 if charset:
862 862 response.charset = charset
863 863
864 864 return response
865 865
866 866 @LoginRequired()
867 867 @HasRepoPermissionAnyDecorator(
868 868 'repository.read', 'repository.write', 'repository.admin')
869 869 @view_config(
870 870 route_name='repo_file_download', request_method='GET',
871 871 renderer=None)
872 872 @view_config(
873 873 route_name='repo_file_download:legacy', request_method='GET',
874 874 renderer=None)
875 875 def repo_file_download(self):
876 876 c = self.load_default_context()
877 877
878 878 commit_id, f_path = self._get_commit_and_path()
879 879 commit = self._get_commit_or_redirect(commit_id)
880 880 file_node = self._get_filenode_or_redirect(commit, f_path)
881 881
882 882 if self.request.GET.get('lf'):
883 883 # only if lf get flag is passed, we download this file
884 884 # as LFS/Largefile
885 885 lf_node = file_node.get_largefile_node()
886 886 if lf_node:
887 887 # overwrite our pointer with the REAL large-file
888 888 file_node = lf_node
889 889
890 890 disposition = self._get_attachement_headers(f_path)
891 891
892 892 stream_content = file_node.stream_bytes()
893 893
894 894 response = Response(app_iter=stream_content)
895 895 response.content_disposition = disposition
896 896 response.content_type = file_node.mimetype
897 897
898 898 charset = self._get_default_encoding(c)
899 899 if charset:
900 900 response.charset = charset
901 901
902 902 return response
903 903
904 904 def _get_nodelist_at_commit(self, repo_name, repo_id, commit_id, f_path):
905 905
906 906 cache_seconds = safe_int(
907 907 rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
908 908 cache_on = cache_seconds > 0
909 909 log.debug(
910 910 'Computing FILE SEARCH for repo_id %s commit_id `%s` and path `%s`'
911 911 'with caching: %s[TTL: %ss]' % (
912 912 repo_id, commit_id, f_path, cache_on, cache_seconds or 0))
913 913
914 914 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
915 915 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
916 916
917 917 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
918 918 condition=cache_on)
919 919 def compute_file_search(repo_id, commit_id, f_path):
920 920 log.debug('Generating cached nodelist for repo_id:%s, %s, %s',
921 921 repo_id, commit_id, f_path)
922 922 try:
923 923 _d, _f = ScmModel().get_quick_filter_nodes(repo_name, commit_id, f_path)
924 924 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
925 925 log.exception(safe_str(e))
926 926 h.flash(safe_str(h.escape(e)), category='error')
927 927 raise HTTPFound(h.route_path(
928 928 'repo_files', repo_name=self.db_repo_name,
929 929 commit_id='tip', f_path='/'))
930 930
931 931 return _d + _f
932 932
933 933 result = compute_file_search(self.db_repo.repo_id, commit_id, f_path)
934 934 return filter(lambda n: self.path_filter.path_access_allowed(n['name']), result)
935 935
936 936 @LoginRequired()
937 937 @HasRepoPermissionAnyDecorator(
938 938 'repository.read', 'repository.write', 'repository.admin')
939 939 @view_config(
940 940 route_name='repo_files_nodelist', request_method='GET',
941 941 renderer='json_ext', xhr=True)
942 942 def repo_nodelist(self):
943 943 self.load_default_context()
944 944
945 945 commit_id, f_path = self._get_commit_and_path()
946 946 commit = self._get_commit_or_redirect(commit_id)
947 947
948 948 metadata = self._get_nodelist_at_commit(
949 949 self.db_repo_name, self.db_repo.repo_id, commit.raw_id, f_path)
950 950 return {'nodes': metadata}
951 951
952 952 def _create_references(self, branches_or_tags, symbolic_reference, f_path, ref_type):
953 953 items = []
954 954 for name, commit_id in branches_or_tags.items():
955 955 sym_ref = symbolic_reference(commit_id, name, f_path, ref_type)
956 956 items.append((sym_ref, name, ref_type))
957 957 return items
958 958
959 959 def _symbolic_reference(self, commit_id, name, f_path, ref_type):
960 960 return commit_id
961 961
962 962 def _symbolic_reference_svn(self, commit_id, name, f_path, ref_type):
963 963 return commit_id
964 964
965 965 # NOTE(dan): old code we used in "diff" mode compare
966 966 new_f_path = vcspath.join(name, f_path)
967 967 return u'%s@%s' % (new_f_path, commit_id)
968 968
969 969 def _get_node_history(self, commit_obj, f_path, commits=None):
970 970 """
971 971 get commit history for given node
972 972
973 973 :param commit_obj: commit to calculate history
974 974 :param f_path: path for node to calculate history for
975 975 :param commits: if passed don't calculate history and take
976 976 commits defined in this list
977 977 """
978 978 _ = self.request.translate
979 979
980 980 # calculate history based on tip
981 981 tip = self.rhodecode_vcs_repo.get_commit()
982 982 if commits is None:
983 983 pre_load = ["author", "branch"]
984 984 try:
985 985 commits = tip.get_path_history(f_path, pre_load=pre_load)
986 986 except (NodeDoesNotExistError, CommitError):
987 987 # this node is not present at tip!
988 988 commits = commit_obj.get_path_history(f_path, pre_load=pre_load)
989 989
990 990 history = []
991 991 commits_group = ([], _("Changesets"))
992 992 for commit in commits:
993 993 branch = ' (%s)' % commit.branch if commit.branch else ''
994 994 n_desc = 'r%s:%s%s' % (commit.idx, commit.short_id, branch)
995 995 commits_group[0].append((commit.raw_id, n_desc, 'sha'))
996 996 history.append(commits_group)
997 997
998 998 symbolic_reference = self._symbolic_reference
999 999
1000 1000 if self.rhodecode_vcs_repo.alias == 'svn':
1001 1001 adjusted_f_path = RepoFilesView.adjust_file_path_for_svn(
1002 1002 f_path, self.rhodecode_vcs_repo)
1003 1003 if adjusted_f_path != f_path:
1004 1004 log.debug(
1005 1005 'Recognized svn tag or branch in file "%s", using svn '
1006 1006 'specific symbolic references', f_path)
1007 1007 f_path = adjusted_f_path
1008 1008 symbolic_reference = self._symbolic_reference_svn
1009 1009
1010 1010 branches = self._create_references(
1011 1011 self.rhodecode_vcs_repo.branches, symbolic_reference, f_path, 'branch')
1012 1012 branches_group = (branches, _("Branches"))
1013 1013
1014 1014 tags = self._create_references(
1015 1015 self.rhodecode_vcs_repo.tags, symbolic_reference, f_path, 'tag')
1016 1016 tags_group = (tags, _("Tags"))
1017 1017
1018 1018 history.append(branches_group)
1019 1019 history.append(tags_group)
1020 1020
1021 1021 return history, commits
1022 1022
1023 1023 @LoginRequired()
1024 1024 @HasRepoPermissionAnyDecorator(
1025 1025 'repository.read', 'repository.write', 'repository.admin')
1026 1026 @view_config(
1027 1027 route_name='repo_file_history', request_method='GET',
1028 1028 renderer='json_ext')
1029 1029 def repo_file_history(self):
1030 1030 self.load_default_context()
1031 1031
1032 1032 commit_id, f_path = self._get_commit_and_path()
1033 1033 commit = self._get_commit_or_redirect(commit_id)
1034 1034 file_node = self._get_filenode_or_redirect(commit, f_path)
1035 1035
1036 1036 if file_node.is_file():
1037 1037 file_history, _hist = self._get_node_history(commit, f_path)
1038 1038
1039 1039 res = []
1040 1040 for section_items, section in file_history:
1041 1041 items = []
1042 1042 for obj_id, obj_text, obj_type in section_items:
1043 1043 at_rev = ''
1044 1044 if obj_type in ['branch', 'bookmark', 'tag']:
1045 1045 at_rev = obj_text
1046 1046 entry = {
1047 1047 'id': obj_id,
1048 1048 'text': obj_text,
1049 1049 'type': obj_type,
1050 1050 'at_rev': at_rev
1051 1051 }
1052 1052
1053 1053 items.append(entry)
1054 1054
1055 1055 res.append({
1056 1056 'text': section,
1057 1057 'children': items
1058 1058 })
1059 1059
1060 1060 data = {
1061 1061 'more': False,
1062 1062 'results': res
1063 1063 }
1064 1064 return data
1065 1065
1066 1066 log.warning('Cannot fetch history for directory')
1067 1067 raise HTTPBadRequest()
1068 1068
1069 1069 @LoginRequired()
1070 1070 @HasRepoPermissionAnyDecorator(
1071 1071 'repository.read', 'repository.write', 'repository.admin')
1072 1072 @view_config(
1073 1073 route_name='repo_file_authors', request_method='GET',
1074 1074 renderer='rhodecode:templates/files/file_authors_box.mako')
1075 1075 def repo_file_authors(self):
1076 1076 c = self.load_default_context()
1077 1077
1078 1078 commit_id, f_path = self._get_commit_and_path()
1079 1079 commit = self._get_commit_or_redirect(commit_id)
1080 1080 file_node = self._get_filenode_or_redirect(commit, f_path)
1081 1081
1082 1082 if not file_node.is_file():
1083 1083 raise HTTPBadRequest()
1084 1084
1085 1085 c.file_last_commit = file_node.last_commit
1086 1086 if self.request.GET.get('annotate') == '1':
1087 1087 # use _hist from annotation if annotation mode is on
1088 1088 commit_ids = set(x[1] for x in file_node.annotate)
1089 1089 _hist = (
1090 1090 self.rhodecode_vcs_repo.get_commit(commit_id)
1091 1091 for commit_id in commit_ids)
1092 1092 else:
1093 1093 _f_history, _hist = self._get_node_history(commit, f_path)
1094 1094 c.file_author = False
1095 1095
1096 1096 unique = collections.OrderedDict()
1097 1097 for commit in _hist:
1098 1098 author = commit.author
1099 1099 if author not in unique:
1100 1100 unique[commit.author] = [
1101 1101 h.email(author),
1102 1102 h.person(author, 'username_or_name_or_email'),
1103 1103 1 # counter
1104 1104 ]
1105 1105
1106 1106 else:
1107 1107 # increase counter
1108 1108 unique[commit.author][2] += 1
1109 1109
1110 1110 c.authors = [val for val in unique.values()]
1111 1111
1112 1112 return self._get_template_context(c)
1113 1113
1114 1114 @LoginRequired()
1115 1115 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1116 1116 @view_config(
1117 route_name='repo_files_check_head', request_method='POST',
1118 renderer='json_ext', xhr=True)
1119 def repo_files_check_head(self):
1120 self.load_default_context()
1121
1122 commit_id, f_path = self._get_commit_and_path()
1123 _branch_name, _sha_commit_id, is_head = \
1124 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1125
1126 new_path = self.request.POST.get('path')
1127 operation = self.request.POST.get('operation')
1128 path_exist = ''
1129
1130 if new_path and operation in ['create', 'upload']:
1131 new_f_path = os.path.join(f_path.lstrip('/'), new_path)
1132 try:
1133 commit_obj = self.rhodecode_vcs_repo.get_commit(commit_id)
1134 # NOTE(dan): construct whole path without leading /
1135 file_node = commit_obj.get_node(new_f_path)
1136 if file_node is not None:
1137 path_exist = new_f_path
1138 except EmptyRepositoryError:
1139 pass
1140 except Exception:
1141 pass
1142
1143 return {
1144 'branch': _branch_name,
1145 'sha': _sha_commit_id,
1146 'is_head': is_head,
1147 'path_exists': path_exist
1148 }
1149
1150 @LoginRequired()
1151 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1152 @view_config(
1117 1153 route_name='repo_files_remove_file', request_method='GET',
1118 1154 renderer='rhodecode:templates/files/files_delete.mako')
1119 1155 def repo_files_remove_file(self):
1120 1156 _ = self.request.translate
1121 1157 c = self.load_default_context()
1122 1158 commit_id, f_path = self._get_commit_and_path()
1123 1159
1124 1160 self._ensure_not_locked()
1125 1161 _branch_name, _sha_commit_id, is_head = \
1126 1162 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1127 1163
1128 1164 self.forbid_non_head(is_head, f_path)
1129 1165 self.check_branch_permission(_branch_name)
1130 1166
1131 1167 c.commit = self._get_commit_or_redirect(commit_id)
1132 1168 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1133 1169
1134 1170 c.default_message = _(
1135 1171 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1136 1172 c.f_path = f_path
1137 1173
1138 1174 return self._get_template_context(c)
1139 1175
1140 1176 @LoginRequired()
1141 1177 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1142 1178 @CSRFRequired()
1143 1179 @view_config(
1144 1180 route_name='repo_files_delete_file', request_method='POST',
1145 1181 renderer=None)
1146 1182 def repo_files_delete_file(self):
1147 1183 _ = self.request.translate
1148 1184
1149 1185 c = self.load_default_context()
1150 1186 commit_id, f_path = self._get_commit_and_path()
1151 1187
1152 1188 self._ensure_not_locked()
1153 1189 _branch_name, _sha_commit_id, is_head = \
1154 1190 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1155 1191
1156 1192 self.forbid_non_head(is_head, f_path)
1157 1193 self.check_branch_permission(_branch_name)
1158 1194
1159 1195 c.commit = self._get_commit_or_redirect(commit_id)
1160 1196 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1161 1197
1162 1198 c.default_message = _(
1163 1199 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1164 1200 c.f_path = f_path
1165 1201 node_path = f_path
1166 1202 author = self._rhodecode_db_user.full_contact
1167 1203 message = self.request.POST.get('message') or c.default_message
1168 1204 try:
1169 1205 nodes = {
1170 1206 node_path: {
1171 1207 'content': ''
1172 1208 }
1173 1209 }
1174 1210 ScmModel().delete_nodes(
1175 1211 user=self._rhodecode_db_user.user_id, repo=self.db_repo,
1176 1212 message=message,
1177 1213 nodes=nodes,
1178 1214 parent_commit=c.commit,
1179 1215 author=author,
1180 1216 )
1181 1217
1182 1218 h.flash(
1183 1219 _('Successfully deleted file `{}`').format(
1184 1220 h.escape(f_path)), category='success')
1185 1221 except Exception:
1186 1222 log.exception('Error during commit operation')
1187 1223 h.flash(_('Error occurred during commit'), category='error')
1188 1224 raise HTTPFound(
1189 1225 h.route_path('repo_commit', repo_name=self.db_repo_name,
1190 1226 commit_id='tip'))
1191 1227
1192 1228 @LoginRequired()
1193 1229 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1194 1230 @view_config(
1195 1231 route_name='repo_files_edit_file', request_method='GET',
1196 1232 renderer='rhodecode:templates/files/files_edit.mako')
1197 1233 def repo_files_edit_file(self):
1198 1234 _ = self.request.translate
1199 1235 c = self.load_default_context()
1200 1236 commit_id, f_path = self._get_commit_and_path()
1201 1237
1202 1238 self._ensure_not_locked()
1203 1239 _branch_name, _sha_commit_id, is_head = \
1204 1240 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1205 1241
1206 1242 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1207 1243 self.check_branch_permission(_branch_name, commit_id=commit_id)
1208 1244
1209 1245 c.commit = self._get_commit_or_redirect(commit_id)
1210 1246 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1211 1247
1212 1248 if c.file.is_binary:
1213 1249 files_url = h.route_path(
1214 1250 'repo_files',
1215 1251 repo_name=self.db_repo_name,
1216 1252 commit_id=c.commit.raw_id, f_path=f_path)
1217 1253 raise HTTPFound(files_url)
1218 1254
1219 1255 c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path)
1220 1256 c.f_path = f_path
1221 1257
1222 1258 return self._get_template_context(c)
1223 1259
1224 1260 @LoginRequired()
1225 1261 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1226 1262 @CSRFRequired()
1227 1263 @view_config(
1228 1264 route_name='repo_files_update_file', request_method='POST',
1229 1265 renderer=None)
1230 1266 def repo_files_update_file(self):
1231 1267 _ = self.request.translate
1232 1268 c = self.load_default_context()
1233 1269 commit_id, f_path = self._get_commit_and_path()
1234 1270
1235 1271 self._ensure_not_locked()
1236 1272
1237 1273 c.commit = self._get_commit_or_redirect(commit_id)
1238 1274 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1239 1275
1240 1276 if c.file.is_binary:
1241 1277 raise HTTPFound(h.route_path('repo_files', repo_name=self.db_repo_name,
1242 1278 commit_id=c.commit.raw_id, f_path=f_path))
1243 1279
1244 1280 _branch_name, _sha_commit_id, is_head = \
1245 1281 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1246 1282
1247 1283 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1248 1284 self.check_branch_permission(_branch_name, commit_id=commit_id)
1249 1285
1250 1286 c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path)
1251 1287 c.f_path = f_path
1252 1288
1253 1289 old_content = c.file.content
1254 1290 sl = old_content.splitlines(1)
1255 1291 first_line = sl[0] if sl else ''
1256 1292
1257 1293 r_post = self.request.POST
1258 1294 # line endings: 0 - Unix, 1 - Mac, 2 - DOS
1259 1295 line_ending_mode = detect_mode(first_line, 0)
1260 1296 content = convert_line_endings(r_post.get('content', ''), line_ending_mode)
1261 1297
1262 1298 message = r_post.get('message') or c.default_message
1263 1299 org_node_path = c.file.unicode_path
1264 1300 filename = r_post['filename']
1265 1301
1266 1302 root_path = c.file.dir_path
1267 1303 pure_path = self.create_pure_path(root_path, filename)
1268 1304 node_path = safe_unicode(bytes(pure_path))
1269 1305
1270 1306 default_redirect_url = h.route_path('repo_commit', repo_name=self.db_repo_name,
1271 1307 commit_id=commit_id)
1272 1308 if content == old_content and node_path == org_node_path:
1273 1309 h.flash(_('No changes detected on {}').format(h.escape(org_node_path)),
1274 1310 category='warning')
1275 1311 raise HTTPFound(default_redirect_url)
1276 1312
1277 1313 try:
1278 1314 mapping = {
1279 1315 org_node_path: {
1280 1316 'org_filename': org_node_path,
1281 1317 'filename': node_path,
1282 1318 'content': content,
1283 1319 'lexer': '',
1284 1320 'op': 'mod',
1285 1321 'mode': c.file.mode
1286 1322 }
1287 1323 }
1288 1324
1289 1325 commit = ScmModel().update_nodes(
1290 1326 user=self._rhodecode_db_user.user_id,
1291 1327 repo=self.db_repo,
1292 1328 message=message,
1293 1329 nodes=mapping,
1294 1330 parent_commit=c.commit,
1295 1331 )
1296 1332
1297 1333 h.flash(_('Successfully committed changes to file `{}`').format(
1298 1334 h.escape(f_path)), category='success')
1299 1335 default_redirect_url = h.route_path(
1300 1336 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1301 1337
1302 1338 except Exception:
1303 1339 log.exception('Error occurred during commit')
1304 1340 h.flash(_('Error occurred during commit'), category='error')
1305 1341
1306 1342 raise HTTPFound(default_redirect_url)
1307 1343
1308 1344 @LoginRequired()
1309 1345 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1310 1346 @view_config(
1311 1347 route_name='repo_files_add_file', request_method='GET',
1312 1348 renderer='rhodecode:templates/files/files_add.mako')
1313 1349 @view_config(
1314 1350 route_name='repo_files_upload_file', request_method='GET',
1315 1351 renderer='rhodecode:templates/files/files_upload.mako')
1316 1352 def repo_files_add_file(self):
1317 1353 _ = self.request.translate
1318 1354 c = self.load_default_context()
1319 1355 commit_id, f_path = self._get_commit_and_path()
1320 1356
1321 1357 self._ensure_not_locked()
1322 1358
1323 1359 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1324 1360 if c.commit is None:
1325 1361 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1326 1362
1327 1363 if self.rhodecode_vcs_repo.is_empty():
1328 1364 # for empty repository we cannot check for current branch, we rely on
1329 1365 # c.commit.branch instead
1330 1366 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1331 1367 else:
1332 1368 _branch_name, _sha_commit_id, is_head = \
1333 1369 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1334 1370
1335 1371 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1336 1372 self.check_branch_permission(_branch_name, commit_id=commit_id)
1337 1373
1338 1374 c.default_message = (_('Added file via RhodeCode Enterprise'))
1339 1375 c.f_path = f_path.lstrip('/') # ensure not relative path
1340 1376
1341 1377 return self._get_template_context(c)
1342 1378
1343 1379 @LoginRequired()
1344 1380 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1345 1381 @CSRFRequired()
1346 1382 @view_config(
1347 1383 route_name='repo_files_create_file', request_method='POST',
1348 1384 renderer=None)
1349 1385 def repo_files_create_file(self):
1350 1386 _ = self.request.translate
1351 1387 c = self.load_default_context()
1352 1388 commit_id, f_path = self._get_commit_and_path()
1353 1389
1354 1390 self._ensure_not_locked()
1355 1391
1356 1392 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1357 1393 if c.commit is None:
1358 1394 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1359 1395
1360 1396 # calculate redirect URL
1361 1397 if self.rhodecode_vcs_repo.is_empty():
1362 1398 default_redirect_url = h.route_path(
1363 1399 'repo_summary', repo_name=self.db_repo_name)
1364 1400 else:
1365 1401 default_redirect_url = h.route_path(
1366 1402 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1367 1403
1368 1404 if self.rhodecode_vcs_repo.is_empty():
1369 1405 # for empty repository we cannot check for current branch, we rely on
1370 1406 # c.commit.branch instead
1371 1407 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1372 1408 else:
1373 1409 _branch_name, _sha_commit_id, is_head = \
1374 1410 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1375 1411
1376 1412 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1377 1413 self.check_branch_permission(_branch_name, commit_id=commit_id)
1378 1414
1379 1415 c.default_message = (_('Added file via RhodeCode Enterprise'))
1380 1416 c.f_path = f_path
1381 1417
1382 1418 r_post = self.request.POST
1383 1419 message = r_post.get('message') or c.default_message
1384 1420 filename = r_post.get('filename')
1385 1421 unix_mode = 0
1386 1422 content = convert_line_endings(r_post.get('content', ''), unix_mode)
1387 1423
1388 1424 if not filename:
1389 1425 # If there's no commit, redirect to repo summary
1390 1426 if type(c.commit) is EmptyCommit:
1391 1427 redirect_url = h.route_path(
1392 1428 'repo_summary', repo_name=self.db_repo_name)
1393 1429 else:
1394 1430 redirect_url = default_redirect_url
1395 1431 h.flash(_('No filename specified'), category='warning')
1396 1432 raise HTTPFound(redirect_url)
1397 1433
1398 1434 root_path = f_path
1399 1435 pure_path = self.create_pure_path(root_path, filename)
1400 1436 node_path = safe_unicode(bytes(pure_path).lstrip('/'))
1401 1437
1402 1438 author = self._rhodecode_db_user.full_contact
1403 1439 nodes = {
1404 1440 node_path: {
1405 1441 'content': content
1406 1442 }
1407 1443 }
1408 1444
1409 1445 try:
1410 1446
1411 1447 commit = ScmModel().create_nodes(
1412 1448 user=self._rhodecode_db_user.user_id,
1413 1449 repo=self.db_repo,
1414 1450 message=message,
1415 1451 nodes=nodes,
1416 1452 parent_commit=c.commit,
1417 1453 author=author,
1418 1454 )
1419 1455
1420 1456 h.flash(_('Successfully committed new file `{}`').format(
1421 1457 h.escape(node_path)), category='success')
1422 1458
1423 1459 default_redirect_url = h.route_path(
1424 1460 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1425 1461
1426 1462 except NonRelativePathError:
1427 1463 log.exception('Non Relative path found')
1428 1464 h.flash(_('The location specified must be a relative path and must not '
1429 1465 'contain .. in the path'), category='warning')
1430 1466 raise HTTPFound(default_redirect_url)
1431 1467 except (NodeError, NodeAlreadyExistsError) as e:
1432 1468 h.flash(_(h.escape(e)), category='error')
1433 1469 except Exception:
1434 1470 log.exception('Error occurred during commit')
1435 1471 h.flash(_('Error occurred during commit'), category='error')
1436 1472
1437 1473 raise HTTPFound(default_redirect_url)
1438 1474
1439 1475 @LoginRequired()
1440 1476 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1441 1477 @CSRFRequired()
1442 1478 @view_config(
1443 1479 route_name='repo_files_upload_file', request_method='POST',
1444 1480 renderer='json_ext')
1445 1481 def repo_files_upload_file(self):
1446 1482 _ = self.request.translate
1447 1483 c = self.load_default_context()
1448 1484 commit_id, f_path = self._get_commit_and_path()
1449 1485
1450 1486 self._ensure_not_locked()
1451 1487
1452 1488 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1453 1489 if c.commit is None:
1454 1490 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1455 1491
1456 1492 # calculate redirect URL
1457 1493 if self.rhodecode_vcs_repo.is_empty():
1458 1494 default_redirect_url = h.route_path(
1459 1495 'repo_summary', repo_name=self.db_repo_name)
1460 1496 else:
1461 1497 default_redirect_url = h.route_path(
1462 1498 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1463 1499
1464 1500 if self.rhodecode_vcs_repo.is_empty():
1465 1501 # for empty repository we cannot check for current branch, we rely on
1466 1502 # c.commit.branch instead
1467 1503 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1468 1504 else:
1469 1505 _branch_name, _sha_commit_id, is_head = \
1470 1506 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1471 1507
1472 1508 error = self.forbid_non_head(is_head, f_path, json_mode=True)
1473 1509 if error:
1474 1510 return {
1475 1511 'error': error,
1476 1512 'redirect_url': default_redirect_url
1477 1513 }
1478 1514 error = self.check_branch_permission(_branch_name, json_mode=True)
1479 1515 if error:
1480 1516 return {
1481 1517 'error': error,
1482 1518 'redirect_url': default_redirect_url
1483 1519 }
1484 1520
1485 1521 c.default_message = (_('Uploaded file via RhodeCode Enterprise'))
1486 1522 c.f_path = f_path
1487 1523
1488 1524 r_post = self.request.POST
1489 1525
1490 1526 message = c.default_message
1491 1527 user_message = r_post.getall('message')
1492 1528 if isinstance(user_message, list) and user_message:
1493 1529 # we take the first from duplicated results if it's not empty
1494 1530 message = user_message[0] if user_message[0] else message
1495 1531
1496 1532 nodes = {}
1497 1533
1498 1534 for file_obj in r_post.getall('files_upload') or []:
1499 1535 content = file_obj.file
1500 1536 filename = file_obj.filename
1501 1537
1502 1538 root_path = f_path
1503 1539 pure_path = self.create_pure_path(root_path, filename)
1504 1540 node_path = safe_unicode(bytes(pure_path).lstrip('/'))
1505 1541
1506 1542 nodes[node_path] = {
1507 1543 'content': content
1508 1544 }
1509 1545
1510 1546 if not nodes:
1511 1547 error = 'missing files'
1512 1548 return {
1513 1549 'error': error,
1514 1550 'redirect_url': default_redirect_url
1515 1551 }
1516 1552
1517 1553 author = self._rhodecode_db_user.full_contact
1518 1554
1519 1555 try:
1520 1556 commit = ScmModel().create_nodes(
1521 1557 user=self._rhodecode_db_user.user_id,
1522 1558 repo=self.db_repo,
1523 1559 message=message,
1524 1560 nodes=nodes,
1525 1561 parent_commit=c.commit,
1526 1562 author=author,
1527 1563 )
1528 1564 if len(nodes) == 1:
1529 1565 flash_message = _('Successfully committed {} new files').format(len(nodes))
1530 1566 else:
1531 1567 flash_message = _('Successfully committed 1 new file')
1532 1568
1533 1569 h.flash(flash_message, category='success')
1534 1570
1535 1571 default_redirect_url = h.route_path(
1536 1572 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1537 1573
1538 1574 except NonRelativePathError:
1539 1575 log.exception('Non Relative path found')
1540 1576 error = _('The location specified must be a relative path and must not '
1541 1577 'contain .. in the path')
1542 1578 h.flash(error, category='warning')
1543 1579
1544 1580 return {
1545 1581 'error': error,
1546 1582 'redirect_url': default_redirect_url
1547 1583 }
1548 1584 except (NodeError, NodeAlreadyExistsError) as e:
1549 1585 error = h.escape(e)
1550 1586 h.flash(error, category='error')
1551 1587
1552 1588 return {
1553 1589 'error': error,
1554 1590 'redirect_url': default_redirect_url
1555 1591 }
1556 1592 except Exception:
1557 1593 log.exception('Error occurred during commit')
1558 1594 error = _('Error occurred during commit')
1559 1595 h.flash(error, category='error')
1560 1596 return {
1561 1597 'error': error,
1562 1598 'redirect_url': default_redirect_url
1563 1599 }
1564 1600
1565 1601 return {
1566 1602 'error': None,
1567 1603 'redirect_url': default_redirect_url
1568 1604 }
@@ -1,3054 +1,3058 b''
1 1 //Primary CSS
2 2
3 3 //--- IMPORTS ------------------//
4 4
5 5 @import 'helpers';
6 6 @import 'mixins';
7 7 @import 'rcicons';
8 8 @import 'variables';
9 9 @import 'bootstrap-variables';
10 10 @import 'form-bootstrap';
11 11 @import 'codemirror';
12 12 @import 'legacy_code_styles';
13 13 @import 'readme-box';
14 14 @import 'progress-bar';
15 15
16 16 @import 'type';
17 17 @import 'alerts';
18 18 @import 'buttons';
19 19 @import 'tags';
20 20 @import 'code-block';
21 21 @import 'examples';
22 22 @import 'login';
23 23 @import 'main-content';
24 24 @import 'select2';
25 25 @import 'comments';
26 26 @import 'panels-bootstrap';
27 27 @import 'panels';
28 28 @import 'deform';
29 29 @import 'tooltips';
30 30
31 31 //--- BASE ------------------//
32 32 .noscript-error {
33 33 top: 0;
34 34 left: 0;
35 35 width: 100%;
36 36 z-index: 101;
37 37 text-align: center;
38 38 font-size: 120%;
39 39 color: white;
40 40 background-color: @alert2;
41 41 padding: 5px 0 5px 0;
42 42 font-weight: @text-semibold-weight;
43 43 font-family: @text-semibold;
44 44 }
45 45
46 46 html {
47 47 display: table;
48 48 height: 100%;
49 49 width: 100%;
50 50 }
51 51
52 52 body {
53 53 display: table-cell;
54 54 width: 100%;
55 55 }
56 56
57 57 //--- LAYOUT ------------------//
58 58
59 59 .hidden{
60 60 display: none !important;
61 61 }
62 62
63 63 .box{
64 64 float: left;
65 65 width: 100%;
66 66 }
67 67
68 68 .browser-header {
69 69 clear: both;
70 70 }
71 71 .main {
72 72 clear: both;
73 73 padding:0 0 @pagepadding;
74 74 height: auto;
75 75
76 76 &:after { //clearfix
77 77 content:"";
78 78 clear:both;
79 79 width:100%;
80 80 display:block;
81 81 }
82 82 }
83 83
84 84 .action-link{
85 85 margin-left: @padding;
86 86 padding-left: @padding;
87 87 border-left: @border-thickness solid @border-default-color;
88 88 }
89 89
90 90 .cursor-pointer {
91 91 cursor: pointer;
92 92 }
93 93
94 94 input + .action-link, .action-link.first{
95 95 border-left: none;
96 96 }
97 97
98 98 .action-link.last{
99 99 margin-right: @padding;
100 100 padding-right: @padding;
101 101 }
102 102
103 103 .action-link.active,
104 104 .action-link.active a{
105 105 color: @grey4;
106 106 }
107 107
108 108 .action-link.disabled {
109 109 color: @grey4;
110 110 cursor: inherit;
111 111 }
112 112
113 113
114 114 .clipboard-action {
115 115 cursor: pointer;
116 116 margin-left: 5px;
117 117
118 118 &:not(.no-grey) {
119 119
120 120 &:hover {
121 121 color: @grey2;
122 122 }
123 123 color: @grey4;
124 124 }
125 125 }
126 126
127 127 ul.simple-list{
128 128 list-style: none;
129 129 margin: 0;
130 130 padding: 0;
131 131 }
132 132
133 133 .main-content {
134 134 padding-bottom: @pagepadding;
135 135 }
136 136
137 137 .wide-mode-wrapper {
138 138 max-width:4000px !important;
139 139 }
140 140
141 141 .wrapper {
142 142 position: relative;
143 143 max-width: @wrapper-maxwidth;
144 144 margin: 0 auto;
145 145 }
146 146
147 147 #content {
148 148 clear: both;
149 149 padding: 0 @contentpadding;
150 150 }
151 151
152 152 .advanced-settings-fields{
153 153 input{
154 154 margin-left: @textmargin;
155 155 margin-right: @padding/2;
156 156 }
157 157 }
158 158
159 159 .cs_files_title {
160 160 margin: @pagepadding 0 0;
161 161 }
162 162
163 163 input.inline[type="file"] {
164 164 display: inline;
165 165 }
166 166
167 167 .error_page {
168 168 margin: 10% auto;
169 169
170 170 h1 {
171 171 color: @grey2;
172 172 }
173 173
174 174 .alert {
175 175 margin: @padding 0;
176 176 }
177 177
178 178 .error-branding {
179 179 color: @grey4;
180 180 font-weight: @text-semibold-weight;
181 181 font-family: @text-semibold;
182 182 }
183 183
184 184 .error_message {
185 185 font-family: @text-regular;
186 186 }
187 187
188 188 .sidebar {
189 189 min-height: 275px;
190 190 margin: 0;
191 191 padding: 0 0 @sidebarpadding @sidebarpadding;
192 192 border: none;
193 193 }
194 194
195 195 .main-content {
196 196 position: relative;
197 197 margin: 0 @sidebarpadding @sidebarpadding;
198 198 padding: 0 0 0 @sidebarpadding;
199 199 border-left: @border-thickness solid @grey5;
200 200
201 201 @media (max-width:767px) {
202 202 clear: both;
203 203 width: 100%;
204 204 margin: 0;
205 205 border: none;
206 206 }
207 207 }
208 208
209 209 .inner-column {
210 210 float: left;
211 211 width: 29.75%;
212 212 min-height: 150px;
213 213 margin: @sidebarpadding 2% 0 0;
214 214 padding: 0 2% 0 0;
215 215 border-right: @border-thickness solid @grey5;
216 216
217 217 @media (max-width:767px) {
218 218 clear: both;
219 219 width: 100%;
220 220 border: none;
221 221 }
222 222
223 223 ul {
224 224 padding-left: 1.25em;
225 225 }
226 226
227 227 &:last-child {
228 228 margin: @sidebarpadding 0 0;
229 229 border: none;
230 230 }
231 231
232 232 h4 {
233 233 margin: 0 0 @padding;
234 234 font-weight: @text-semibold-weight;
235 235 font-family: @text-semibold;
236 236 }
237 237 }
238 238 }
239 239 .error-page-logo {
240 240 width: 130px;
241 241 height: 160px;
242 242 }
243 243
244 244 // HEADER
245 245 .header {
246 246
247 247 // TODO: johbo: Fix login pages, so that they work without a min-height
248 248 // for the header and then remove the min-height. I chose a smaller value
249 249 // intentionally here to avoid rendering issues in the main navigation.
250 250 min-height: 49px;
251 251 min-width: 1024px;
252 252
253 253 position: relative;
254 254 vertical-align: bottom;
255 255 padding: 0 @header-padding;
256 256 background-color: @grey1;
257 257 color: @grey5;
258 258
259 259 .title {
260 260 overflow: visible;
261 261 }
262 262
263 263 &:before,
264 264 &:after {
265 265 content: "";
266 266 clear: both;
267 267 width: 100%;
268 268 }
269 269
270 270 // TODO: johbo: Avoids breaking "Repositories" chooser
271 271 .select2-container .select2-choice .select2-arrow {
272 272 display: none;
273 273 }
274 274 }
275 275
276 276 #header-inner {
277 277 &.title {
278 278 margin: 0;
279 279 }
280 280 &:before,
281 281 &:after {
282 282 content: "";
283 283 clear: both;
284 284 }
285 285 }
286 286
287 287 // Gists
288 288 #files_data {
289 289 clear: both; //for firefox
290 290 padding-top: 10px;
291 291 }
292 292
293 293 #gistid {
294 294 margin-right: @padding;
295 295 }
296 296
297 297 // Global Settings Editor
298 298 .textarea.editor {
299 299 float: left;
300 300 position: relative;
301 301 max-width: @texteditor-width;
302 302
303 303 select {
304 304 position: absolute;
305 305 top:10px;
306 306 right:0;
307 307 }
308 308
309 309 .CodeMirror {
310 310 margin: 0;
311 311 }
312 312
313 313 .help-block {
314 314 margin: 0 0 @padding;
315 315 padding:.5em;
316 316 background-color: @grey6;
317 317 &.pre-formatting {
318 318 white-space: pre;
319 319 }
320 320 }
321 321 }
322 322
323 323 ul.auth_plugins {
324 324 margin: @padding 0 @padding @legend-width;
325 325 padding: 0;
326 326
327 327 li {
328 328 margin-bottom: @padding;
329 329 line-height: 1em;
330 330 list-style-type: none;
331 331
332 332 .auth_buttons .btn {
333 333 margin-right: @padding;
334 334 }
335 335
336 336 }
337 337 }
338 338
339 339
340 340 // My Account PR list
341 341
342 342 #show_closed {
343 343 margin: 0 1em 0 0;
344 344 }
345 345
346 346 #pull_request_list_table {
347 347 .closed {
348 348 background-color: @grey6;
349 349 }
350 350
351 351 .state-creating,
352 352 .state-updating,
353 353 .state-merging
354 354 {
355 355 background-color: @grey6;
356 356 }
357 357
358 358 .td-status {
359 359 padding-left: .5em;
360 360 }
361 361 .log-container .truncate {
362 362 height: 2.75em;
363 363 white-space: pre-line;
364 364 }
365 365 table.rctable .user {
366 366 padding-left: 0;
367 367 }
368 368 table.rctable {
369 369 td.td-description,
370 370 .rc-user {
371 371 min-width: auto;
372 372 }
373 373 }
374 374 }
375 375
376 376 // Pull Requests
377 377
378 378 .pullrequests_section_head {
379 379 display: block;
380 380 clear: both;
381 381 margin: @padding 0;
382 382 font-weight: @text-bold-weight;
383 383 font-family: @text-bold;
384 384 }
385 385
386 386 .pr-commit-flow {
387 387 position: relative;
388 388 font-weight: 600;
389 389
390 390 .tag {
391 391 display: inline-block;
392 392 margin: 0 1em .5em 0;
393 393 }
394 394
395 395 .clone-url {
396 396 display: inline-block;
397 397 margin: 0 0 .5em 0;
398 398 padding: 0;
399 399 line-height: 1.2em;
400 400 }
401 401 }
402 402
403 403 .pr-mergeinfo {
404 404 min-width: 95% !important;
405 405 padding: 0 !important;
406 406 border: 0;
407 407 }
408 408 .pr-mergeinfo-copy {
409 409 padding: 0 0;
410 410 }
411 411
412 412 .pr-pullinfo {
413 413 min-width: 95% !important;
414 414 padding: 0 !important;
415 415 border: 0;
416 416 }
417 417 .pr-pullinfo-copy {
418 418 padding: 0 0;
419 419 }
420 420
421 421 .pr-title-input {
422 422 width: 100%;
423 423 font-size: 18px;
424 424 margin: 0 0 4px 0;
425 425 padding: 0;
426 426 line-height: 1.7em;
427 427 color: @text-color;
428 428 letter-spacing: .02em;
429 429 font-weight: @text-bold-weight;
430 430 font-family: @text-bold;
431 431
432 432 &:hover {
433 433 box-shadow: none;
434 434 }
435 435 }
436 436
437 437 #pr-title {
438 438 input {
439 439 border: 1px transparent;
440 440 color: black;
441 441 opacity: 1;
442 442 background: #fff;
443 443 font-size: 18px;
444 444 }
445 445 }
446 446
447 447 .pr-title-closed-tag {
448 448 font-size: 16px;
449 449 }
450 450
451 451 #pr-desc {
452 452 padding: 10px 0;
453 453
454 454 .markdown-block {
455 455 padding: 0;
456 456 margin-bottom: -30px;
457 457 }
458 458 }
459 459
460 460 #pullrequest_title {
461 461 width: 100%;
462 462 box-sizing: border-box;
463 463 }
464 464
465 465 #pr_open_message {
466 466 border: @border-thickness solid #fff;
467 467 border-radius: @border-radius;
468 468 padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0;
469 469 text-align: left;
470 470 overflow: hidden;
471 471 }
472 472
473 473 .pr-details-title {
474 474 height: 16px
475 475 }
476 476
477 477 .pr-details-title-author-pref {
478 478 padding-right: 10px
479 479 }
480 480
481 481 .label-pr-detail {
482 482 display: table-cell;
483 483 width: 120px;
484 484 padding-top: 7.5px;
485 485 padding-bottom: 7.5px;
486 486 padding-right: 7.5px;
487 487 }
488 488
489 489 .source-details ul {
490 490 padding: 10px 16px;
491 491 }
492 492
493 493 .source-details-action {
494 494 color: @grey4;
495 495 font-size: 11px
496 496 }
497 497
498 498 .pr-submit-button {
499 499 float: right;
500 500 margin: 0 0 0 5px;
501 501 }
502 502
503 503 .pr-spacing-container {
504 504 padding: 20px;
505 505 clear: both
506 506 }
507 507
508 508 #pr-description-input {
509 509 margin-bottom: 0;
510 510 }
511 511
512 512 .pr-description-label {
513 513 vertical-align: top;
514 514 }
515 515
516 516 #open_edit_pullrequest {
517 517 padding: 0;
518 518 }
519 519
520 520 #close_edit_pullrequest {
521 521
522 522 }
523 523
524 524 #delete_pullrequest {
525 525 clear: inherit;
526 526
527 527 form {
528 528 display: inline;
529 529 }
530 530
531 531 }
532 532
533 533 .perms_section_head {
534 534 min-width: 625px;
535 535
536 536 h2 {
537 537 margin-bottom: 0;
538 538 }
539 539
540 540 .label-checkbox {
541 541 float: left;
542 542 }
543 543
544 544 &.field {
545 545 margin: @space 0 @padding;
546 546 }
547 547
548 548 &:first-child.field {
549 549 margin-top: 0;
550 550
551 551 .label {
552 552 margin-top: 0;
553 553 padding-top: 0;
554 554 }
555 555
556 556 .radios {
557 557 padding-top: 0;
558 558 }
559 559 }
560 560
561 561 .radios {
562 562 position: relative;
563 563 width: 505px;
564 564 }
565 565 }
566 566
567 567 //--- MODULES ------------------//
568 568
569 569
570 570 // Server Announcement
571 571 #server-announcement {
572 572 width: 95%;
573 573 margin: @padding auto;
574 574 padding: @padding;
575 575 border-width: 2px;
576 576 border-style: solid;
577 577 .border-radius(2px);
578 578 font-weight: @text-bold-weight;
579 579 font-family: @text-bold;
580 580
581 581 &.info { border-color: @alert4; background-color: @alert4-inner; }
582 582 &.warning { border-color: @alert3; background-color: @alert3-inner; }
583 583 &.error { border-color: @alert2; background-color: @alert2-inner; }
584 584 &.success { border-color: @alert1; background-color: @alert1-inner; }
585 585 &.neutral { border-color: @grey3; background-color: @grey6; }
586 586 }
587 587
588 588 // Fixed Sidebar Column
589 589 .sidebar-col-wrapper {
590 590 padding-left: @sidebar-all-width;
591 591
592 592 .sidebar {
593 593 width: @sidebar-width;
594 594 margin-left: -@sidebar-all-width;
595 595 }
596 596 }
597 597
598 598 .sidebar-col-wrapper.scw-small {
599 599 padding-left: @sidebar-small-all-width;
600 600
601 601 .sidebar {
602 602 width: @sidebar-small-width;
603 603 margin-left: -@sidebar-small-all-width;
604 604 }
605 605 }
606 606
607 607
608 608 // FOOTER
609 609 #footer {
610 610 padding: 0;
611 611 text-align: center;
612 612 vertical-align: middle;
613 613 color: @grey2;
614 614 font-size: 11px;
615 615
616 616 p {
617 617 margin: 0;
618 618 padding: 1em;
619 619 line-height: 1em;
620 620 }
621 621
622 622 .server-instance { //server instance
623 623 display: none;
624 624 }
625 625
626 626 .title {
627 627 float: none;
628 628 margin: 0 auto;
629 629 }
630 630 }
631 631
632 632 button.close {
633 633 padding: 0;
634 634 cursor: pointer;
635 635 background: transparent;
636 636 border: 0;
637 637 .box-shadow(none);
638 638 -webkit-appearance: none;
639 639 }
640 640
641 641 .close {
642 642 float: right;
643 643 font-size: 21px;
644 644 font-family: @text-bootstrap;
645 645 line-height: 1em;
646 646 font-weight: bold;
647 647 color: @grey2;
648 648
649 649 &:hover,
650 650 &:focus {
651 651 color: @grey1;
652 652 text-decoration: none;
653 653 cursor: pointer;
654 654 }
655 655 }
656 656
657 657 // GRID
658 658 .sorting,
659 659 .sorting_desc,
660 660 .sorting_asc {
661 661 cursor: pointer;
662 662 }
663 663 .sorting_desc:after {
664 664 content: "\00A0\25B2";
665 665 font-size: .75em;
666 666 }
667 667 .sorting_asc:after {
668 668 content: "\00A0\25BC";
669 669 font-size: .68em;
670 670 }
671 671
672 672
673 673 .user_auth_tokens {
674 674
675 675 &.truncate {
676 676 white-space: nowrap;
677 677 overflow: hidden;
678 678 text-overflow: ellipsis;
679 679 }
680 680
681 681 .fields .field .input {
682 682 margin: 0;
683 683 }
684 684
685 685 input#description {
686 686 width: 100px;
687 687 margin: 0;
688 688 }
689 689
690 690 .drop-menu {
691 691 // TODO: johbo: Remove this, should work out of the box when
692 692 // having multiple inputs inline
693 693 margin: 0 0 0 5px;
694 694 }
695 695 }
696 696 #user_list_table {
697 697 .closed {
698 698 background-color: @grey6;
699 699 }
700 700 }
701 701
702 702
703 703 input, textarea {
704 704 &.disabled {
705 705 opacity: .5;
706 706 }
707 707
708 708 &:hover {
709 709 border-color: @grey3;
710 710 box-shadow: @button-shadow;
711 711 }
712 712
713 713 &:focus {
714 714 border-color: @rcblue;
715 715 box-shadow: @button-shadow;
716 716 }
717 717 }
718 718
719 719 // remove extra padding in firefox
720 720 input::-moz-focus-inner { border:0; padding:0 }
721 721
722 722 .adjacent input {
723 723 margin-bottom: @padding;
724 724 }
725 725
726 726 .permissions_boxes {
727 727 display: block;
728 728 }
729 729
730 730 //FORMS
731 731
732 732 .medium-inline,
733 733 input#description.medium-inline {
734 734 display: inline;
735 735 width: @medium-inline-input-width;
736 736 min-width: 100px;
737 737 }
738 738
739 739 select {
740 740 //reset
741 741 -webkit-appearance: none;
742 742 -moz-appearance: none;
743 743
744 744 display: inline-block;
745 745 height: 28px;
746 746 width: auto;
747 747 margin: 0 @padding @padding 0;
748 748 padding: 0 18px 0 8px;
749 749 line-height:1em;
750 750 font-size: @basefontsize;
751 751 border: @border-thickness solid @grey5;
752 752 border-radius: @border-radius;
753 753 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
754 754 color: @grey4;
755 755 box-shadow: @button-shadow;
756 756
757 757 &:after {
758 758 content: "\00A0\25BE";
759 759 }
760 760
761 761 &:focus, &:hover {
762 762 outline: none;
763 763 border-color: @grey4;
764 764 color: @rcdarkblue;
765 765 }
766 766 }
767 767
768 768 option {
769 769 &:focus {
770 770 outline: none;
771 771 }
772 772 }
773 773
774 774 input,
775 775 textarea {
776 776 padding: @input-padding;
777 777 border: @input-border-thickness solid @border-highlight-color;
778 778 .border-radius (@border-radius);
779 779 font-family: @text-light;
780 780 font-size: @basefontsize;
781 781
782 782 &.input-sm {
783 783 padding: 5px;
784 784 }
785 785
786 786 &#description {
787 787 min-width: @input-description-minwidth;
788 788 min-height: 1em;
789 789 padding: 10px;
790 790 }
791 791 }
792 792
793 793 .field-sm {
794 794 input,
795 795 textarea {
796 796 padding: 5px;
797 797 }
798 798 }
799 799
800 800 textarea {
801 801 display: block;
802 802 clear: both;
803 803 width: 100%;
804 804 min-height: 100px;
805 805 margin-bottom: @padding;
806 806 .box-sizing(border-box);
807 807 overflow: auto;
808 808 }
809 809
810 810 label {
811 811 font-family: @text-light;
812 812 }
813 813
814 814 // GRAVATARS
815 815 // centers gravatar on username to the right
816 816
817 817 .gravatar {
818 818 display: inline;
819 819 min-width: 16px;
820 820 min-height: 16px;
821 821 margin: -5px 0;
822 822 padding: 0;
823 823 line-height: 1em;
824 824 box-sizing: content-box;
825 825 border-radius: 50%;
826 826
827 827 &.gravatar-large {
828 828 margin: -0.5em .25em -0.5em 0;
829 829 }
830 830
831 831 & + .user {
832 832 display: inline;
833 833 margin: 0;
834 834 padding: 0 0 0 .17em;
835 835 line-height: 1em;
836 836 }
837 837
838 838 & + .no-margin {
839 839 margin: 0
840 840 }
841 841
842 842 }
843 843
844 844 .user-inline-data {
845 845 display: inline-block;
846 846 float: left;
847 847 padding-left: .5em;
848 848 line-height: 1.3em;
849 849 }
850 850
851 851 .rc-user { // gravatar + user wrapper
852 852 float: left;
853 853 position: relative;
854 854 min-width: 100px;
855 855 max-width: 200px;
856 856 min-height: (@gravatar-size + @border-thickness * 2); // account for border
857 857 display: block;
858 858 padding: 0 0 0 (@gravatar-size + @basefontsize/4);
859 859
860 860
861 861 .gravatar {
862 862 display: block;
863 863 position: absolute;
864 864 top: 0;
865 865 left: 0;
866 866 min-width: @gravatar-size;
867 867 min-height: @gravatar-size;
868 868 margin: 0;
869 869 }
870 870
871 871 .user {
872 872 display: block;
873 873 max-width: 175px;
874 874 padding-top: 2px;
875 875 overflow: hidden;
876 876 text-overflow: ellipsis;
877 877 }
878 878 }
879 879
880 880 .gist-gravatar,
881 881 .journal_container {
882 882 .gravatar-large {
883 883 margin: 0 .5em -10px 0;
884 884 }
885 885 }
886 886
887 887 .gist-type-fields {
888 888 line-height: 30px;
889 889 height: 30px;
890 890
891 891 .gist-type-fields-wrapper {
892 892 vertical-align: middle;
893 893 display: inline-block;
894 894 line-height: 25px;
895 895 }
896 896 }
897 897
898 898 // ADMIN SETTINGS
899 899
900 900 // Tag Patterns
901 901 .tag_patterns {
902 902 .tag_input {
903 903 margin-bottom: @padding;
904 904 }
905 905 }
906 906
907 907 .locked_input {
908 908 position: relative;
909 909
910 910 input {
911 911 display: inline;
912 912 margin: 3px 5px 0px 0px;
913 913 }
914 914
915 915 br {
916 916 display: none;
917 917 }
918 918
919 919 .error-message {
920 920 float: left;
921 921 width: 100%;
922 922 }
923 923
924 924 .lock_input_button {
925 925 display: inline;
926 926 }
927 927
928 928 .help-block {
929 929 clear: both;
930 930 }
931 931 }
932 932
933 933 // Notifications
934 934
935 935 .notifications_buttons {
936 936 margin: 0 0 @space 0;
937 937 padding: 0;
938 938
939 939 .btn {
940 940 display: inline-block;
941 941 }
942 942 }
943 943
944 944 .notification-list {
945 945
946 946 div {
947 947 vertical-align: middle;
948 948 }
949 949
950 950 .container {
951 951 display: block;
952 952 margin: 0 0 @padding 0;
953 953 }
954 954
955 955 .delete-notifications {
956 956 margin-left: @padding;
957 957 text-align: right;
958 958 cursor: pointer;
959 959 }
960 960
961 961 .read-notifications {
962 962 margin-left: @padding/2;
963 963 text-align: right;
964 964 width: 35px;
965 965 cursor: pointer;
966 966 }
967 967
968 968 .icon-minus-sign {
969 969 color: @alert2;
970 970 }
971 971
972 972 .icon-ok-sign {
973 973 color: @alert1;
974 974 }
975 975 }
976 976
977 977 .user_settings {
978 978 float: left;
979 979 clear: both;
980 980 display: block;
981 981 width: 100%;
982 982
983 983 .gravatar_box {
984 984 margin-bottom: @padding;
985 985
986 986 &:after {
987 987 content: " ";
988 988 clear: both;
989 989 width: 100%;
990 990 }
991 991 }
992 992
993 993 .fields .field {
994 994 clear: both;
995 995 }
996 996 }
997 997
998 998 .advanced_settings {
999 999 margin-bottom: @space;
1000 1000
1001 1001 .help-block {
1002 1002 margin-left: 0;
1003 1003 }
1004 1004
1005 1005 button + .help-block {
1006 1006 margin-top: @padding;
1007 1007 }
1008 1008 }
1009 1009
1010 1010 // admin settings radio buttons and labels
1011 1011 .label-2 {
1012 1012 float: left;
1013 1013 width: @label2-width;
1014 1014
1015 1015 label {
1016 1016 color: @grey1;
1017 1017 }
1018 1018 }
1019 1019 .checkboxes {
1020 1020 float: left;
1021 1021 width: @checkboxes-width;
1022 1022 margin-bottom: @padding;
1023 1023
1024 1024 .checkbox {
1025 1025 width: 100%;
1026 1026
1027 1027 label {
1028 1028 margin: 0;
1029 1029 padding: 0;
1030 1030 }
1031 1031 }
1032 1032
1033 1033 .checkbox + .checkbox {
1034 1034 display: inline-block;
1035 1035 }
1036 1036
1037 1037 label {
1038 1038 margin-right: 1em;
1039 1039 }
1040 1040 }
1041 1041
1042 1042 // CHANGELOG
1043 1043 .container_header {
1044 1044 float: left;
1045 1045 display: block;
1046 1046 width: 100%;
1047 1047 margin: @padding 0 @padding;
1048 1048
1049 1049 #filter_changelog {
1050 1050 float: left;
1051 1051 margin-right: @padding;
1052 1052 }
1053 1053
1054 1054 .breadcrumbs_light {
1055 1055 display: inline-block;
1056 1056 }
1057 1057 }
1058 1058
1059 1059 .info_box {
1060 1060 float: right;
1061 1061 }
1062 1062
1063 1063
1064 1064
1065 1065 #graph_content{
1066 1066
1067 1067 // adjust for table headers so that graph renders properly
1068 1068 // #graph_nodes padding - table cell padding
1069 1069 padding-top: (@space - (@basefontsize * 2.4));
1070 1070
1071 1071 &.graph_full_width {
1072 1072 width: 100%;
1073 1073 max-width: 100%;
1074 1074 }
1075 1075 }
1076 1076
1077 1077 #graph {
1078 1078
1079 1079 .pagination-left {
1080 1080 float: left;
1081 1081 clear: both;
1082 1082 }
1083 1083
1084 1084 .log-container {
1085 1085 max-width: 345px;
1086 1086
1087 1087 .message{
1088 1088 max-width: 340px;
1089 1089 }
1090 1090 }
1091 1091
1092 1092 .graph-col-wrapper {
1093 1093
1094 1094 #graph_nodes {
1095 1095 width: 100px;
1096 1096 position: absolute;
1097 1097 left: 70px;
1098 1098 z-index: -1;
1099 1099 }
1100 1100 }
1101 1101
1102 1102 .load-more-commits {
1103 1103 text-align: center;
1104 1104 }
1105 1105 .load-more-commits:hover {
1106 1106 background-color: @grey7;
1107 1107 }
1108 1108 .load-more-commits {
1109 1109 a {
1110 1110 display: block;
1111 1111 }
1112 1112 }
1113 1113 }
1114 1114
1115 1115 .obsolete-toggle {
1116 1116 line-height: 30px;
1117 1117 margin-left: -15px;
1118 1118 }
1119 1119
1120 1120 #rev_range_container, #rev_range_clear, #rev_range_more {
1121 1121 margin-top: -5px;
1122 1122 margin-bottom: -5px;
1123 1123 }
1124 1124
1125 1125 #filter_changelog {
1126 1126 float: left;
1127 1127 }
1128 1128
1129 1129
1130 1130 //--- THEME ------------------//
1131 1131
1132 1132 #logo {
1133 1133 float: left;
1134 1134 margin: 9px 0 0 0;
1135 1135
1136 1136 .header {
1137 1137 background-color: transparent;
1138 1138 }
1139 1139
1140 1140 a {
1141 1141 display: inline-block;
1142 1142 }
1143 1143
1144 1144 img {
1145 1145 height:30px;
1146 1146 }
1147 1147 }
1148 1148
1149 1149 .logo-wrapper {
1150 1150 float:left;
1151 1151 }
1152 1152
1153 1153 .branding {
1154 1154 float: left;
1155 1155 padding: 9px 2px;
1156 1156 line-height: 1em;
1157 1157 font-size: @navigation-fontsize;
1158 1158
1159 1159 a {
1160 1160 color: @grey5
1161 1161 }
1162 1162 @media screen and (max-width: 1200px) {
1163 1163 display: none;
1164 1164 }
1165 1165 }
1166 1166
1167 1167 img {
1168 1168 border: none;
1169 1169 outline: none;
1170 1170 }
1171 1171 user-profile-header
1172 1172 label {
1173 1173
1174 1174 input[type="checkbox"] {
1175 1175 margin-right: 1em;
1176 1176 }
1177 1177 input[type="radio"] {
1178 1178 margin-right: 1em;
1179 1179 }
1180 1180 }
1181 1181
1182 1182 .review-status {
1183 1183 &.under_review {
1184 1184 color: @alert3;
1185 1185 }
1186 1186 &.approved {
1187 1187 color: @alert1;
1188 1188 }
1189 1189 &.rejected,
1190 1190 &.forced_closed{
1191 1191 color: @alert2;
1192 1192 }
1193 1193 &.not_reviewed {
1194 1194 color: @grey5;
1195 1195 }
1196 1196 }
1197 1197
1198 1198 .review-status-under_review {
1199 1199 color: @alert3;
1200 1200 }
1201 1201 .status-tag-under_review {
1202 1202 border-color: @alert3;
1203 1203 }
1204 1204
1205 1205 .review-status-approved {
1206 1206 color: @alert1;
1207 1207 }
1208 1208 .status-tag-approved {
1209 1209 border-color: @alert1;
1210 1210 }
1211 1211
1212 1212 .review-status-rejected,
1213 1213 .review-status-forced_closed {
1214 1214 color: @alert2;
1215 1215 }
1216 1216 .status-tag-rejected,
1217 1217 .status-tag-forced_closed {
1218 1218 border-color: @alert2;
1219 1219 }
1220 1220
1221 1221 .review-status-not_reviewed {
1222 1222 color: @grey5;
1223 1223 }
1224 1224 .status-tag-not_reviewed {
1225 1225 border-color: @grey5;
1226 1226 }
1227 1227
1228 1228 .test_pattern_preview {
1229 1229 margin: @space 0;
1230 1230
1231 1231 p {
1232 1232 margin-bottom: 0;
1233 1233 border-bottom: @border-thickness solid @border-default-color;
1234 1234 color: @grey3;
1235 1235 }
1236 1236
1237 1237 .btn {
1238 1238 margin-bottom: @padding;
1239 1239 }
1240 1240 }
1241 1241 #test_pattern_result {
1242 1242 display: none;
1243 1243 &:extend(pre);
1244 1244 padding: .9em;
1245 1245 color: @grey3;
1246 1246 background-color: @grey7;
1247 1247 border-right: @border-thickness solid @border-default-color;
1248 1248 border-bottom: @border-thickness solid @border-default-color;
1249 1249 border-left: @border-thickness solid @border-default-color;
1250 1250 }
1251 1251
1252 1252 #repo_vcs_settings {
1253 1253 #inherit_overlay_vcs_default {
1254 1254 display: none;
1255 1255 }
1256 1256 #inherit_overlay_vcs_custom {
1257 1257 display: custom;
1258 1258 }
1259 1259 &.inherited {
1260 1260 #inherit_overlay_vcs_default {
1261 1261 display: block;
1262 1262 }
1263 1263 #inherit_overlay_vcs_custom {
1264 1264 display: none;
1265 1265 }
1266 1266 }
1267 1267 }
1268 1268
1269 1269 .issue-tracker-link {
1270 1270 color: @rcblue;
1271 1271 }
1272 1272
1273 1273 // Issue Tracker Table Show/Hide
1274 1274 #repo_issue_tracker {
1275 1275 #inherit_overlay {
1276 1276 display: none;
1277 1277 }
1278 1278 #custom_overlay {
1279 1279 display: custom;
1280 1280 }
1281 1281 &.inherited {
1282 1282 #inherit_overlay {
1283 1283 display: block;
1284 1284 }
1285 1285 #custom_overlay {
1286 1286 display: none;
1287 1287 }
1288 1288 }
1289 1289 }
1290 1290 table.issuetracker {
1291 1291 &.readonly {
1292 1292 tr, td {
1293 1293 color: @grey3;
1294 1294 }
1295 1295 }
1296 1296 .edit {
1297 1297 display: none;
1298 1298 }
1299 1299 .editopen {
1300 1300 .edit {
1301 1301 display: inline;
1302 1302 }
1303 1303 .entry {
1304 1304 display: none;
1305 1305 }
1306 1306 }
1307 1307 tr td.td-action {
1308 1308 min-width: 117px;
1309 1309 }
1310 1310 td input {
1311 1311 max-width: none;
1312 1312 min-width: 30px;
1313 1313 width: 80%;
1314 1314 }
1315 1315 .issuetracker_pref input {
1316 1316 width: 40%;
1317 1317 }
1318 1318 input.edit_issuetracker_update {
1319 1319 margin-right: 0;
1320 1320 width: auto;
1321 1321 }
1322 1322 }
1323 1323
1324 1324 table.integrations {
1325 1325 .td-icon {
1326 1326 width: 20px;
1327 1327 .integration-icon {
1328 1328 height: 20px;
1329 1329 width: 20px;
1330 1330 }
1331 1331 }
1332 1332 }
1333 1333
1334 1334 .integrations {
1335 1335 a.integration-box {
1336 1336 color: @text-color;
1337 1337 &:hover {
1338 1338 .panel {
1339 1339 background: #fbfbfb;
1340 1340 }
1341 1341 }
1342 1342 .integration-icon {
1343 1343 width: 30px;
1344 1344 height: 30px;
1345 1345 margin-right: 20px;
1346 1346 float: left;
1347 1347 }
1348 1348
1349 1349 .panel-body {
1350 1350 padding: 10px;
1351 1351 }
1352 1352 .panel {
1353 1353 margin-bottom: 10px;
1354 1354 }
1355 1355 h2 {
1356 1356 display: inline-block;
1357 1357 margin: 0;
1358 1358 min-width: 140px;
1359 1359 }
1360 1360 }
1361 1361 a.integration-box.dummy-integration {
1362 1362 color: @grey4
1363 1363 }
1364 1364 }
1365 1365
1366 1366 //Permissions Settings
1367 1367 #add_perm {
1368 1368 margin: 0 0 @padding;
1369 1369 cursor: pointer;
1370 1370 }
1371 1371
1372 1372 .perm_ac {
1373 1373 input {
1374 1374 width: 95%;
1375 1375 }
1376 1376 }
1377 1377
1378 1378 .autocomplete-suggestions {
1379 1379 width: auto !important; // overrides autocomplete.js
1380 1380 min-width: 278px;
1381 1381 margin: 0;
1382 1382 border: @border-thickness solid @grey5;
1383 1383 border-radius: @border-radius;
1384 1384 color: @grey2;
1385 1385 background-color: white;
1386 1386 }
1387 1387
1388 1388 .autocomplete-qfilter-suggestions {
1389 1389 width: auto !important; // overrides autocomplete.js
1390 1390 max-height: 100% !important;
1391 1391 min-width: 376px;
1392 1392 margin: 0;
1393 1393 border: @border-thickness solid @grey5;
1394 1394 color: @grey2;
1395 1395 background-color: white;
1396 1396 }
1397 1397
1398 1398 .autocomplete-selected {
1399 1399 background: #F0F0F0;
1400 1400 }
1401 1401
1402 1402 .ac-container-wrap {
1403 1403 margin: 0;
1404 1404 padding: 8px;
1405 1405 border-bottom: @border-thickness solid @grey5;
1406 1406 list-style-type: none;
1407 1407 cursor: pointer;
1408 1408
1409 1409 &:hover {
1410 1410 background-color: @grey7;
1411 1411 }
1412 1412
1413 1413 img {
1414 1414 height: @gravatar-size;
1415 1415 width: @gravatar-size;
1416 1416 margin-right: 1em;
1417 1417 }
1418 1418
1419 1419 strong {
1420 1420 font-weight: normal;
1421 1421 }
1422 1422 }
1423 1423
1424 1424 // Settings Dropdown
1425 1425 .user-menu .container {
1426 1426 padding: 0 4px;
1427 1427 margin: 0;
1428 1428 }
1429 1429
1430 1430 .user-menu .gravatar {
1431 1431 cursor: pointer;
1432 1432 }
1433 1433
1434 1434 .codeblock {
1435 1435 margin-bottom: @padding;
1436 1436 clear: both;
1437 1437
1438 1438 .stats {
1439 1439 overflow: hidden;
1440 1440 }
1441 1441
1442 1442 .message{
1443 1443 textarea{
1444 1444 margin: 0;
1445 1445 }
1446 1446 }
1447 1447
1448 1448 .code-header {
1449 1449 .stats {
1450 1450 line-height: 2em;
1451 1451
1452 1452 .revision_id {
1453 1453 margin-left: 0;
1454 1454 }
1455 1455 .buttons {
1456 1456 padding-right: 0;
1457 1457 }
1458 1458 }
1459 1459
1460 1460 .item{
1461 1461 margin-right: 0.5em;
1462 1462 }
1463 1463 }
1464 1464
1465 1465 #editor_container {
1466 1466 position: relative;
1467 1467 margin: @padding 10px;
1468 1468 }
1469 1469 }
1470 1470
1471 1471 #file_history_container {
1472 1472 display: none;
1473 1473 }
1474 1474
1475 1475 .file-history-inner {
1476 1476 margin-bottom: 10px;
1477 1477 }
1478 1478
1479 1479 // Pull Requests
1480 1480 .summary-details {
1481 1481 width: 72%;
1482 1482 }
1483 1483 .pr-summary {
1484 1484 border-bottom: @border-thickness solid @grey5;
1485 1485 margin-bottom: @space;
1486 1486 }
1487 1487
1488 1488 .reviewers-title {
1489 1489 width: 25%;
1490 1490 min-width: 200px;
1491 1491
1492 1492 &.first-panel {
1493 1493 margin-top: 34px;
1494 1494 }
1495 1495 }
1496 1496
1497 1497 .reviewers {
1498 1498 width: 25%;
1499 1499 min-width: 200px;
1500 1500 }
1501 1501 .reviewers ul li {
1502 1502 position: relative;
1503 1503 width: 100%;
1504 1504 padding-bottom: 8px;
1505 1505 list-style-type: none;
1506 1506 }
1507 1507
1508 1508 .reviewer_entry {
1509 1509 min-height: 55px;
1510 1510 }
1511 1511
1512 1512 .reviewers_member {
1513 1513 width: 100%;
1514 1514 overflow: auto;
1515 1515 }
1516 1516 .reviewer_reason {
1517 1517 padding-left: 20px;
1518 1518 line-height: 1.5em;
1519 1519 }
1520 1520 .reviewer_status {
1521 1521 display: inline-block;
1522 1522 width: 25px;
1523 1523 min-width: 25px;
1524 1524 height: 1.2em;
1525 1525 line-height: 1em;
1526 1526 }
1527 1527
1528 1528 .reviewer_name {
1529 1529 display: inline-block;
1530 1530 max-width: 83%;
1531 1531 padding-right: 20px;
1532 1532 vertical-align: middle;
1533 1533 line-height: 1;
1534 1534
1535 1535 .rc-user {
1536 1536 min-width: 0;
1537 1537 margin: -2px 1em 0 0;
1538 1538 }
1539 1539
1540 1540 .reviewer {
1541 1541 float: left;
1542 1542 }
1543 1543 }
1544 1544
1545 1545 .reviewer_member_mandatory {
1546 1546 position: absolute;
1547 1547 left: 15px;
1548 1548 top: 8px;
1549 1549 width: 16px;
1550 1550 font-size: 11px;
1551 1551 margin: 0;
1552 1552 padding: 0;
1553 1553 color: black;
1554 1554 }
1555 1555
1556 1556 .reviewer_member_mandatory_remove,
1557 1557 .reviewer_member_remove {
1558 1558 position: absolute;
1559 1559 right: 0;
1560 1560 top: 0;
1561 1561 width: 16px;
1562 1562 margin-bottom: 10px;
1563 1563 padding: 0;
1564 1564 color: black;
1565 1565 }
1566 1566
1567 1567 .reviewer_member_mandatory_remove {
1568 1568 color: @grey4;
1569 1569 }
1570 1570
1571 1571 .reviewer_member_status {
1572 1572 margin-top: 5px;
1573 1573 }
1574 1574 .pr-summary #summary{
1575 1575 width: 100%;
1576 1576 }
1577 1577 .pr-summary .action_button:hover {
1578 1578 border: 0;
1579 1579 cursor: pointer;
1580 1580 }
1581 1581 .pr-details-title {
1582 1582 padding-bottom: 8px;
1583 1583 border-bottom: @border-thickness solid @grey5;
1584 1584
1585 1585 .action_button.disabled {
1586 1586 color: @grey4;
1587 1587 cursor: inherit;
1588 1588 }
1589 1589 .action_button {
1590 1590 color: @rcblue;
1591 1591 }
1592 1592 }
1593 1593 .pr-details-content {
1594 1594 margin-top: @textmargin - 5;
1595 1595 margin-bottom: @textmargin - 5;
1596 1596 }
1597 1597
1598 1598 .pr-reviewer-rules {
1599 1599 padding: 10px 0px 20px 0px;
1600 1600 }
1601 1601
1602 1602 .todo-resolved {
1603 1603 text-decoration: line-through;
1604 1604 }
1605 1605
1606 1606 .todo-table {
1607 1607 width: 100%;
1608 1608
1609 1609 td {
1610 1610 padding: 5px 0px;
1611 1611 }
1612 1612
1613 1613 .td-todo-number {
1614 1614 text-align: left;
1615 1615 white-space: nowrap;
1616 1616 width: 15%;
1617 1617 }
1618 1618
1619 1619 .td-todo-gravatar {
1620 1620 width: 5%;
1621 1621
1622 1622 img {
1623 1623 margin: -3px 0;
1624 1624 }
1625 1625 }
1626 1626
1627 1627 }
1628 1628
1629 1629 .todo-comment-text-wrapper {
1630 1630 display: inline-grid;
1631 1631 }
1632 1632
1633 1633 .todo-comment-text {
1634 1634 margin-left: 5px;
1635 1635 white-space: nowrap;
1636 1636 overflow: hidden;
1637 1637 text-overflow: ellipsis;
1638 1638 }
1639 1639
1640 1640 .group_members {
1641 1641 margin-top: 0;
1642 1642 padding: 0;
1643 1643 list-style: outside none none;
1644 1644
1645 1645 img {
1646 1646 height: @gravatar-size;
1647 1647 width: @gravatar-size;
1648 1648 margin-right: .5em;
1649 1649 margin-left: 3px;
1650 1650 }
1651 1651
1652 1652 .to-delete {
1653 1653 .user {
1654 1654 text-decoration: line-through;
1655 1655 }
1656 1656 }
1657 1657 }
1658 1658
1659 1659 .compare_view_commits_title {
1660 1660 .disabled {
1661 1661 cursor: inherit;
1662 1662 &:hover{
1663 1663 background-color: inherit;
1664 1664 color: inherit;
1665 1665 }
1666 1666 }
1667 1667 }
1668 1668
1669 1669 .subtitle-compare {
1670 1670 margin: -15px 0px 0px 0px;
1671 1671 }
1672 1672
1673 1673 // new entry in group_members
1674 1674 .td-author-new-entry {
1675 1675 background-color: rgba(red(@alert1), green(@alert1), blue(@alert1), 0.3);
1676 1676 }
1677 1677
1678 1678 .usergroup_member_remove {
1679 1679 width: 16px;
1680 1680 margin-bottom: 10px;
1681 1681 padding: 0;
1682 1682 color: black !important;
1683 1683 cursor: pointer;
1684 1684 }
1685 1685
1686 1686 .reviewer_ac .ac-input {
1687 1687 width: 92%;
1688 1688 margin-bottom: 1em;
1689 1689 }
1690 1690
1691 1691 .compare_view_commits tr{
1692 1692 height: 20px;
1693 1693 }
1694 1694 .compare_view_commits td {
1695 1695 vertical-align: top;
1696 1696 padding-top: 10px;
1697 1697 }
1698 1698 .compare_view_commits .author {
1699 1699 margin-left: 5px;
1700 1700 }
1701 1701
1702 1702 .compare_view_commits {
1703 1703 .color-a {
1704 1704 color: @alert1;
1705 1705 }
1706 1706
1707 1707 .color-c {
1708 1708 color: @color3;
1709 1709 }
1710 1710
1711 1711 .color-r {
1712 1712 color: @color5;
1713 1713 }
1714 1714
1715 1715 .color-a-bg {
1716 1716 background-color: @alert1;
1717 1717 }
1718 1718
1719 1719 .color-c-bg {
1720 1720 background-color: @alert3;
1721 1721 }
1722 1722
1723 1723 .color-r-bg {
1724 1724 background-color: @alert2;
1725 1725 }
1726 1726
1727 1727 .color-a-border {
1728 1728 border: 1px solid @alert1;
1729 1729 }
1730 1730
1731 1731 .color-c-border {
1732 1732 border: 1px solid @alert3;
1733 1733 }
1734 1734
1735 1735 .color-r-border {
1736 1736 border: 1px solid @alert2;
1737 1737 }
1738 1738
1739 1739 .commit-change-indicator {
1740 1740 width: 15px;
1741 1741 height: 15px;
1742 1742 position: relative;
1743 1743 left: 15px;
1744 1744 }
1745 1745
1746 1746 .commit-change-content {
1747 1747 text-align: center;
1748 1748 vertical-align: middle;
1749 1749 line-height: 15px;
1750 1750 }
1751 1751 }
1752 1752
1753 1753 .compare_view_filepath {
1754 1754 color: @grey1;
1755 1755 }
1756 1756
1757 1757 .show_more {
1758 1758 display: inline-block;
1759 1759 width: 0;
1760 1760 height: 0;
1761 1761 vertical-align: middle;
1762 1762 content: "";
1763 1763 border: 4px solid;
1764 1764 border-right-color: transparent;
1765 1765 border-bottom-color: transparent;
1766 1766 border-left-color: transparent;
1767 1767 font-size: 0;
1768 1768 }
1769 1769
1770 1770 .journal_more .show_more {
1771 1771 display: inline;
1772 1772
1773 1773 &:after {
1774 1774 content: none;
1775 1775 }
1776 1776 }
1777 1777
1778 1778 .compare_view_commits .collapse_commit:after {
1779 1779 cursor: pointer;
1780 1780 content: "\00A0\25B4";
1781 1781 margin-left: -3px;
1782 1782 font-size: 17px;
1783 1783 color: @grey4;
1784 1784 }
1785 1785
1786 1786 .diff_links {
1787 1787 margin-left: 8px;
1788 1788 }
1789 1789
1790 1790 #pull_request_overview {
1791 1791 div.ancestor {
1792 1792 margin: -33px 0;
1793 1793 }
1794 1794 }
1795 1795
1796 1796 div.ancestor {
1797 1797 line-height: 33px;
1798 1798 }
1799 1799
1800 1800 .cs_icon_td input[type="checkbox"] {
1801 1801 display: none;
1802 1802 }
1803 1803
1804 1804 .cs_icon_td .expand_file_icon:after {
1805 1805 cursor: pointer;
1806 1806 content: "\00A0\25B6";
1807 1807 font-size: 12px;
1808 1808 color: @grey4;
1809 1809 }
1810 1810
1811 1811 .cs_icon_td .collapse_file_icon:after {
1812 1812 cursor: pointer;
1813 1813 content: "\00A0\25BC";
1814 1814 font-size: 12px;
1815 1815 color: @grey4;
1816 1816 }
1817 1817
1818 1818 /*new binary
1819 1819 NEW_FILENODE = 1
1820 1820 DEL_FILENODE = 2
1821 1821 MOD_FILENODE = 3
1822 1822 RENAMED_FILENODE = 4
1823 1823 COPIED_FILENODE = 5
1824 1824 CHMOD_FILENODE = 6
1825 1825 BIN_FILENODE = 7
1826 1826 */
1827 1827 .cs_files_expand {
1828 1828 font-size: @basefontsize + 5px;
1829 1829 line-height: 1.8em;
1830 1830 float: right;
1831 1831 }
1832 1832
1833 1833 .cs_files_expand span{
1834 1834 color: @rcblue;
1835 1835 cursor: pointer;
1836 1836 }
1837 1837 .cs_files {
1838 1838 clear: both;
1839 1839 padding-bottom: @padding;
1840 1840
1841 1841 .cur_cs {
1842 1842 margin: 10px 2px;
1843 1843 font-weight: bold;
1844 1844 }
1845 1845
1846 1846 .node {
1847 1847 float: left;
1848 1848 }
1849 1849
1850 1850 .changes {
1851 1851 float: right;
1852 1852 color: white;
1853 1853 font-size: @basefontsize - 4px;
1854 1854 margin-top: 4px;
1855 1855 opacity: 0.6;
1856 1856 filter: Alpha(opacity=60); /* IE8 and earlier */
1857 1857
1858 1858 .added {
1859 1859 background-color: @alert1;
1860 1860 float: left;
1861 1861 text-align: center;
1862 1862 }
1863 1863
1864 1864 .deleted {
1865 1865 background-color: @alert2;
1866 1866 float: left;
1867 1867 text-align: center;
1868 1868 }
1869 1869
1870 1870 .bin {
1871 1871 background-color: @alert1;
1872 1872 text-align: center;
1873 1873 }
1874 1874
1875 1875 /*new binary*/
1876 1876 .bin.bin1 {
1877 1877 background-color: @alert1;
1878 1878 text-align: center;
1879 1879 }
1880 1880
1881 1881 /*deleted binary*/
1882 1882 .bin.bin2 {
1883 1883 background-color: @alert2;
1884 1884 text-align: center;
1885 1885 }
1886 1886
1887 1887 /*mod binary*/
1888 1888 .bin.bin3 {
1889 1889 background-color: @grey2;
1890 1890 text-align: center;
1891 1891 }
1892 1892
1893 1893 /*rename file*/
1894 1894 .bin.bin4 {
1895 1895 background-color: @alert4;
1896 1896 text-align: center;
1897 1897 }
1898 1898
1899 1899 /*copied file*/
1900 1900 .bin.bin5 {
1901 1901 background-color: @alert4;
1902 1902 text-align: center;
1903 1903 }
1904 1904
1905 1905 /*chmod file*/
1906 1906 .bin.bin6 {
1907 1907 background-color: @grey2;
1908 1908 text-align: center;
1909 1909 }
1910 1910 }
1911 1911 }
1912 1912
1913 1913 .cs_files .cs_added, .cs_files .cs_A,
1914 1914 .cs_files .cs_added, .cs_files .cs_M,
1915 1915 .cs_files .cs_added, .cs_files .cs_D {
1916 1916 height: 16px;
1917 1917 padding-right: 10px;
1918 1918 margin-top: 7px;
1919 1919 text-align: left;
1920 1920 }
1921 1921
1922 1922 .cs_icon_td {
1923 1923 min-width: 16px;
1924 1924 width: 16px;
1925 1925 }
1926 1926
1927 1927 .pull-request-merge {
1928 1928 border: 1px solid @grey5;
1929 1929 padding: 10px 0px 20px;
1930 1930 margin-top: 10px;
1931 1931 margin-bottom: 20px;
1932 1932 }
1933 1933
1934 1934 .pull-request-merge-refresh {
1935 1935 margin: 2px 7px;
1936 1936 a {
1937 1937 color: @grey3;
1938 1938 }
1939 1939 }
1940 1940
1941 1941 .pull-request-merge ul {
1942 1942 padding: 0px 0px;
1943 1943 }
1944 1944
1945 1945 .pull-request-merge li {
1946 1946 list-style-type: none;
1947 1947 }
1948 1948
1949 1949 .pull-request-merge .pull-request-wrap {
1950 1950 height: auto;
1951 1951 padding: 0px 0px;
1952 1952 text-align: right;
1953 1953 }
1954 1954
1955 1955 .pull-request-merge span {
1956 1956 margin-right: 5px;
1957 1957 }
1958 1958
1959 1959 .pull-request-merge-actions {
1960 1960 min-height: 30px;
1961 1961 padding: 0px 0px;
1962 1962 }
1963 1963
1964 1964 .pull-request-merge-info {
1965 1965 padding: 0px 5px 5px 0px;
1966 1966 }
1967 1967
1968 1968 .merge-status {
1969 1969 margin-right: 5px;
1970 1970 }
1971 1971
1972 1972 .merge-message {
1973 1973 font-size: 1.2em
1974 1974 }
1975 1975
1976 1976 .merge-message.success i,
1977 1977 .merge-icon.success i {
1978 1978 color:@alert1;
1979 1979 }
1980 1980
1981 1981 .merge-message.warning i,
1982 1982 .merge-icon.warning i {
1983 1983 color: @alert3;
1984 1984 }
1985 1985
1986 1986 .merge-message.error i,
1987 1987 .merge-icon.error i {
1988 1988 color:@alert2;
1989 1989 }
1990 1990
1991 1991 .pr-versions {
1992 1992 font-size: 1.1em;
1993 1993 padding: 7.5px;
1994 1994
1995 1995 table {
1996 1996
1997 1997 }
1998 1998
1999 1999 td {
2000 2000 line-height: 15px;
2001 2001 }
2002 2002
2003 2003 .compare-radio-button {
2004 2004 position: relative;
2005 2005 top: -3px;
2006 2006 }
2007 2007 }
2008 2008
2009 2009
2010 2010 #close_pull_request {
2011 2011 margin-right: 0px;
2012 2012 }
2013 2013
2014 2014 .empty_data {
2015 2015 color: @grey4;
2016 2016 }
2017 2017
2018 2018 #changeset_compare_view_content {
2019 2019 clear: both;
2020 2020 width: 100%;
2021 2021 box-sizing: border-box;
2022 2022 .border-radius(@border-radius);
2023 2023
2024 2024 .help-block {
2025 2025 margin: @padding 0;
2026 2026 color: @text-color;
2027 2027 &.pre-formatting {
2028 2028 white-space: pre;
2029 2029 }
2030 2030 }
2031 2031
2032 2032 .empty_data {
2033 2033 margin: @padding 0;
2034 2034 }
2035 2035
2036 2036 .alert {
2037 2037 margin-bottom: @space;
2038 2038 }
2039 2039 }
2040 2040
2041 2041 .table_disp {
2042 2042 .status {
2043 2043 width: auto;
2044 2044 }
2045 2045 }
2046 2046
2047 2047
2048 2048 .creation_in_progress {
2049 2049 color: @grey4
2050 2050 }
2051 2051
2052 2052 .status_box_menu {
2053 2053 margin: 0;
2054 2054 }
2055 2055
2056 2056 .notification-table{
2057 2057 margin-bottom: @space;
2058 2058 display: table;
2059 2059 width: 100%;
2060 2060
2061 2061 .container{
2062 2062 display: table-row;
2063 2063
2064 2064 .notification-header{
2065 2065 border-bottom: @border-thickness solid @border-default-color;
2066 2066 }
2067 2067
2068 2068 .notification-subject{
2069 2069 display: table-cell;
2070 2070 }
2071 2071 }
2072 2072 }
2073 2073
2074 2074 // Notifications
2075 2075 .notification-header{
2076 2076 display: table;
2077 2077 width: 100%;
2078 2078 padding: floor(@basefontsize/2) 0;
2079 2079 line-height: 1em;
2080 2080
2081 2081 .desc, .delete-notifications, .read-notifications{
2082 2082 display: table-cell;
2083 2083 text-align: left;
2084 2084 }
2085 2085
2086 2086 .delete-notifications, .read-notifications{
2087 2087 width: 35px;
2088 2088 min-width: 35px; //fixes when only one button is displayed
2089 2089 }
2090 2090 }
2091 2091
2092 2092 .notification-body {
2093 2093 .markdown-block,
2094 2094 .rst-block {
2095 2095 padding: @padding 0;
2096 2096 }
2097 2097
2098 2098 .notification-subject {
2099 2099 padding: @textmargin 0;
2100 2100 border-bottom: @border-thickness solid @border-default-color;
2101 2101 }
2102 2102 }
2103 2103
2104 2104 .notice-messages {
2105 2105 .markdown-block,
2106 2106 .rst-block {
2107 2107 padding: 0;
2108 2108 }
2109 2109 }
2110 2110
2111 2111 .notifications_buttons{
2112 2112 float: right;
2113 2113 }
2114 2114
2115 2115 #notification-status{
2116 2116 display: inline;
2117 2117 }
2118 2118
2119 2119 // Repositories
2120 2120
2121 2121 #summary.fields{
2122 2122 display: table;
2123 2123
2124 2124 .field{
2125 2125 display: table-row;
2126 2126
2127 2127 .label-summary{
2128 2128 display: table-cell;
2129 2129 min-width: @label-summary-minwidth;
2130 2130 padding-top: @padding/2;
2131 2131 padding-bottom: @padding/2;
2132 2132 padding-right: @padding/2;
2133 2133 }
2134 2134
2135 2135 .input{
2136 2136 display: table-cell;
2137 2137 padding: @padding/2;
2138 2138
2139 2139 input{
2140 2140 min-width: 29em;
2141 2141 padding: @padding/4;
2142 2142 }
2143 2143 }
2144 2144 .statistics, .downloads{
2145 2145 .disabled{
2146 2146 color: @grey4;
2147 2147 }
2148 2148 }
2149 2149 }
2150 2150 }
2151 2151
2152 2152 #summary{
2153 2153 width: 70%;
2154 2154 }
2155 2155
2156 2156
2157 2157 // Journal
2158 2158 .journal.title {
2159 2159 h5 {
2160 2160 float: left;
2161 2161 margin: 0;
2162 2162 width: 70%;
2163 2163 }
2164 2164
2165 2165 ul {
2166 2166 float: right;
2167 2167 display: inline-block;
2168 2168 margin: 0;
2169 2169 width: 30%;
2170 2170 text-align: right;
2171 2171
2172 2172 li {
2173 2173 display: inline;
2174 2174 font-size: @journal-fontsize;
2175 2175 line-height: 1em;
2176 2176
2177 2177 list-style-type: none;
2178 2178 }
2179 2179 }
2180 2180 }
2181 2181
2182 2182 .filterexample {
2183 2183 position: absolute;
2184 2184 top: 95px;
2185 2185 left: @contentpadding;
2186 2186 color: @rcblue;
2187 2187 font-size: 11px;
2188 2188 font-family: @text-regular;
2189 2189 cursor: help;
2190 2190
2191 2191 &:hover {
2192 2192 color: @rcdarkblue;
2193 2193 }
2194 2194
2195 2195 @media (max-width:768px) {
2196 2196 position: relative;
2197 2197 top: auto;
2198 2198 left: auto;
2199 2199 display: block;
2200 2200 }
2201 2201 }
2202 2202
2203 2203
2204 2204 #journal{
2205 2205 margin-bottom: @space;
2206 2206
2207 2207 .journal_day{
2208 2208 margin-bottom: @textmargin/2;
2209 2209 padding-bottom: @textmargin/2;
2210 2210 font-size: @journal-fontsize;
2211 2211 border-bottom: @border-thickness solid @border-default-color;
2212 2212 }
2213 2213
2214 2214 .journal_container{
2215 2215 margin-bottom: @space;
2216 2216
2217 2217 .journal_user{
2218 2218 display: inline-block;
2219 2219 }
2220 2220 .journal_action_container{
2221 2221 display: block;
2222 2222 margin-top: @textmargin;
2223 2223
2224 2224 div{
2225 2225 display: inline;
2226 2226 }
2227 2227
2228 2228 div.journal_action_params{
2229 2229 display: block;
2230 2230 }
2231 2231
2232 2232 div.journal_repo:after{
2233 2233 content: "\A";
2234 2234 white-space: pre;
2235 2235 }
2236 2236
2237 2237 div.date{
2238 2238 display: block;
2239 2239 margin-bottom: @textmargin;
2240 2240 }
2241 2241 }
2242 2242 }
2243 2243 }
2244 2244
2245 2245 // Files
2246 2246 .edit-file-title {
2247 2247 font-size: 16px;
2248 2248
2249 2249 .title-heading {
2250 2250 padding: 2px;
2251 2251 }
2252 2252 }
2253 2253
2254 2254 .edit-file-fieldset {
2255 2255 margin: @sidebarpadding 0;
2256 2256
2257 2257 .fieldset {
2258 2258 .left-label {
2259 2259 width: 13%;
2260 2260 }
2261 2261 .right-content {
2262 2262 width: 87%;
2263 2263 max-width: 100%;
2264 2264 }
2265 2265 .filename-label {
2266 2266 margin-top: 13px;
2267 2267 }
2268 2268 .commit-message-label {
2269 2269 margin-top: 4px;
2270 2270 }
2271 2271 .file-upload-input {
2272 2272 input {
2273 2273 display: none;
2274 2274 }
2275 2275 margin-top: 10px;
2276 2276 }
2277 2277 .file-upload-label {
2278 2278 margin-top: 10px;
2279 2279 }
2280 2280 p {
2281 2281 margin-top: 5px;
2282 2282 }
2283 2283
2284 2284 }
2285 2285 .custom-path-link {
2286 2286 margin-left: 5px;
2287 2287 }
2288 2288 #commit {
2289 2289 resize: vertical;
2290 2290 }
2291 2291 }
2292 2292
2293 2293 .delete-file-preview {
2294 2294 max-height: 250px;
2295 2295 }
2296 2296
2297 2297 .new-file,
2298 2298 #filter_activate,
2299 2299 #filter_deactivate {
2300 2300 float: right;
2301 2301 margin: 0 0 0 10px;
2302 2302 }
2303 2303
2304 2304 .file-upload-transaction-wrapper {
2305 2305 margin-top: 57px;
2306 2306 clear: both;
2307 2307 }
2308 2308
2309 2309 .file-upload-transaction-wrapper .error {
2310 2310 color: @color5;
2311 2311 }
2312 2312
2313 2313 .file-upload-transaction {
2314 2314 min-height: 200px;
2315 2315 padding: 54px;
2316 2316 border: 1px solid @grey5;
2317 2317 text-align: center;
2318 2318 clear: both;
2319 2319 }
2320 2320
2321 2321 .file-upload-transaction i {
2322 2322 font-size: 48px
2323 2323 }
2324 2324
2325 2325 h3.files_location{
2326 2326 line-height: 2.4em;
2327 2327 }
2328 2328
2329 2329 .browser-nav {
2330 2330 width: 100%;
2331 2331 display: table;
2332 2332 margin-bottom: 20px;
2333 2333
2334 2334 .info_box {
2335 2335 float: left;
2336 2336 display: inline-table;
2337 2337 height: 2.5em;
2338 2338
2339 2339 .browser-cur-rev, .info_box_elem {
2340 2340 display: table-cell;
2341 2341 vertical-align: middle;
2342 2342 }
2343 2343
2344 2344 .drop-menu {
2345 2345 margin: 0 10px;
2346 2346 }
2347 2347
2348 2348 .info_box_elem {
2349 2349 border-top: @border-thickness solid @grey5;
2350 2350 border-bottom: @border-thickness solid @grey5;
2351 2351 box-shadow: @button-shadow;
2352 2352
2353 2353 #at_rev, a {
2354 2354 padding: 0.6em 0.4em;
2355 2355 margin: 0;
2356 2356 .box-shadow(none);
2357 2357 border: 0;
2358 2358 height: 12px;
2359 2359 color: @grey2;
2360 2360 }
2361 2361
2362 2362 input#at_rev {
2363 2363 max-width: 50px;
2364 2364 text-align: center;
2365 2365 }
2366 2366
2367 2367 &.previous {
2368 2368 border: @border-thickness solid @grey5;
2369 2369 border-top-left-radius: @border-radius;
2370 2370 border-bottom-left-radius: @border-radius;
2371 2371
2372 2372 &:hover {
2373 2373 border-color: @grey4;
2374 2374 }
2375 2375
2376 2376 .disabled {
2377 2377 color: @grey5;
2378 2378 cursor: not-allowed;
2379 2379 opacity: 0.5;
2380 2380 }
2381 2381 }
2382 2382
2383 2383 &.next {
2384 2384 border: @border-thickness solid @grey5;
2385 2385 border-top-right-radius: @border-radius;
2386 2386 border-bottom-right-radius: @border-radius;
2387 2387
2388 2388 &:hover {
2389 2389 border-color: @grey4;
2390 2390 }
2391 2391
2392 2392 .disabled {
2393 2393 color: @grey5;
2394 2394 cursor: not-allowed;
2395 2395 opacity: 0.5;
2396 2396 }
2397 2397 }
2398 2398 }
2399 2399
2400 2400 .browser-cur-rev {
2401 2401
2402 2402 span{
2403 2403 margin: 0;
2404 2404 color: @rcblue;
2405 2405 height: 12px;
2406 2406 display: inline-block;
2407 2407 padding: 0.7em 1em ;
2408 2408 border: @border-thickness solid @rcblue;
2409 2409 margin-right: @padding;
2410 2410 }
2411 2411 }
2412 2412
2413 2413 }
2414 2414
2415 2415 .select-index-number {
2416 2416 margin: 0 0 0 20px;
2417 2417 color: @grey3;
2418 2418 }
2419 2419
2420 2420 .search_activate {
2421 2421 display: table-cell;
2422 2422 vertical-align: middle;
2423 2423
2424 2424 input, label{
2425 2425 margin: 0;
2426 2426 padding: 0;
2427 2427 }
2428 2428
2429 2429 input{
2430 2430 margin-left: @textmargin;
2431 2431 }
2432 2432
2433 2433 }
2434 2434 }
2435 2435
2436 2436 .browser-cur-rev{
2437 2437 margin-bottom: @textmargin;
2438 2438 }
2439 2439
2440 2440 #node_filter_box_loading{
2441 2441 .info_text;
2442 2442 }
2443 2443
2444 2444 .browser-search {
2445 2445 margin: -25px 0px 5px 0px;
2446 2446 }
2447 2447
2448 2448 .files-quick-filter {
2449 2449 float: right;
2450 2450 width: 180px;
2451 2451 position: relative;
2452 2452 }
2453 2453
2454 2454 .files-filter-box {
2455 2455 display: flex;
2456 2456 padding: 0px;
2457 2457 border-radius: 3px;
2458 2458 margin-bottom: 0;
2459 2459
2460 2460 a {
2461 2461 border: none !important;
2462 2462 }
2463 2463
2464 2464 li {
2465 2465 list-style-type: none
2466 2466 }
2467 2467 }
2468 2468
2469 2469 .files-filter-box-path {
2470 2470 line-height: 33px;
2471 2471 padding: 0;
2472 2472 width: 20px;
2473 2473 position: absolute;
2474 2474 z-index: 11;
2475 2475 left: 5px;
2476 2476 }
2477 2477
2478 2478 .files-filter-box-input {
2479 2479 margin-right: 0;
2480 2480
2481 2481 input {
2482 2482 border: 1px solid @white;
2483 2483 padding-left: 25px;
2484 2484 width: 145px;
2485 2485
2486 2486 &:hover {
2487 2487 border-color: @grey6;
2488 2488 }
2489 2489
2490 2490 &:focus {
2491 2491 border-color: @grey5;
2492 2492 }
2493 2493 }
2494 2494 }
2495 2495
2496 2496 .browser-result{
2497 2497 td a{
2498 2498 margin-left: 0.5em;
2499 2499 display: inline-block;
2500 2500
2501 2501 em {
2502 2502 font-weight: @text-bold-weight;
2503 2503 font-family: @text-bold;
2504 2504 }
2505 2505 }
2506 2506 }
2507 2507
2508 2508 .browser-highlight{
2509 2509 background-color: @grey5-alpha;
2510 2510 }
2511 2511
2512 2512
2513 2513 .edit-file-fieldset #location,
2514 2514 .edit-file-fieldset #filename {
2515 2515 display: flex;
2516 2516 width: -moz-available; /* WebKit-based browsers will ignore this. */
2517 2517 width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
2518 2518 width: fill-available;
2519 2519 border: 0;
2520 2520 }
2521 2521
2522 2522 .path-items {
2523 2523 display: flex;
2524 2524 padding: 0;
2525 2525 border: 1px solid #eeeeee;
2526 2526 width: 100%;
2527 2527 float: left;
2528 2528
2529 2529 .breadcrumb-path {
2530 2530 line-height: 30px;
2531 2531 padding: 0 4px;
2532 2532 white-space: nowrap;
2533 2533 }
2534 2534
2535 .upload-form {
2536 margin-top: 46px;
2537 }
2538
2535 2539 .location-path {
2536 2540 width: -moz-available; /* WebKit-based browsers will ignore this. */
2537 2541 width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
2538 2542 width: fill-available;
2539 2543
2540 2544 .file-name-input {
2541 2545 padding: 0.5em 0;
2542 2546 }
2543 2547
2544 2548 }
2545 2549
2546 2550 ul {
2547 2551 display: flex;
2548 2552 margin: 0;
2549 2553 padding: 0;
2550 2554 width: 100%;
2551 2555 }
2552 2556
2553 2557 li {
2554 2558 list-style-type: none;
2555 2559 }
2556 2560
2557 2561 }
2558 2562
2559 2563 .editor-items {
2560 2564 height: 40px;
2561 2565 margin: 10px 0 -17px 10px;
2562 2566
2563 2567 .editor-action {
2564 2568 cursor: pointer;
2565 2569 }
2566 2570
2567 2571 .editor-action.active {
2568 2572 border-bottom: 2px solid #5C5C5C;
2569 2573 }
2570 2574
2571 2575 li {
2572 2576 list-style-type: none;
2573 2577 }
2574 2578 }
2575 2579
2576 2580 .edit-file-fieldset .message textarea {
2577 2581 border: 1px solid #eeeeee;
2578 2582 }
2579 2583
2580 2584 #files_data .codeblock {
2581 2585 background-color: #F5F5F5;
2582 2586 }
2583 2587
2584 2588 #editor_preview {
2585 2589 background: white;
2586 2590 }
2587 2591
2588 2592 .show-editor {
2589 2593 padding: 10px;
2590 2594 background-color: white;
2591 2595
2592 2596 }
2593 2597
2594 2598 .show-preview {
2595 2599 padding: 10px;
2596 2600 background-color: white;
2597 2601 border-left: 1px solid #eeeeee;
2598 2602 }
2599 2603 // quick filter
2600 2604 .grid-quick-filter {
2601 2605 float: right;
2602 2606 position: relative;
2603 2607 }
2604 2608
2605 2609 .grid-filter-box {
2606 2610 display: flex;
2607 2611 padding: 0px;
2608 2612 border-radius: 3px;
2609 2613 margin-bottom: 0;
2610 2614
2611 2615 a {
2612 2616 border: none !important;
2613 2617 }
2614 2618
2615 2619 li {
2616 2620 list-style-type: none
2617 2621 }
2618 2622 }
2619 2623
2620 2624 .grid-filter-box-icon {
2621 2625 line-height: 33px;
2622 2626 padding: 0;
2623 2627 width: 20px;
2624 2628 position: absolute;
2625 2629 z-index: 11;
2626 2630 left: 5px;
2627 2631 }
2628 2632
2629 2633 .grid-filter-box-input {
2630 2634 margin-right: 0;
2631 2635
2632 2636 input {
2633 2637 border: 1px solid @white;
2634 2638 padding-left: 25px;
2635 2639 width: 145px;
2636 2640
2637 2641 &:hover {
2638 2642 border-color: @grey6;
2639 2643 }
2640 2644
2641 2645 &:focus {
2642 2646 border-color: @grey5;
2643 2647 }
2644 2648 }
2645 2649 }
2646 2650
2647 2651
2648 2652
2649 2653 // Search
2650 2654
2651 2655 .search-form{
2652 2656 #q {
2653 2657 width: @search-form-width;
2654 2658 }
2655 2659 .fields{
2656 2660 margin: 0 0 @space;
2657 2661 }
2658 2662
2659 2663 label{
2660 2664 display: inline-block;
2661 2665 margin-right: @textmargin;
2662 2666 padding-top: 0.25em;
2663 2667 }
2664 2668
2665 2669
2666 2670 .results{
2667 2671 clear: both;
2668 2672 margin: 0 0 @padding;
2669 2673 }
2670 2674
2671 2675 .search-tags {
2672 2676 padding: 5px 0;
2673 2677 }
2674 2678 }
2675 2679
2676 2680 div.search-feedback-items {
2677 2681 display: inline-block;
2678 2682 }
2679 2683
2680 2684 div.search-code-body {
2681 2685 background-color: #ffffff; padding: 5px 0 5px 10px;
2682 2686 pre {
2683 2687 .match { background-color: #faffa6;}
2684 2688 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
2685 2689 }
2686 2690 }
2687 2691
2688 2692 .expand_commit.search {
2689 2693 .show_more.open {
2690 2694 height: auto;
2691 2695 max-height: none;
2692 2696 }
2693 2697 }
2694 2698
2695 2699 .search-results {
2696 2700
2697 2701 h2 {
2698 2702 margin-bottom: 0;
2699 2703 }
2700 2704 .codeblock {
2701 2705 border: none;
2702 2706 background: transparent;
2703 2707 }
2704 2708
2705 2709 .codeblock-header {
2706 2710 border: none;
2707 2711 background: transparent;
2708 2712 }
2709 2713
2710 2714 .code-body {
2711 2715 border: @border-thickness solid @grey6;
2712 2716 .border-radius(@border-radius);
2713 2717 }
2714 2718
2715 2719 .td-commit {
2716 2720 &:extend(pre);
2717 2721 border-bottom: @border-thickness solid @border-default-color;
2718 2722 }
2719 2723
2720 2724 .message {
2721 2725 height: auto;
2722 2726 max-width: 350px;
2723 2727 white-space: normal;
2724 2728 text-overflow: initial;
2725 2729 overflow: visible;
2726 2730
2727 2731 .match { background-color: #faffa6;}
2728 2732 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
2729 2733 }
2730 2734
2731 2735 .path {
2732 2736 border-bottom: none !important;
2733 2737 border-left: 1px solid @grey6 !important;
2734 2738 border-right: 1px solid @grey6 !important;
2735 2739 }
2736 2740 }
2737 2741
2738 2742 table.rctable td.td-search-results div {
2739 2743 max-width: 100%;
2740 2744 }
2741 2745
2742 2746 #tip-box, .tip-box{
2743 2747 padding: @menupadding/2;
2744 2748 display: block;
2745 2749 border: @border-thickness solid @border-highlight-color;
2746 2750 .border-radius(@border-radius);
2747 2751 background-color: white;
2748 2752 z-index: 99;
2749 2753 white-space: pre-wrap;
2750 2754 }
2751 2755
2752 2756 #linktt {
2753 2757 width: 79px;
2754 2758 }
2755 2759
2756 2760 #help_kb .modal-content{
2757 2761 max-width: 750px;
2758 2762 margin: 10% auto;
2759 2763
2760 2764 table{
2761 2765 td,th{
2762 2766 border-bottom: none;
2763 2767 line-height: 2.5em;
2764 2768 }
2765 2769 th{
2766 2770 padding-bottom: @textmargin/2;
2767 2771 }
2768 2772 td.keys{
2769 2773 text-align: center;
2770 2774 }
2771 2775 }
2772 2776
2773 2777 .block-left{
2774 2778 width: 45%;
2775 2779 margin-right: 5%;
2776 2780 }
2777 2781 .modal-footer{
2778 2782 clear: both;
2779 2783 }
2780 2784 .key.tag{
2781 2785 padding: 0.5em;
2782 2786 background-color: @rcblue;
2783 2787 color: white;
2784 2788 border-color: @rcblue;
2785 2789 .box-shadow(none);
2786 2790 }
2787 2791 }
2788 2792
2789 2793
2790 2794
2791 2795 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2792 2796
2793 2797 @import 'statistics-graph';
2794 2798 @import 'tables';
2795 2799 @import 'forms';
2796 2800 @import 'diff';
2797 2801 @import 'summary';
2798 2802 @import 'navigation';
2799 2803
2800 2804 //--- SHOW/HIDE SECTIONS --//
2801 2805
2802 2806 .btn-collapse {
2803 2807 float: right;
2804 2808 text-align: right;
2805 2809 font-family: @text-light;
2806 2810 font-size: @basefontsize;
2807 2811 cursor: pointer;
2808 2812 border: none;
2809 2813 color: @rcblue;
2810 2814 }
2811 2815
2812 2816 table.rctable,
2813 2817 table.dataTable {
2814 2818 .btn-collapse {
2815 2819 float: right;
2816 2820 text-align: right;
2817 2821 }
2818 2822 }
2819 2823
2820 2824 table.rctable {
2821 2825 &.permissions {
2822 2826
2823 2827 th.td-owner {
2824 2828 padding: 0;
2825 2829 }
2826 2830
2827 2831 th {
2828 2832 font-weight: normal;
2829 2833 padding: 0 5px;
2830 2834 }
2831 2835
2832 2836 }
2833 2837 }
2834 2838
2835 2839
2836 2840 // TODO: johbo: Fix for IE10, this avoids that we see a border
2837 2841 // and padding around checkboxes and radio boxes. Move to the right place,
2838 2842 // or better: Remove this once we did the form refactoring.
2839 2843 input[type=checkbox],
2840 2844 input[type=radio] {
2841 2845 padding: 0;
2842 2846 border: none;
2843 2847 }
2844 2848
2845 2849 .toggle-ajax-spinner{
2846 2850 height: 16px;
2847 2851 width: 16px;
2848 2852 }
2849 2853
2850 2854
2851 2855 .markup-form .clearfix {
2852 2856 .border-radius(@border-radius);
2853 2857 margin: 0px;
2854 2858 }
2855 2859
2856 2860 .markup-form-area {
2857 2861 padding: 8px 12px;
2858 2862 border: 1px solid @grey4;
2859 2863 .border-radius(@border-radius);
2860 2864 }
2861 2865
2862 2866 .markup-form-area-header .nav-links {
2863 2867 display: flex;
2864 2868 flex-flow: row wrap;
2865 2869 -webkit-flex-flow: row wrap;
2866 2870 width: 100%;
2867 2871 }
2868 2872
2869 2873 .markup-form-area-footer {
2870 2874 display: flex;
2871 2875 }
2872 2876
2873 2877 .markup-form-area-footer .toolbar {
2874 2878
2875 2879 }
2876 2880
2877 2881 // markup Form
2878 2882 div.markup-form {
2879 2883 margin-top: 20px;
2880 2884 }
2881 2885
2882 2886 .markup-form strong {
2883 2887 display: block;
2884 2888 margin-bottom: 15px;
2885 2889 }
2886 2890
2887 2891 .markup-form textarea {
2888 2892 width: 100%;
2889 2893 height: 100px;
2890 2894 font-family: @text-monospace;
2891 2895 }
2892 2896
2893 2897 form.markup-form {
2894 2898 margin-top: 10px;
2895 2899 margin-left: 10px;
2896 2900 }
2897 2901
2898 2902 .markup-form .comment-block-ta,
2899 2903 .markup-form .preview-box {
2900 2904 .border-radius(@border-radius);
2901 2905 .box-sizing(border-box);
2902 2906 background-color: white;
2903 2907 }
2904 2908
2905 2909 .markup-form .preview-box.unloaded {
2906 2910 height: 50px;
2907 2911 text-align: center;
2908 2912 padding: 20px;
2909 2913 background-color: white;
2910 2914 }
2911 2915
2912 2916
2913 2917 .dropzone-wrapper {
2914 2918 border: 1px solid @grey5;
2915 2919 padding: 20px;
2916 2920 }
2917 2921
2918 2922 .dropzone,
2919 2923 .dropzone-pure {
2920 2924 border: 2px dashed @grey5;
2921 2925 border-radius: 5px;
2922 2926 background: white;
2923 2927 min-height: 200px;
2924 2928 padding: 54px;
2925 2929
2926 2930 .dz-message {
2927 2931 font-weight: 700;
2928 2932 text-align: center;
2929 2933 margin: 2em 0;
2930 2934 }
2931 2935
2932 2936 }
2933 2937
2934 2938 .dz-preview {
2935 2939 margin: 10px 0 !important;
2936 2940 position: relative;
2937 2941 vertical-align: top;
2938 2942 padding: 10px;
2939 2943 border-bottom: 1px solid @grey5;
2940 2944 }
2941 2945
2942 2946 .dz-filename {
2943 2947 font-weight: 700;
2944 2948 float: left;
2945 2949 }
2946 2950
2947 2951 .dz-sending {
2948 2952 float: right;
2949 2953 }
2950 2954
2951 2955 .dz-response {
2952 2956 clear: both
2953 2957 }
2954 2958
2955 2959 .dz-filename-size {
2956 2960 float: right
2957 2961 }
2958 2962
2959 2963 .dz-error-message {
2960 2964 color: @alert2;
2961 2965 padding-top: 10px;
2962 2966 clear: both;
2963 2967 }
2964 2968
2965 2969
2966 2970 .user-hovercard {
2967 2971 padding: 5px;
2968 2972 }
2969 2973
2970 2974 .user-hovercard-icon {
2971 2975 display: inline;
2972 2976 padding: 0;
2973 2977 box-sizing: content-box;
2974 2978 border-radius: 50%;
2975 2979 float: left;
2976 2980 }
2977 2981
2978 2982 .user-hovercard-name {
2979 2983 float: right;
2980 2984 vertical-align: top;
2981 2985 padding-left: 10px;
2982 2986 min-width: 150px;
2983 2987 }
2984 2988
2985 2989 .user-hovercard-bio {
2986 2990 clear: both;
2987 2991 padding-top: 10px;
2988 2992 }
2989 2993
2990 2994 .user-hovercard-header {
2991 2995 clear: both;
2992 2996 min-height: 10px;
2993 2997 }
2994 2998
2995 2999 .user-hovercard-footer {
2996 3000 clear: both;
2997 3001 min-height: 10px;
2998 3002 }
2999 3003
3000 3004 .user-group-hovercard {
3001 3005 padding: 5px;
3002 3006 }
3003 3007
3004 3008 .user-group-hovercard-icon {
3005 3009 display: inline;
3006 3010 padding: 0;
3007 3011 box-sizing: content-box;
3008 3012 border-radius: 50%;
3009 3013 float: left;
3010 3014 }
3011 3015
3012 3016 .user-group-hovercard-name {
3013 3017 float: left;
3014 3018 vertical-align: top;
3015 3019 padding-left: 10px;
3016 3020 min-width: 150px;
3017 3021 }
3018 3022
3019 3023 .user-group-hovercard-icon i {
3020 3024 border: 1px solid @grey4;
3021 3025 border-radius: 4px;
3022 3026 }
3023 3027
3024 3028 .user-group-hovercard-bio {
3025 3029 clear: both;
3026 3030 padding-top: 10px;
3027 3031 line-height: 1.0em;
3028 3032 }
3029 3033
3030 3034 .user-group-hovercard-header {
3031 3035 clear: both;
3032 3036 min-height: 10px;
3033 3037 }
3034 3038
3035 3039 .user-group-hovercard-footer {
3036 3040 clear: both;
3037 3041 min-height: 10px;
3038 3042 }
3039 3043
3040 3044 .pr-hovercard-header {
3041 3045 clear: both;
3042 3046 display: block;
3043 3047 line-height: 20px;
3044 3048 }
3045 3049
3046 3050 .pr-hovercard-user {
3047 3051 display: flex;
3048 3052 align-items: center;
3049 3053 padding-left: 5px;
3050 3054 }
3051 3055
3052 3056 .pr-hovercard-title {
3053 3057 padding-top: 5px;
3054 3058 } No newline at end of file
@@ -1,394 +1,395 b''
1 1
2 2 /******************************************************************************
3 3 * *
4 4 * DO NOT CHANGE THIS FILE MANUALLY *
5 5 * *
6 6 * *
7 7 * This file is automatically generated when the app starts up with *
8 8 * generate_js_files = true *
9 9 * *
10 10 * To add a route here pass jsroute=True to the route definition in the app *
11 11 * *
12 12 ******************************************************************************/
13 13 function registerRCRoutes() {
14 14 // routes registration
15 15 pyroutes.register('favicon', '/favicon.ico', []);
16 16 pyroutes.register('robots', '/robots.txt', []);
17 17 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
18 18 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
19 19 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
20 20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
21 21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
22 22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
23 23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
24 24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
25 25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
26 26 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/_settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
27 27 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/_settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
28 28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
29 29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
30 30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
31 31 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
32 32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
33 33 pyroutes.register('hovercard_user', '/_hovercard/user/%(user_id)s', ['user_id']);
34 34 pyroutes.register('hovercard_username', '/_hovercard/username/%(username)s', ['username']);
35 35 pyroutes.register('hovercard_user_group', '/_hovercard/user_group/%(user_group_id)s', ['user_group_id']);
36 36 pyroutes.register('hovercard_pull_request', '/_hovercard/pull_request/%(pull_request_id)s', ['pull_request_id']);
37 37 pyroutes.register('hovercard_repo_commit', '/_hovercard/commit/%(repo_name)s/%(commit_id)s', ['repo_name', 'commit_id']);
38 38 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
39 39 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
40 40 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
41 41 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
42 42 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
43 43 pyroutes.register('admin_home', '/_admin', []);
44 44 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
45 45 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
46 46 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
47 47 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
48 48 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
49 49 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
50 50 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
51 51 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
52 52 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
53 53 pyroutes.register('admin_settings_exception_tracker', '/_admin/settings/exceptions', []);
54 54 pyroutes.register('admin_settings_exception_tracker_delete_all', '/_admin/settings/exceptions/delete', []);
55 55 pyroutes.register('admin_settings_exception_tracker_show', '/_admin/settings/exceptions/%(exception_id)s', ['exception_id']);
56 56 pyroutes.register('admin_settings_exception_tracker_delete', '/_admin/settings/exceptions/%(exception_id)s/delete', ['exception_id']);
57 57 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
58 58 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
59 59 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
60 60 pyroutes.register('admin_settings_process_management_data', '/_admin/settings/process_management/data', []);
61 61 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
62 62 pyroutes.register('admin_settings_process_management_master_signal', '/_admin/settings/process_management/master_signal', []);
63 63 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
64 64 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
65 65 pyroutes.register('admin_settings', '/_admin/settings', []);
66 66 pyroutes.register('admin_settings_update', '/_admin/settings/update', []);
67 67 pyroutes.register('admin_settings_global', '/_admin/settings/global', []);
68 68 pyroutes.register('admin_settings_global_update', '/_admin/settings/global/update', []);
69 69 pyroutes.register('admin_settings_vcs', '/_admin/settings/vcs', []);
70 70 pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []);
71 71 pyroutes.register('admin_settings_vcs_svn_pattern_delete', '/_admin/settings/vcs/svn_pattern_delete', []);
72 72 pyroutes.register('admin_settings_mapping', '/_admin/settings/mapping', []);
73 73 pyroutes.register('admin_settings_mapping_update', '/_admin/settings/mapping/update', []);
74 74 pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []);
75 75 pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []);
76 76 pyroutes.register('admin_settings_issuetracker', '/_admin/settings/issue-tracker', []);
77 77 pyroutes.register('admin_settings_issuetracker_update', '/_admin/settings/issue-tracker/update', []);
78 78 pyroutes.register('admin_settings_issuetracker_test', '/_admin/settings/issue-tracker/test', []);
79 79 pyroutes.register('admin_settings_issuetracker_delete', '/_admin/settings/issue-tracker/delete', []);
80 80 pyroutes.register('admin_settings_email', '/_admin/settings/email', []);
81 81 pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []);
82 82 pyroutes.register('admin_settings_hooks', '/_admin/settings/hooks', []);
83 83 pyroutes.register('admin_settings_hooks_update', '/_admin/settings/hooks/update', []);
84 84 pyroutes.register('admin_settings_hooks_delete', '/_admin/settings/hooks/delete', []);
85 85 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
86 86 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
87 87 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
88 88 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
89 89 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
90 90 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
91 91 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
92 92 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
93 93 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
94 94 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
95 95 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
96 96 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
97 97 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
98 98 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
99 99 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
100 100 pyroutes.register('users', '/_admin/users', []);
101 101 pyroutes.register('users_data', '/_admin/users_data', []);
102 102 pyroutes.register('users_create', '/_admin/users/create', []);
103 103 pyroutes.register('users_new', '/_admin/users/new', []);
104 104 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
105 105 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
106 106 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
107 107 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
108 108 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
109 109 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
110 110 pyroutes.register('user_enable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_enable', ['user_id']);
111 111 pyroutes.register('user_disable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_disable', ['user_id']);
112 112 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
113 113 pyroutes.register('user_notice_dismiss', '/_admin/users/%(user_id)s/notice_dismiss', ['user_id']);
114 114 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
115 115 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
116 116 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
117 117 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
118 118 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
119 119 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
120 120 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
121 121 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
122 122 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
123 123 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
124 124 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
125 125 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
126 126 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
127 127 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
128 128 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
129 129 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
130 130 pyroutes.register('edit_user_audit_logs_download', '/_admin/users/%(user_id)s/edit/audit/download', ['user_id']);
131 131 pyroutes.register('edit_user_caches', '/_admin/users/%(user_id)s/edit/caches', ['user_id']);
132 132 pyroutes.register('edit_user_caches_update', '/_admin/users/%(user_id)s/edit/caches/update', ['user_id']);
133 133 pyroutes.register('user_groups', '/_admin/user_groups', []);
134 134 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
135 135 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
136 136 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
137 137 pyroutes.register('repos', '/_admin/repos', []);
138 138 pyroutes.register('repos_data', '/_admin/repos_data', []);
139 139 pyroutes.register('repo_new', '/_admin/repos/new', []);
140 140 pyroutes.register('repo_create', '/_admin/repos/create', []);
141 141 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
142 142 pyroutes.register('repo_groups_data', '/_admin/repo_groups_data', []);
143 143 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
144 144 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
145 145 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
146 146 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
147 147 pyroutes.register('channelstream_proxy', '/_channelstream', []);
148 148 pyroutes.register('upload_file', '/_file_store/upload', []);
149 149 pyroutes.register('download_file', '/_file_store/download/%(fid)s', ['fid']);
150 150 pyroutes.register('download_file_by_token', '/_file_store/token-download/%(_auth_token)s/%(fid)s', ['_auth_token', 'fid']);
151 151 pyroutes.register('logout', '/_admin/logout', []);
152 152 pyroutes.register('reset_password', '/_admin/password_reset', []);
153 153 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
154 154 pyroutes.register('home', '/', []);
155 155 pyroutes.register('main_page_repos_data', '/_home_repos', []);
156 156 pyroutes.register('main_page_repo_groups_data', '/_home_repo_groups', []);
157 157 pyroutes.register('user_autocomplete_data', '/_users', []);
158 158 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
159 159 pyroutes.register('repo_list_data', '/_repos', []);
160 160 pyroutes.register('repo_group_list_data', '/_repo_groups', []);
161 161 pyroutes.register('goto_switcher_data', '/_goto_data', []);
162 162 pyroutes.register('markup_preview', '/_markup_preview', []);
163 163 pyroutes.register('file_preview', '/_file_preview', []);
164 164 pyroutes.register('store_user_session_value', '/_store_session_attr', []);
165 165 pyroutes.register('journal', '/_admin/journal', []);
166 166 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
167 167 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
168 168 pyroutes.register('journal_public', '/_admin/public_journal', []);
169 169 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
170 170 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
171 171 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
172 172 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
173 173 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
174 174 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
175 175 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
176 176 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
177 177 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
178 178 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
179 179 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
180 180 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
181 181 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
182 182 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
183 183 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
184 184 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
185 185 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
186 186 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
187 187 pyroutes.register('repo_commit_comment_attachment_upload', '/%(repo_name)s/changeset/%(commit_id)s/comment/attachment_upload', ['repo_name', 'commit_id']);
188 188 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
189 189 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
190 190 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
191 191 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
192 192 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
193 193 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
194 194 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
195 195 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
196 196 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
197 197 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
198 198 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
199 199 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
200 200 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
201 201 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
202 202 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
203 203 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
204 204 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
205 205 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
206 206 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
207 pyroutes.register('repo_files_check_head', '/%(repo_name)s/check_head/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
207 208 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
208 209 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
209 210 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
210 211 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
211 212 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
212 213 pyroutes.register('repo_files_upload_file', '/%(repo_name)s/upload_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
213 214 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
214 215 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
215 216 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
216 217 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
217 218 pyroutes.register('repo_commits', '/%(repo_name)s/commits', ['repo_name']);
218 219 pyroutes.register('repo_commits_file', '/%(repo_name)s/commits/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
219 220 pyroutes.register('repo_commits_elements', '/%(repo_name)s/commits_elements', ['repo_name']);
220 221 pyroutes.register('repo_commits_elements_file', '/%(repo_name)s/commits_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
221 222 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
222 223 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
223 224 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
224 225 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
225 226 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
226 227 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
227 228 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
228 229 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
229 230 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
230 231 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
231 232 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
232 233 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
233 234 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
234 235 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
235 236 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
236 237 pyroutes.register('pullrequest_repo_targets', '/%(repo_name)s/pull-request/repo-targets', ['repo_name']);
237 238 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
238 239 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
239 240 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
240 241 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
241 242 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
242 243 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
243 244 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
244 245 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
245 246 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
246 247 pyroutes.register('edit_repo_advanced_archive', '/%(repo_name)s/settings/advanced/archive', ['repo_name']);
247 248 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
248 249 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
249 250 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
250 251 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
251 252 pyroutes.register('edit_repo_advanced_hooks', '/%(repo_name)s/settings/advanced/hooks', ['repo_name']);
252 253 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
253 254 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
254 255 pyroutes.register('edit_repo_perms_set_private', '/%(repo_name)s/settings/permissions/set_private', ['repo_name']);
255 256 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
256 257 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
257 258 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
258 259 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
259 260 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
260 261 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
261 262 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
262 263 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
263 264 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
264 265 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
265 266 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
266 267 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
267 268 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
268 269 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
269 270 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
270 271 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
271 272 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
272 273 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
273 274 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
274 275 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
275 276 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
276 277 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
277 278 pyroutes.register('edit_repo_audit_logs', '/%(repo_name)s/settings/audit_logs', ['repo_name']);
278 279 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed-rss', ['repo_name']);
279 280 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed-atom', ['repo_name']);
280 281 pyroutes.register('rss_feed_home_old', '/%(repo_name)s/feed/rss', ['repo_name']);
281 282 pyroutes.register('atom_feed_home_old', '/%(repo_name)s/feed/atom', ['repo_name']);
282 283 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
283 284 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
284 285 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
285 286 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
286 287 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
287 288 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
288 289 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
289 290 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
290 291 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
291 292 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
292 293 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
293 294 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
294 295 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
295 296 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
296 297 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
297 298 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
298 299 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
299 300 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
300 301 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
301 302 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
302 303 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
303 304 pyroutes.register('search', '/_admin/search', []);
304 305 pyroutes.register('search_repo', '/%(repo_name)s/_search', ['repo_name']);
305 306 pyroutes.register('search_repo_alt', '/%(repo_name)s/search', ['repo_name']);
306 307 pyroutes.register('search_repo_group', '/%(repo_group_name)s/_search', ['repo_group_name']);
307 308 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
308 309 pyroutes.register('user_group_profile', '/_profile_user_group/%(user_group_name)s', ['user_group_name']);
309 310 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
310 311 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
311 312 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
312 313 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
313 314 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
314 315 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
315 316 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
316 317 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
317 318 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
318 319 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
319 320 pyroutes.register('my_account_user_group_membership', '/_admin/my_account/user_group_membership', []);
320 321 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
321 322 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
322 323 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
323 324 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
324 325 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
325 326 pyroutes.register('my_account_bookmarks', '/_admin/my_account/bookmarks', []);
326 327 pyroutes.register('my_account_bookmarks_update', '/_admin/my_account/bookmarks/update', []);
327 328 pyroutes.register('my_account_goto_bookmark', '/_admin/my_account/bookmark/%(bookmark_id)s', ['bookmark_id']);
328 329 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
329 330 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
330 331 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
331 332 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
332 333 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
333 334 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
334 335 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
335 336 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
336 337 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
337 338 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
338 339 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
339 340 pyroutes.register('gists_show', '/_admin/gists', []);
340 341 pyroutes.register('gists_new', '/_admin/gists/new', []);
341 342 pyroutes.register('gists_create', '/_admin/gists/create', []);
342 343 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
343 344 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
344 345 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
345 346 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
346 347 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
347 348 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
348 349 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
349 350 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
350 351 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
351 352 pyroutes.register('debug_style_email', '/_admin/debug_style/email/%(email_id)s', ['email_id']);
352 353 pyroutes.register('debug_style_email_plain_rendered', '/_admin/debug_style/email-rendered/%(email_id)s', ['email_id']);
353 354 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
354 355 pyroutes.register('apiv2', '/_admin/api', []);
355 356 pyroutes.register('admin_settings_license', '/_admin/settings/license', []);
356 357 pyroutes.register('admin_settings_license_unlock', '/_admin/settings/license_unlock', []);
357 358 pyroutes.register('login', '/_admin/login', []);
358 359 pyroutes.register('register', '/_admin/register', []);
359 360 pyroutes.register('repo_reviewers_review_rule_new', '/%(repo_name)s/settings/review/rules/new', ['repo_name']);
360 361 pyroutes.register('repo_reviewers_review_rule_edit', '/%(repo_name)s/settings/review/rules/%(rule_id)s', ['repo_name', 'rule_id']);
361 362 pyroutes.register('repo_reviewers_review_rule_delete', '/%(repo_name)s/settings/review/rules/%(rule_id)s/delete', ['repo_name', 'rule_id']);
362 363 pyroutes.register('plugin_admin_chat', '/_admin/plugin_admin_chat/%(action)s', ['action']);
363 364 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
364 365 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
365 366 pyroutes.register('admin_settings_scheduler_show_tasks', '/_admin/settings/scheduler/_tasks', []);
366 367 pyroutes.register('admin_settings_scheduler_show_all', '/_admin/settings/scheduler', []);
367 368 pyroutes.register('admin_settings_scheduler_new', '/_admin/settings/scheduler/new', []);
368 369 pyroutes.register('admin_settings_scheduler_create', '/_admin/settings/scheduler/create', []);
369 370 pyroutes.register('admin_settings_scheduler_edit', '/_admin/settings/scheduler/%(schedule_id)s', ['schedule_id']);
370 371 pyroutes.register('admin_settings_scheduler_update', '/_admin/settings/scheduler/%(schedule_id)s/update', ['schedule_id']);
371 372 pyroutes.register('admin_settings_scheduler_delete', '/_admin/settings/scheduler/%(schedule_id)s/delete', ['schedule_id']);
372 373 pyroutes.register('admin_settings_scheduler_execute', '/_admin/settings/scheduler/%(schedule_id)s/execute', ['schedule_id']);
373 374 pyroutes.register('admin_settings_automation', '/_admin/settings/automation', []);
374 375 pyroutes.register('admin_settings_automation_update', '/_admin/settings/automation/%(entry_id)s/update', ['entry_id']);
375 376 pyroutes.register('admin_permissions_branch', '/_admin/permissions/branch', []);
376 377 pyroutes.register('admin_permissions_branch_update', '/_admin/permissions/branch/update', []);
377 378 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
378 379 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
379 380 pyroutes.register('my_account_external_identity', '/_admin/my_account/external-identity', []);
380 381 pyroutes.register('my_account_external_identity_delete', '/_admin/my_account/external-identity/delete', []);
381 382 pyroutes.register('repo_artifacts_list', '/%(repo_name)s/artifacts', ['repo_name']);
382 383 pyroutes.register('repo_artifacts_data', '/%(repo_name)s/artifacts_data', ['repo_name']);
383 384 pyroutes.register('repo_artifacts_new', '/%(repo_name)s/artifacts/new', ['repo_name']);
384 385 pyroutes.register('repo_artifacts_get', '/%(repo_name)s/artifacts/download/%(uid)s', ['repo_name', 'uid']);
385 386 pyroutes.register('repo_artifacts_store', '/%(repo_name)s/artifacts/store', ['repo_name']);
386 387 pyroutes.register('repo_artifacts_info', '/%(repo_name)s/artifacts/info/%(uid)s', ['repo_name', 'uid']);
387 388 pyroutes.register('repo_artifacts_delete', '/%(repo_name)s/artifacts/delete/%(uid)s', ['repo_name', 'uid']);
388 389 pyroutes.register('repo_artifacts_update', '/%(repo_name)s/artifacts/update/%(uid)s', ['repo_name', 'uid']);
389 390 pyroutes.register('repo_automation', '/%(repo_name)s/settings/automation', ['repo_name']);
390 391 pyroutes.register('repo_automation_update', '/%(repo_name)s/settings/automation/%(entry_id)s/update', ['repo_name', 'entry_id']);
391 392 pyroutes.register('edit_repo_remote_push', '/%(repo_name)s/settings/remote/push', ['repo_name']);
392 393 pyroutes.register('edit_repo_perms_branch', '/%(repo_name)s/settings/branch_permissions', ['repo_name']);
393 394 pyroutes.register('edit_repo_perms_branch_delete', '/%(repo_name)s/settings/branch_permissions/%(rule_id)s/delete', ['repo_name', 'rule_id']);
394 395 }
@@ -1,523 +1,600 b''
1 1 // # Copyright (C) 2010-2019 RhodeCode GmbH
2 2 // #
3 3 // # This program is free software: you can redistribute it and/or modify
4 4 // # it under the terms of the GNU Affero General Public License, version 3
5 5 // # (only), as published by the Free Software Foundation.
6 6 // #
7 7 // # This program is distributed in the hope that it will be useful,
8 8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 // # GNU General Public License for more details.
11 11 // #
12 12 // # You should have received a copy of the GNU Affero General Public License
13 13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 // #
15 15 // # This program is dual-licensed. If you wish to learn more about the
16 16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 /**
20 20 * Search file list
21 21 */
22 22
23 23 var NodeFilter = {};
24 24
25 25 var fileBrowserListeners = function (node_list_url, url_base) {
26 26 var $filterInput = $('#node_filter');
27 27 var n_filter = $filterInput.get(0);
28 28
29 29 NodeFilter.filterTimeout = null;
30 30 var nodes = null;
31 31
32 32 NodeFilter.focus = function () {
33 33 $filterInput.focus()
34 34 };
35 35
36 36 NodeFilter.fetchNodes = function (callback) {
37 37 $.ajax(
38 38 {url: node_list_url, headers: {'X-PARTIAL-XHR': true}})
39 39 .done(function (data) {
40 40 nodes = data.nodes;
41 41 if (callback) {
42 42 callback();
43 43 }
44 44 })
45 45 .fail(function (data) {
46 46 console.log('failed to load');
47 47 });
48 48 };
49 49
50 50 NodeFilter.initFilter = function (e) {
51 51 if ($filterInput.hasClass('loading')) {
52 52 return
53 53 }
54 54
55 55 // in case we are already loaded, do nothing
56 56 if (!$filterInput.hasClass('init')) {
57 57 return NodeFilter.handleKey(e);
58 58 }
59 59 var iconLoading = 'icon-spin animate-spin';
60 60 var iconSearch = 'icon-search';
61 61 $('.files-filter-box-path i').removeClass(iconSearch).addClass(iconLoading);
62 62 $filterInput.addClass('loading');
63 63
64 64 var callback = function (org) {
65 65 return function () {
66 66 if ($filterInput.hasClass('init')) {
67 67 $filterInput.removeClass('init');
68 68 $filterInput.removeClass('loading');
69 69 }
70 70 $('.files-filter-box-path i').removeClass(iconLoading).addClass(iconSearch);
71 71
72 72 // auto re-filter if we filled in the input
73 73 if (n_filter.value !== "") {
74 74 NodeFilter.updateFilter(n_filter, e)()
75 75 }
76 76
77 77 }
78 78 };
79 79 // load node data
80 80 NodeFilter.fetchNodes(callback());
81 81
82 82 };
83 83
84 84 NodeFilter.resetFilter = function () {
85 85 $('#tbody').show();
86 86 $('#tbody_filtered').hide();
87 87 $filterInput.val('');
88 88 };
89 89
90 90 NodeFilter.handleKey = function (e) {
91 91 var scrollDown = function (element) {
92 92 var elementBottom = element.offset().top + $(element).outerHeight();
93 93 var windowBottom = window.innerHeight + $(window).scrollTop();
94 94 if (elementBottom > windowBottom) {
95 95 var offset = elementBottom - window.innerHeight;
96 96 $('html,body').scrollTop(offset);
97 97 return false;
98 98 }
99 99 return true;
100 100 };
101 101
102 102 var scrollUp = function (element) {
103 103 if (element.offset().top < $(window).scrollTop()) {
104 104 $('html,body').scrollTop(element.offset().top);
105 105 return false;
106 106 }
107 107 return true;
108 108 };
109 109 var $hlElem = $('.browser-highlight');
110 110
111 111 if (e.keyCode === 40) { // Down
112 112 if ($hlElem.length === 0) {
113 113 $('.browser-result').first().addClass('browser-highlight');
114 114 } else {
115 115 var next = $hlElem.next();
116 116 if (next.length !== 0) {
117 117 $hlElem.removeClass('browser-highlight');
118 118 next.addClass('browser-highlight');
119 119 }
120 120 }
121 121
122 122 if ($hlElem.get(0) !== undefined){
123 123 scrollDown($hlElem);
124 124 }
125 125 }
126 126 if (e.keyCode === 38) { // Up
127 127 e.preventDefault();
128 128 if ($hlElem.length !== 0) {
129 129 var next = $hlElem.prev();
130 130 if (next.length !== 0) {
131 131 $('.browser-highlight').removeClass('browser-highlight');
132 132 next.addClass('browser-highlight');
133 133 }
134 134 }
135 135
136 136 if ($hlElem.get(0) !== undefined){
137 137 scrollUp($hlElem);
138 138 }
139 139
140 140 }
141 141 if (e.keyCode === 13) { // Enter
142 142 if ($('.browser-highlight').length !== 0) {
143 143 var url = $('.browser-highlight').find('.match-link').attr('href');
144 144 window.location = url;
145 145 }
146 146 }
147 147 if (e.keyCode === 27) { // Esc
148 148 NodeFilter.resetFilter();
149 149 $('html,body').scrollTop(0);
150 150 }
151 151
152 152 var capture_keys = [
153 153 40, // ArrowDown
154 154 38, // ArrowUp
155 155 39, // ArrowRight
156 156 37, // ArrowLeft
157 157 13, // Enter
158 158 27 // Esc
159 159 ];
160 160
161 161 if ($.inArray(e.keyCode, capture_keys) === -1) {
162 162 clearTimeout(NodeFilter.filterTimeout);
163 163 NodeFilter.filterTimeout = setTimeout(NodeFilter.updateFilter(n_filter, e), 200);
164 164 }
165 165
166 166 };
167 167
168 168 NodeFilter.fuzzy_match = function (filepath, query) {
169 169 var highlight = [];
170 170 var order = 0;
171 171 for (var i = 0; i < query.length; i++) {
172 172 var match_position = filepath.indexOf(query[i]);
173 173 if (match_position !== -1) {
174 174 var prev_match_position = highlight[highlight.length - 1];
175 175 if (prev_match_position === undefined) {
176 176 highlight.push(match_position);
177 177 } else {
178 178 var current_match_position = prev_match_position + match_position + 1;
179 179 highlight.push(current_match_position);
180 180 order = order + current_match_position - prev_match_position;
181 181 }
182 182 filepath = filepath.substring(match_position + 1);
183 183 } else {
184 184 return false;
185 185 }
186 186 }
187 187 return {
188 188 'order': order,
189 189 'highlight': highlight
190 190 };
191 191 };
192 192
193 193 NodeFilter.sortPredicate = function (a, b) {
194 194 if (a.order < b.order) return -1;
195 195 if (a.order > b.order) return 1;
196 196 if (a.filepath < b.filepath) return -1;
197 197 if (a.filepath > b.filepath) return 1;
198 198 return 0;
199 199 };
200 200
201 201 NodeFilter.updateFilter = function (elem, e) {
202 202 return function () {
203 203 // Reset timeout
204 204 NodeFilter.filterTimeout = null;
205 205 var query = elem.value.toLowerCase();
206 206 var match = [];
207 207 var matches_max = 20;
208 208 if (query !== "") {
209 209 var results = [];
210 210 for (var k = 0; k < nodes.length; k++) {
211 211 var result = NodeFilter.fuzzy_match(
212 212 nodes[k].name.toLowerCase(), query);
213 213 if (result) {
214 214 result.type = nodes[k].type;
215 215 result.filepath = nodes[k].name;
216 216 results.push(result);
217 217 }
218 218 }
219 219 results = results.sort(NodeFilter.sortPredicate);
220 220 var limit = matches_max;
221 221 if (results.length < matches_max) {
222 222 limit = results.length;
223 223 }
224 224 for (var i = 0; i < limit; i++) {
225 225 if (query && results.length > 0) {
226 226 var n = results[i].filepath;
227 227 var t = results[i].type;
228 228 var n_hl = n.split("");
229 229 var pos = results[i].highlight;
230 230 for (var j = 0; j < pos.length; j++) {
231 231 n_hl[pos[j]] = "<em>" + n_hl[pos[j]] + "</em>";
232 232 }
233 233 n_hl = n_hl.join("");
234 234 var new_url = url_base.replace('__FPATH__', n);
235 235
236 236 var typeObj = {
237 237 dir: 'icon-directory browser-dir',
238 238 file: 'icon-file-text browser-file'
239 239 };
240 240
241 241 var typeIcon = '<i class="{0}"></i>'.format(typeObj[t]);
242 242 match.push('<tr class="browser-result"><td><a class="match-link" href="{0}">{1}{2}</a></td><td colspan="5"></td></tr>'.format(new_url, typeIcon, n_hl));
243 243 }
244 244 }
245 245 if (results.length > limit) {
246 246 var truncated_count = results.length - matches_max;
247 247 if (truncated_count === 1) {
248 248 match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated result')));
249 249 } else {
250 250 match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated results')));
251 251 }
252 252 }
253 253 }
254 254 if (query !== "") {
255 255 $('#tbody').hide();
256 256 $('#tbody_filtered').show();
257 257
258 258 if (match.length === 0) {
259 259 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_gettext('No matching files')));
260 260 }
261 261 $('#tbody_filtered').html(match.join(""));
262 262 } else {
263 263 $('#tbody').show();
264 264 $('#tbody_filtered').hide();
265 265 }
266 266
267 267 };
268 268 };
269 269
270 270 };
271 271
272 272 var getIdentNode = function(n){
273 273 // iterate through nodes until matched interesting node
274 274 if (typeof n === 'undefined'){
275 275 return -1;
276 276 }
277 277 if(typeof n.id !== "undefined" && n.id.match('L[0-9]+')){
278 278 return n;
279 279 }
280 280 else{
281 281 return getIdentNode(n.parentNode);
282 282 }
283 283 };
284 284
285 285 var getSelectionLink = function(e) {
286 286 // get selection from start/to nodes
287 287 if (typeof window.getSelection !== "undefined") {
288 288 s = window.getSelection();
289 289
290 290 from = getIdentNode(s.anchorNode);
291 291 till = getIdentNode(s.focusNode);
292 292
293 293 f_int = parseInt(from.id.replace('L',''));
294 294 t_int = parseInt(till.id.replace('L',''));
295 295
296 296 if (f_int > t_int){
297 297 // highlight from bottom
298 298 offset = -35;
299 299 ranges = [t_int,f_int];
300 300 }
301 301 else{
302 302 // highligth from top
303 303 offset = 35;
304 304 ranges = [f_int,t_int];
305 305 }
306 306 // if we select more than 2 lines
307 307 if (ranges[0] !== ranges[1]){
308 308 if($('#linktt').length === 0){
309 309 hl_div = document.createElement('div');
310 310 hl_div.id = 'linktt';
311 311 }
312 312 hl_div.innerHTML = '';
313 313
314 314 anchor = '#L'+ranges[0]+'-'+ranges[1];
315 315 var link = document.createElement('a');
316 316 link.href = location.href.substring(0,location.href.indexOf('#'))+anchor;
317 317 link.innerHTML = _gettext('Selection link');
318 318 hl_div.appendChild(link);
319 319 $('#codeblock').append(hl_div);
320 320
321 321 var xy = $(till).offset();
322 322 $('#linktt').addClass('hl-tip-box tip-box');
323 323 $('#linktt').offset({top: xy.top + offset, left: xy.left});
324 324 $('#linktt').css('visibility','visible');
325 325 }
326 326 else{
327 327 $('#linktt').css('visibility','hidden');
328 328 }
329 329 }
330 330 };
331 331
332 332 var getFileState = function() {
333 333 // relies on a global set filesUrlData
334 334 var f_path = filesUrlData['f_path'];
335 335 var commit_id = filesUrlData['commit_id'];
336 336
337 337 var url_params = {
338 338 repo_name: templateContext.repo_name,
339 339 commit_id: commit_id,
340 340 f_path:'__FPATH__'
341 341 };
342 342 if (atRef !== '') {
343 343 url_params['at'] = atRef
344 344 }
345 345
346 346 var _url_base = pyroutes.url('repo_files', url_params);
347 347 var _node_list_url = pyroutes.url('repo_files_nodelist',
348 348 {repo_name: templateContext.repo_name,
349 349 commit_id: commit_id, f_path: f_path});
350 350
351 351 return {
352 352 f_path: f_path,
353 353 commit_id: commit_id,
354 354 node_list_url: _node_list_url,
355 355 url_base: _url_base
356 356 };
357 357 };
358 358
359 359 var getFilesMetadata = function() {
360 360 // relies on metadataRequest global state
361 361 if (metadataRequest && metadataRequest.readyState != 4) {
362 362 metadataRequest.abort();
363 363 }
364 364
365 365 if ($('#file-tree-wrapper').hasClass('full-load')) {
366 366 // in case our HTML wrapper has full-load class we don't
367 367 // trigger the async load of metadata
368 368 return false;
369 369 }
370 370
371 371 var state = getFileState();
372 372 var url_data = {
373 373 'repo_name': templateContext.repo_name,
374 374 'commit_id': state.commit_id,
375 375 'f_path': state.f_path,
376 376 };
377 377
378 378 if (atRef !== '') {
379 379 url_data['at'] = atRef
380 380 }
381 381
382 382 var url = pyroutes.url('repo_nodetree_full', url_data);
383 383
384 384 metadataRequest = $.ajax({url: url});
385 385
386 386 metadataRequest.done(function(data) {
387 387 $('#file-tree').html(data);
388 388 timeagoActivate();
389 389 tooltipActivate();
390 390 });
391 391 metadataRequest.fail(function (data, textStatus, errorThrown) {
392 392 if (data.status != 0) {
393 393 alert("Error while fetching metadata.\nError code {0} ({1}).Please consider reloading the page".format(data.status,data.statusText));
394 394 }
395 395 });
396 396 };
397 397
398 398 // show more authors
399 399 var showAuthors = function(elem, annotate) {
400 400 var state = getFileState('callbacks');
401 401
402 402 var url = pyroutes.url('repo_file_authors',
403 403 {'repo_name': templateContext.repo_name,
404 404 'commit_id': state.commit_id, 'f_path': state.f_path});
405 405
406 406 $.pjax({
407 407 url: url,
408 408 data: 'annotate={0}'.format(annotate),
409 409 container: '#file_authors',
410 410 push: false,
411 411 timeout: 5000
412 412 }).complete(function(){
413 413 $(elem).hide();
414 414 $('#file_authors_title').html(_gettext('All Authors'));
415 415 tooltipActivate();
416 416 })
417 417 };
418 418
419 419
420 420 (function (mod) {
421 421
422 422 if (typeof exports == "object" && typeof module == "object") {
423 423 // CommonJS
424 424 module.exports = mod();
425 425 } else {
426 426 // Plain browser env
427 427 (this || window).FileEditor = mod();
428 428 }
429 429
430 430 })(function () {
431 431 "use strict";
432 432
433 433 function FileEditor(textAreaElement, options) {
434 434 if (!(this instanceof FileEditor)) {
435 435 return new FileEditor(textAreaElement, options);
436 436 }
437 437 // bind the element instance to our Form
438 438 var te = $(textAreaElement).get(0);
439 439 if (te !== undefined) {
440 440 te.FileEditor = this;
441 441 }
442 442
443 443 this.modes_select = '#set_mode';
444 444 this.filename_selector = '#filename';
445 445 this.commit_btn_selector = '#commit_btn';
446 446 this.line_wrap_selector = '#line_wrap';
447 447 this.editor_preview_selector = '#editor_preview';
448 448
449 449 if (te !== undefined) {
450 450 this.cm = initCodeMirror(textAreaElement, null, false);
451 451 }
452 452
453 453 // FUNCTIONS and helpers
454 454 var self = this;
455 455
456 456 this.submitHandler = function() {
457 457 $(self.commit_btn_selector).on('click', function(e) {
458 458
459 459 var filename = $(self.filename_selector).val();
460 460 if (filename === "") {
461 461 alert("Missing filename");
462 462 e.preventDefault();
463 463 }
464 464
465 465 var button = $(this);
466 466 if (button.hasClass('clicked')) {
467 467 button.attr('disabled', true);
468 468 } else {
469 469 button.addClass('clicked');
470 470 }
471 471 });
472 472 };
473 473 this.submitHandler();
474 474
475 475 // on select line wraps change the editor
476 476 this.lineWrapHandler = function () {
477 477 $(self.line_wrap_selector).on('change', function (e) {
478 478 var selected = e.currentTarget;
479 479 var line_wraps = {'on': true, 'off': false}[selected.value];
480 480 setCodeMirrorLineWrap(self.cm, line_wraps)
481 481 });
482 482 };
483 483 this.lineWrapHandler();
484 484
485 485
486 486 this.showPreview = function () {
487 487
488 488 var _text = self.cm.getValue();
489 489 var _file_path = $(self.filename_selector).val();
490 490 if (_text && _file_path) {
491 491 $('.show-preview').addClass('active');
492 492 $('.show-editor').removeClass('active');
493 493
494 494 $(self.editor_preview_selector).show();
495 495 $(self.cm.getWrapperElement()).hide();
496 496
497 497
498 498 var post_data = {'text': _text, 'file_path': _file_path, 'csrf_token': CSRF_TOKEN};
499 499 $(self.editor_preview_selector).html(_gettext('Loading ...'));
500 500
501 501 var url = pyroutes.url('file_preview');
502 502
503 503 ajaxPOST(url, post_data, function (o) {
504 504 $(self.editor_preview_selector).html(o);
505 505 })
506 506 }
507 507
508 508 };
509 509
510 510 this.showEditor = function () {
511 511 $(self.editor_preview_selector).hide();
512 512 $('.show-editor').addClass('active');
513 513 $('.show-preview').removeClass('active');
514 514
515 515 $(self.cm.getWrapperElement()).show();
516 516 };
517 517
518 518
519 519 }
520 520
521 521 return FileEditor;
522 522 });
523 523
524
525 var checkFileHead = function($editForm, commit_id, f_path, operation) {
526 function getFormData($form){
527 var unindexed_array = $form.serializeArray();
528 var indexed_array = {};
529
530 $.map(unindexed_array, function(n, i){
531 indexed_array[n['name']] = n['value'];
532 });
533
534 return indexed_array;
535 }
536
537 $editForm.on('submit', function (e) {
538
539 var validHead = $editForm.find('#commit_btn').data('validHead');
540 if (validHead === true){
541 return true
542 }
543
544 // no marker, we do "checks"
545 e.preventDefault();
546 var formData = getFormData($editForm);
547 var new_path = formData['filename'];
548
549 var success = function(data) {
550
551 if (data['is_head'] === true && data['path_exists'] === "") {
552
553 $editForm.find('#commit_btn').data('validHead', true);
554 $editForm.find('#commit_btn').val('Committing...');
555 $editForm.submit();
556
557 } else {
558 var message = '';
559 var urlTmpl = '<a target="_blank" href="{0}">here</a>';
560 $editForm.find('#commit_btn').val('Commit aborted');
561
562 if (operation === 'edit') {
563 var new_url = urlTmpl.format(pyroutes.url('repo_files_edit_file', {"repo_name": templateContext.repo_name, "commit_id": data['branch'], "f_path": f_path}));
564 message = _gettext('File `{0}` has a newer version available, or has been removed. Click {1} to see the latest version.'.format(f_path, new_url));
565 } else if (operation === 'delete') {
566 var new_url = urlTmpl.format(pyroutes.url('repo_files_remove_file', {"repo_name": templateContext.repo_name, "commit_id": data['branch'], "f_path": f_path}));
567 message = _gettext('File `{0}` has a newer version available, or has been removed. Click {1} to see the latest version.'.format(f_path, new_url));
568 } else if (operation === 'create') {
569 if (data['path_exists'] !== "") {
570 message = _gettext('There is an existing path `{0}` at this commit.'.format(data['path_exists']));
571 } else {
572 var new_url = urlTmpl.format(pyroutes.url('repo_files_add_file', {"repo_name": templateContext.repo_name, "commit_id": data['branch'], "f_path": f_path, "filename": new_path}));
573 message = _gettext('There is a later version of file tree available. Click {0} to create a file at the latest tree.'.format(new_url));
574 }
575 }
576
577 var payload = {
578 message: {
579 message: message,
580 level: 'warning',
581 force: true
582 }
583 };
584 $.Topic('/notifications').publish(payload);
585 }
586 };
587
588 // lock button
589 $editForm.find('#commit_btn').attr('disabled', 'disabled');
590 $editForm.find('#commit_btn').val('Checking commit...');
591
592 var postData = {'csrf_token': CSRF_TOKEN, 'path': new_path, 'operation': operation};
593 ajaxPOST(pyroutes.url('repo_files_check_head',
594 {'repo_name': templateContext.repo_name, 'commit_id': commit_id, 'f_path': f_path}),
595 postData, success);
596
597 return false
598
599 });
600 };
@@ -1,114 +1,117 b''
1 1 <%inherit file="/base/base.mako"/>
2 2
3 3 <%def name="title()">
4 4 ${_('{} Files Add').format(c.repo_name)}
5 5 %if c.rhodecode_name:
6 6 &middot; ${h.branding(c.rhodecode_name)}
7 7 %endif
8 8 </%def>
9 9
10 10 <%def name="menu_bar_nav()">
11 11 ${self.menu_items(active='repositories')}
12 12 </%def>
13 13
14 14 <%def name="breadcrumbs_links()"></%def>
15 15
16 16 <%def name="menu_bar_subnav()">
17 17 ${self.repo_menu(active='files')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21
22 22 <div class="box">
23 23
24 24 <div class="edit-file-title">
25 25 <span class="title-heading">${_('Add new file')} @ <code>${h.show_id(c.commit)}</code></span>
26 26 % if c.commit.branch:
27 27 <span class="tag branchtag">
28 28 <i class="icon-branch"></i> ${c.commit.branch}
29 29 </span>
30 30 % endif
31 31 </div>
32 32
33 33 ${h.secure_form(h.route_path('repo_files_create_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', request=request)}
34 34 <div class="edit-file-fieldset">
35 35 <div class="path-items">
36 36 <ul>
37 37 <li class="breadcrumb-path">
38 38 <div>
39 39 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path='')}"><i class="icon-home"></i></a> /
40 40 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path)}">${c.f_path}</a> ${('/' if c.f_path else '')}
41 41 </div>
42 42 </li>
43 43 <li class="location-path">
44 <input class="file-name-input input-small" type="text" value="" name="filename" id="filename" placeholder="${_('Filename e.g example.py, or docs/readme.md')}">
44 <input class="file-name-input input-small" type="text" value="${request.GET.get('filename')}" name="filename" id="filename" placeholder="${_('Filename e.g example.py, or docs/readme.md')}">
45 45 </li>
46 46 </ul>
47 47 </div>
48 48
49 49 </div>
50 50
51 51 <div class="table">
52 52 <div>
53
54 53 <div id="codeblock" class="codeblock">
55 54 <div class="editor-items">
56 55 <div class="editor-action active show-editor pull-left" onclick="fileEditor.showEditor(); return false">
57 56 ${_('Edit')}
58 57 </div>
59 58
60 59 <div class="editor-action show-preview pull-left" onclick="fileEditor.showPreview(); return false">
61 60 ${_('Preview')}
62 61 </div>
63 62
64 63 <div class="pull-right">
65 64 ${h.dropdownmenu('line_wrap', 'off', [('on', _('Line wraps on')), ('off', _('line wraps off'))], extra_classes=['last-item'])}
66 65 </div>
67 66 <div class="pull-right">
68 67 ${h.dropdownmenu('set_mode','plain',[('plain', _('plain'))], enable_filter=True)}
69 68 </div>
70 69 </div>
71 70
72 71 <div id="editor_container">
73 72 <pre id="editor_pre"></pre>
74 73 <textarea id="editor" name="content" ></textarea>
75 74 <div id="editor_preview"></div>
76 75 </div>
77 76 </div>
78 77 </div>
79 78 </div>
80 79
81 80 <div class="edit-file-fieldset">
82 81 <div class="fieldset">
83 82 <div class="message">
84 83 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
85 84 </div>
86 85 </div>
87 86 <div class="pull-left">
88 87 ${h.submit('commit_btn',_('Commit changes'), class_="btn btn-small btn-success")}
89 88 </div>
90 89 </div>
91 90 ${h.end_form()}
92 91 </div>
93 92
94 93 <script type="text/javascript">
95 94
96 95 $(document).ready(function () {
97 96 var modes_select = $('#set_mode');
98 97 var filename_selector = '#filename';
99 98 fillCodeMirrorOptions(modes_select);
100 99
101 100 fileEditor = new FileEditor('#editor');
102 101
103 102 // on change of select field set mode
104 103 setCodeMirrorModeFromSelect(modes_select, filename_selector, fileEditor.cm, null);
105 104
106 105 // on entering the new filename set mode, from given extension
107 106 setCodeMirrorModeFromInput(modes_select, filename_selector, fileEditor.cm, null);
108 107
109 108 $('#filename').focus();
110 109
110 var commit_id = "${c.commit.raw_id}";
111 var f_path = "${c.f_path}";
112
113 checkFileHead($('#eform'), commit_id, f_path, 'create')
111 114 });
112 115
113 116 </script>
114 117 </%def>
@@ -1,89 +1,93 b''
1 1 <%inherit file="/base/base.mako"/>
2 2
3 3 <%def name="title()">
4 4 ${_('{} Files Delete').format(c.repo_name)}
5 5 %if c.rhodecode_name:
6 6 &middot; ${h.branding(c.rhodecode_name)}
7 7 %endif
8 8 </%def>
9 9
10 10 <%def name="menu_bar_nav()">
11 11 ${self.menu_items(active='repositories')}
12 12 </%def>
13 13
14 14 <%def name="breadcrumbs_links()"></%def>
15 15
16 16 <%def name="menu_bar_subnav()">
17 17 ${self.repo_menu(active='files')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21
22 22 <div class="box">
23 23
24 24 <div class="edit-file-title">
25 25 <span class="title-heading">${_('Delete file')} @ <code>${h.show_id(c.commit)}</code></span>
26 26 % if c.commit.branch:
27 27 <span class="tag branchtag">
28 28 <i class="icon-branch"></i> ${c.commit.branch}
29 29 </span>
30 30 % endif
31 31 </div>
32 32
33 33 ${h.secure_form(h.route_path('repo_files_delete_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', request=request)}
34 34 <div class="edit-file-fieldset">
35 35 <div class="path-items">
36 36 <li class="breadcrumb-path">
37 37 <div>
38 38 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path='')}"><i class="icon-home"></i></a> /
39 39 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.file.dir_path)}">${c.file.dir_path}</a> ${('/' if c.file.dir_path else '')}
40 40 </div>
41 41 </li>
42 42 <li class="location-path">
43 43 <input type="hidden" value="${c.f_path}" name="root_path">
44 44 <input class="file-name-input input-small" type="text" value="${c.file.name}" name="filename" id="filename" disabled="disabled">
45 45 </li>
46 46 </div>
47 47
48 48 </div>
49 49
50 50 <div id="codeblock" class="codeblock delete-file-preview">
51 51 <div class="code-body">
52 52 %if c.file.is_binary:
53 53 ${_('Binary file (%s)') % c.file.mimetype}
54 54 %else:
55 55 %if c.file.size < c.visual.cut_off_limit_file:
56 56 ${h.pygmentize(c.file,linenos=True,anchorlinenos=False,cssclass="code-highlight")}
57 57 %else:
58 58 ${_('File size {} is bigger then allowed limit {}. ').format(h.format_byte_size_binary(c.file.size), h.format_byte_size_binary(c.visual.cut_off_limit_file))} ${h.link_to(_('Show as raw'),
59 59 h.route_path('repo_file_raw',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path))}
60 60 %endif
61 61 %endif
62 62 </div>
63 63 </div>
64 64
65 65 <div class="edit-file-fieldset">
66 66 <div class="fieldset">
67 67 <div class="message">
68 68 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
69 69 </div>
70 70 </div>
71 71 <div class="pull-left">
72 ${h.submit('commit',_('Commit changes'),class_="btn btn-small btn-danger-action")}
72 ${h.submit('commit_btn',_('Commit changes'),class_="btn btn-small btn-danger-action")}
73 73 </div>
74 74 </div>
75 75 ${h.end_form()}
76 76 </div>
77 77
78 78
79 79 <script type="text/javascript">
80 80
81 81 $(document).ready(function () {
82 82
83 83 fileEditor = new FileEditor('#editor');
84 84
85 var commit_id = "${c.commit.raw_id}";
86 var f_path = "${c.f_path}";
87
88 checkFileHead($('#eform'), commit_id, f_path, 'delete');
85 89 });
86 90
87 91 </script>
88 92
89 93 </%def>
@@ -1,124 +1,129 b''
1 1 <%inherit file="/base/base.mako"/>
2 2
3 3 <%def name="title()">
4 4 ${_('{} Files Edit').format(c.repo_name)}
5 5 %if c.rhodecode_name:
6 6 &middot; ${h.branding(c.rhodecode_name)}
7 7 %endif
8 8 </%def>
9 9
10 10 <%def name="menu_bar_nav()">
11 11 ${self.menu_items(active='repositories')}
12 12 </%def>
13 13
14 14 <%def name="breadcrumbs_links()"></%def>
15 15
16 16 <%def name="menu_bar_subnav()">
17 17 ${self.repo_menu(active='files')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21
22 22 <div class="box">
23 23
24 24 <div class="edit-file-title">
25 25 <span class="title-heading">${_('Edit file')} @ <code>${h.show_id(c.commit)}</code></span>
26 26 % if c.commit.branch:
27 27 <span class="tag branchtag">
28 28 <i class="icon-branch"></i> ${c.commit.branch}
29 29 </span>
30 30 % endif
31 31 </div>
32 32
33 33 ${h.secure_form(h.route_path('repo_files_update_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path), id='eform', request=request)}
34 34 <div class="edit-file-fieldset">
35 35 <div class="path-items">
36 36 <ul>
37 37 <li class="breadcrumb-path">
38 38 <div>
39 39 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path='')}"><i class="icon-home"></i></a> /
40 40 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.file.dir_path)}">${c.file.dir_path}</a> ${('/' if c.file.dir_path else '')}
41 41 </div>
42 42 </li>
43 43 <li class="location-path">
44 44 <input type="hidden" value="${c.f_path}" name="root_path">
45 45 <input class="file-name-input input-small" type="text" value="${c.file.name}" name="filename" id="filename" placeholder="${_('Filename e.g example.py, or docs/readme.md')}">
46 46 </li>
47 47 </ul>
48 48 </div>
49 49
50 50 </div>
51 51
52 52 <div class="table">
53 53 <div>
54 54
55 55 <div id="codeblock" class="codeblock">
56 56 <div class="editor-items">
57 57 <div class="editor-action active show-editor pull-left" onclick="fileEditor.showEditor(); return false">
58 58 ${_('Edit')}
59 59 </div>
60 60
61 61 <div class="editor-action show-preview pull-left" onclick="fileEditor.showPreview(); return false">
62 62 ${_('Preview')}
63 63 </div>
64 64
65 65 <div class="pull-right">
66 66 ${h.dropdownmenu('line_wrap', 'off', [('on', _('Line wraps on')), ('off', _('line wraps off')),])}
67 67 </div>
68 68 <div class="pull-right">
69 69 ${h.dropdownmenu('set_mode','plain',[('plain', _('plain'))],enable_filter=True)}
70 70 </div>
71 71 </div>
72 72
73 73 <div id="editor_container">
74 74 <pre id="editor_pre"></pre>
75 75 <textarea id="editor" name="content" >${h.escape(c.file.content)|n}</textarea>
76 76 <div id="editor_preview" ></div>
77 77 </div>
78 78 </div>
79 79 </div>
80 80 </div>
81 81
82 82 <div class="edit-file-fieldset">
83 83 <div class="fieldset">
84 84 <div class="message">
85 85 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
86 86 </div>
87 87 </div>
88 88 <div class="pull-left">
89 89 ${h.submit('commit_btn',_('Commit changes'), class_="btn btn-small btn-success")}
90 90 </div>
91 91 </div>
92 92 ${h.end_form()}
93 93 </div>
94 94
95 95 <script type="text/javascript">
96 96
97 97 $(document).ready(function() {
98 98 var modes_select = $('#set_mode');
99 99 var filename_selector = '#filename';
100 100 fillCodeMirrorOptions(modes_select);
101 101
102 102 fileEditor = new FileEditor('#editor');
103 103
104 104 // try to detect the mode based on the file we edit
105 105 var detected_mode = detectCodeMirrorMode("${c.file.name}", "${c.file.mimetype}");
106 106
107 107 if (detected_mode) {
108 108 setCodeMirrorMode(fileEditor.cm, detected_mode);
109 109
110 110 var mimetype = $(modes_select).find("option[mode={0}]".format(detected_mode)).val();
111 111 $(modes_select).select2("val", mimetype).trigger('change');
112 112 }
113 113
114 114 // on change of select field set mode
115 115 setCodeMirrorModeFromSelect(modes_select, filename_selector, fileEditor.cm, null);
116 116
117 117 // on entering the new filename set mode, from given extension
118 118 setCodeMirrorModeFromInput(modes_select, filename_selector, fileEditor.cm, null);
119 119
120 var commit_id = "${c.commit.raw_id}";
121 var f_path = "${c.f_path}";
122
123 checkFileHead($('#eform'), commit_id, f_path, 'edit')
124
120 125 });
121 126
122 127
123 128 </script>
124 129 </%def>
@@ -1,211 +1,211 b''
1 1 <%inherit file="/base/base.mako"/>
2 2
3 3 <%def name="title()">
4 4 ${_('{} Files Upload').format(c.repo_name)}
5 5 %if c.rhodecode_name:
6 6 &middot; ${h.branding(c.rhodecode_name)}
7 7 %endif
8 8 </%def>
9 9
10 10 <%def name="menu_bar_nav()">
11 11 ${self.menu_items(active='repositories')}
12 12 </%def>
13 13
14 14 <%def name="breadcrumbs_links()"></%def>
15 15
16 16 <%def name="menu_bar_subnav()">
17 17 ${self.repo_menu(active='files')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21
22 22 <div class="box">
23 23 ## Template for uploads
24 24 <div style="display: none" id="tpl-dropzone">
25 25 <div class="dz-preview dz-file-preview">
26 26 <div class="dz-details">
27 27
28 28 <div class="dz-filename">
29 29 <span data-dz-name></span>
30 30 </div>
31 31 <div class="dz-filename-size">
32 32 <span class="dz-size" data-dz-size></span>
33 33
34 34 </div>
35 35
36 36 <div class="dz-sending" style="display: none">${_('Uploading...')}</div>
37 37 <div class="dz-response" style="display: none">
38 38 ${_('Uploaded')} 100%
39 39 </div>
40 40
41 41 </div>
42 42 <div class="dz-progress">
43 43 <span class="dz-upload" data-dz-uploadprogress></span>
44 44 </div>
45 45
46 46 <div class="dz-error-message">
47 47 </div>
48 48 </div>
49 49 </div>
50 50
51 51 <div class="edit-file-title">
52 52 <span class="title-heading">${_('Upload new file')} @ <code>${h.show_id(c.commit)}</code></span>
53 53 % if c.commit.branch:
54 54 <span class="tag branchtag">
55 55 <i class="icon-branch"></i> ${c.commit.branch}
56 56 </span>
57 57 % endif
58 58 </div>
59 59
60 60 <% form_url = h.route_path('repo_files_upload_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path) %>
61 61 ##${h.secure_form(form_url, id='eform', enctype="multipart/form-data", request=request)}
62 62 <div class="edit-file-fieldset">
63 63 <div class="path-items">
64 64 <ul>
65 65 <li class="breadcrumb-path">
66 66 <div>
67 67 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path='')}"><i class="icon-home"></i></a> /
68 68 <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path)}">${c.f_path}</a> ${('/' if c.f_path else '')}
69 69 </div>
70 70 </li>
71 71 <li class="location-path">
72 72
73 73 </li>
74 74 </ul>
75 75 </div>
76 76
77 77 </div>
78 78
79 79 <div class="upload-form table">
80 80 <div>
81 81
82 82 <div class="dropzone-wrapper" id="file-uploader">
83 83 <div class="dropzone-pure">
84 84 <div class="dz-message">
85 85 <i class="icon-upload" style="font-size:36px"></i></br>
86 86 ${_("Drag'n Drop files here or")} <span class="link">${_('Choose your files')}</span>.<br>
87 87 </div>
88 88 </div>
89 89
90 90 </div>
91 91 </div>
92 92
93 93 </div>
94 94
95 95 <div class="upload-form edit-file-fieldset">
96 96 <div class="fieldset">
97 97 <div class="message">
98 98 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
99 99 </div>
100 100 </div>
101 101 <div class="pull-left">
102 102 ${h.submit('commit_btn',_('Commit changes'), class_="btn btn-small btn-success")}
103 103 </div>
104 104 </div>
105 105 ##${h.end_form()}
106 106
107 107 <div class="file-upload-transaction-wrapper" style="display: none">
108 108 <div class="file-upload-transaction">
109 <h3>${_('Commiting...')}</h3>
109 <h3>${_('Committing...')}</h3>
110 110 <p>${_('Please wait while the files are being uploaded')}</p>
111 111 <p class="error" style="display: none">
112 112
113 113 </p>
114 114 <i class="icon-spin animate-spin"></i>
115 115 <p></p>
116 116 </div>
117 117 </div>
118 118
119 119 </div>
120 120
121 121 <script type="text/javascript">
122 122
123 123 $(document).ready(function () {
124 124
125 125 //see: https://www.dropzonejs.com/#configuration
126 126 myDropzone = new Dropzone("div#file-uploader", {
127 127 url: "${form_url}",
128 128 headers: {"X-CSRF-Token": CSRF_TOKEN},
129 129 paramName: function () {
130 130 return "files_upload"
131 131 }, // The name that will be used to transfer the file
132 132 parallelUploads: 20,
133 133 maxFiles: 20,
134 134 uploadMultiple: true,
135 135 //chunking: true, // use chunking transfer, not supported at the moment
136 136 //maxFilesize: 2, // in MBs
137 137 autoProcessQueue: false, // if false queue will not be processed automatically.
138 138 createImageThumbnails: false,
139 139 previewTemplate: document.querySelector('#tpl-dropzone').innerHTML,
140 140 accept: function (file, done) {
141 141 done();
142 142 },
143 143 init: function () {
144 144 this.on("addedfile", function (file) {
145 145
146 146 });
147 147
148 148 this.on("sending", function (file, xhr, formData) {
149 149 formData.append("message", $('#commit').val());
150 150 $(file.previewElement).find('.dz-sending').show();
151 151 });
152 152
153 153 this.on("success", function (file, response) {
154 154 $(file.previewElement).find('.dz-sending').hide();
155 155 $(file.previewElement).find('.dz-response').show();
156 156
157 157 if (response.error !== null) {
158 158 $('.file-upload-transaction-wrapper .error').html('ERROR: {0}'.format(response.error));
159 159 $('.file-upload-transaction-wrapper .error').show();
160 160 $('.file-upload-transaction-wrapper i').hide()
161 161 }
162 162
163 163 var redirect_url = response.redirect_url || '/';
164 164 window.location = redirect_url
165 165
166 166 });
167 167
168 168 this.on("error", function (file, errorMessage, xhr) {
169 169 var error = null;
170 170
171 171 if (xhr !== undefined){
172 172 var httpStatus = xhr.status + " " + xhr.statusText;
173 173 if (xhr !== undefined && xhr.status >= 500) {
174 174 error = httpStatus;
175 175 }
176 176 }
177 177
178 178 if (error === null) {
179 179 error = errorMessage.error || errorMessage || httpStatus;
180 180 }
181 181
182 182 $(file.previewElement).find('.dz-error-message').html('ERROR: {0}'.format(error));
183 183 });
184 184 }
185 185 });
186 186
187 187 $('#commit_btn').on('click', function(e) {
188 188 e.preventDefault();
189 189 var button = $(this);
190 190 if (button.hasClass('clicked')) {
191 191 button.attr('disabled', true);
192 192 } else {
193 193 button.addClass('clicked');
194 194 }
195 195
196 196 var files = myDropzone.getQueuedFiles();
197 197 if (files.length === 0) {
198 198 alert("Missing files");
199 199 e.preventDefault();
200 200 }
201 201
202 202 $('.upload-form').hide();
203 203 $('.file-upload-transaction-wrapper').show();
204 204 myDropzone.processQueue();
205 205
206 206 });
207 207
208 208 });
209 209
210 210 </script>
211 211 </%def>
General Comments 0
You need to be logged in to leave comments. Login now