##// END OF EJS Templates
rss: use permalinks without slashes for feeds. Fixes #5557
marcink -
r3810:d0cfe4e5 stable
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,504 +1,512 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_delete',
83 83 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/{comment_id}/delete', repo_route=True)
84 84
85 85 # still working url for backward compat.
86 86 config.add_route(
87 87 name='repo_commit_raw_deprecated',
88 88 pattern='/{repo_name:.*?[^/]}/raw-changeset/{commit_id}', repo_route=True)
89 89
90 90 # Files
91 91 config.add_route(
92 92 name='repo_archivefile',
93 93 pattern='/{repo_name:.*?[^/]}/archive/{fname:.*}', repo_route=True)
94 94
95 95 config.add_route(
96 96 name='repo_files_diff',
97 97 pattern='/{repo_name:.*?[^/]}/diff/{f_path:.*}', repo_route=True)
98 98 config.add_route( # legacy route to make old links work
99 99 name='repo_files_diff_2way_redirect',
100 100 pattern='/{repo_name:.*?[^/]}/diff-2way/{f_path:.*}', repo_route=True)
101 101
102 102 config.add_route(
103 103 name='repo_files',
104 104 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/{f_path:.*}', repo_route=True)
105 105 config.add_route(
106 106 name='repo_files:default_path',
107 107 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/', repo_route=True)
108 108 config.add_route(
109 109 name='repo_files:default_commit',
110 110 pattern='/{repo_name:.*?[^/]}/files', repo_route=True)
111 111
112 112 config.add_route(
113 113 name='repo_files:rendered',
114 114 pattern='/{repo_name:.*?[^/]}/render/{commit_id}/{f_path:.*}', repo_route=True)
115 115
116 116 config.add_route(
117 117 name='repo_files:annotated',
118 118 pattern='/{repo_name:.*?[^/]}/annotate/{commit_id}/{f_path:.*}', repo_route=True)
119 119 config.add_route(
120 120 name='repo_files:annotated_previous',
121 121 pattern='/{repo_name:.*?[^/]}/annotate-previous/{commit_id}/{f_path:.*}', repo_route=True)
122 122
123 123 config.add_route(
124 124 name='repo_nodetree_full',
125 125 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/{f_path:.*}', repo_route=True)
126 126 config.add_route(
127 127 name='repo_nodetree_full:default_path',
128 128 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/', repo_route=True)
129 129
130 130 config.add_route(
131 131 name='repo_files_nodelist',
132 132 pattern='/{repo_name:.*?[^/]}/nodelist/{commit_id}/{f_path:.*}', repo_route=True)
133 133
134 134 config.add_route(
135 135 name='repo_file_raw',
136 136 pattern='/{repo_name:.*?[^/]}/raw/{commit_id}/{f_path:.*}', repo_route=True)
137 137
138 138 config.add_route(
139 139 name='repo_file_download',
140 140 pattern='/{repo_name:.*?[^/]}/download/{commit_id}/{f_path:.*}', repo_route=True)
141 141 config.add_route( # backward compat to keep old links working
142 142 name='repo_file_download:legacy',
143 143 pattern='/{repo_name:.*?[^/]}/rawfile/{commit_id}/{f_path:.*}',
144 144 repo_route=True)
145 145
146 146 config.add_route(
147 147 name='repo_file_history',
148 148 pattern='/{repo_name:.*?[^/]}/history/{commit_id}/{f_path:.*}', repo_route=True)
149 149
150 150 config.add_route(
151 151 name='repo_file_authors',
152 152 pattern='/{repo_name:.*?[^/]}/authors/{commit_id}/{f_path:.*}', repo_route=True)
153 153
154 154 config.add_route(
155 155 name='repo_files_remove_file',
156 156 pattern='/{repo_name:.*?[^/]}/remove_file/{commit_id}/{f_path:.*}',
157 157 repo_route=True)
158 158 config.add_route(
159 159 name='repo_files_delete_file',
160 160 pattern='/{repo_name:.*?[^/]}/delete_file/{commit_id}/{f_path:.*}',
161 161 repo_route=True)
162 162 config.add_route(
163 163 name='repo_files_edit_file',
164 164 pattern='/{repo_name:.*?[^/]}/edit_file/{commit_id}/{f_path:.*}',
165 165 repo_route=True)
166 166 config.add_route(
167 167 name='repo_files_update_file',
168 168 pattern='/{repo_name:.*?[^/]}/update_file/{commit_id}/{f_path:.*}',
169 169 repo_route=True)
170 170 config.add_route(
171 171 name='repo_files_add_file',
172 172 pattern='/{repo_name:.*?[^/]}/add_file/{commit_id}/{f_path:.*}',
173 173 repo_route=True)
174 174 config.add_route(
175 175 name='repo_files_upload_file',
176 176 pattern='/{repo_name:.*?[^/]}/upload_file/{commit_id}/{f_path:.*}',
177 177 repo_route=True)
178 178 config.add_route(
179 179 name='repo_files_create_file',
180 180 pattern='/{repo_name:.*?[^/]}/create_file/{commit_id}/{f_path:.*}',
181 181 repo_route=True)
182 182
183 183 # Refs data
184 184 config.add_route(
185 185 name='repo_refs_data',
186 186 pattern='/{repo_name:.*?[^/]}/refs-data', repo_route=True)
187 187
188 188 config.add_route(
189 189 name='repo_refs_changelog_data',
190 190 pattern='/{repo_name:.*?[^/]}/refs-data-changelog', repo_route=True)
191 191
192 192 config.add_route(
193 193 name='repo_stats',
194 194 pattern='/{repo_name:.*?[^/]}/repo_stats/{commit_id}', repo_route=True)
195 195
196 196 # Commits
197 197 config.add_route(
198 198 name='repo_commits',
199 199 pattern='/{repo_name:.*?[^/]}/commits', repo_route=True)
200 200 config.add_route(
201 201 name='repo_commits_file',
202 202 pattern='/{repo_name:.*?[^/]}/commits/{commit_id}/{f_path:.*}', repo_route=True)
203 203 config.add_route(
204 204 name='repo_commits_elements',
205 205 pattern='/{repo_name:.*?[^/]}/commits_elements', repo_route=True)
206 206 config.add_route(
207 207 name='repo_commits_elements_file',
208 208 pattern='/{repo_name:.*?[^/]}/commits_elements/{commit_id}/{f_path:.*}', repo_route=True)
209 209
210 210 # Changelog (old deprecated name for commits page)
211 211 config.add_route(
212 212 name='repo_changelog',
213 213 pattern='/{repo_name:.*?[^/]}/changelog', repo_route=True)
214 214 config.add_route(
215 215 name='repo_changelog_file',
216 216 pattern='/{repo_name:.*?[^/]}/changelog/{commit_id}/{f_path:.*}', repo_route=True)
217 217
218 218 # Compare
219 219 config.add_route(
220 220 name='repo_compare_select',
221 221 pattern='/{repo_name:.*?[^/]}/compare', repo_route=True)
222 222
223 223 config.add_route(
224 224 name='repo_compare',
225 225 pattern='/{repo_name:.*?[^/]}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}', repo_route=True)
226 226
227 227 # Tags
228 228 config.add_route(
229 229 name='tags_home',
230 230 pattern='/{repo_name:.*?[^/]}/tags', repo_route=True)
231 231
232 232 # Branches
233 233 config.add_route(
234 234 name='branches_home',
235 235 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
236 236
237 237 # Bookmarks
238 238 config.add_route(
239 239 name='bookmarks_home',
240 240 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
241 241
242 242 # Forks
243 243 config.add_route(
244 244 name='repo_fork_new',
245 245 pattern='/{repo_name:.*?[^/]}/fork', repo_route=True,
246 246 repo_forbid_when_archived=True,
247 247 repo_accepted_types=['hg', 'git'])
248 248
249 249 config.add_route(
250 250 name='repo_fork_create',
251 251 pattern='/{repo_name:.*?[^/]}/fork/create', repo_route=True,
252 252 repo_forbid_when_archived=True,
253 253 repo_accepted_types=['hg', 'git'])
254 254
255 255 config.add_route(
256 256 name='repo_forks_show_all',
257 257 pattern='/{repo_name:.*?[^/]}/forks', repo_route=True,
258 258 repo_accepted_types=['hg', 'git'])
259 259 config.add_route(
260 260 name='repo_forks_data',
261 261 pattern='/{repo_name:.*?[^/]}/forks/data', repo_route=True,
262 262 repo_accepted_types=['hg', 'git'])
263 263
264 264 # Pull Requests
265 265 config.add_route(
266 266 name='pullrequest_show',
267 267 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}',
268 268 repo_route=True)
269 269
270 270 config.add_route(
271 271 name='pullrequest_show_all',
272 272 pattern='/{repo_name:.*?[^/]}/pull-request',
273 273 repo_route=True, repo_accepted_types=['hg', 'git'])
274 274
275 275 config.add_route(
276 276 name='pullrequest_show_all_data',
277 277 pattern='/{repo_name:.*?[^/]}/pull-request-data',
278 278 repo_route=True, repo_accepted_types=['hg', 'git'])
279 279
280 280 config.add_route(
281 281 name='pullrequest_repo_refs',
282 282 pattern='/{repo_name:.*?[^/]}/pull-request/refs/{target_repo_name:.*?[^/]}',
283 283 repo_route=True)
284 284
285 285 config.add_route(
286 286 name='pullrequest_repo_targets',
287 287 pattern='/{repo_name:.*?[^/]}/pull-request/repo-targets',
288 288 repo_route=True)
289 289
290 290 config.add_route(
291 291 name='pullrequest_new',
292 292 pattern='/{repo_name:.*?[^/]}/pull-request/new',
293 293 repo_route=True, repo_accepted_types=['hg', 'git'],
294 294 repo_forbid_when_archived=True)
295 295
296 296 config.add_route(
297 297 name='pullrequest_create',
298 298 pattern='/{repo_name:.*?[^/]}/pull-request/create',
299 299 repo_route=True, repo_accepted_types=['hg', 'git'],
300 300 repo_forbid_when_archived=True)
301 301
302 302 config.add_route(
303 303 name='pullrequest_update',
304 304 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/update',
305 305 repo_route=True, repo_forbid_when_archived=True)
306 306
307 307 config.add_route(
308 308 name='pullrequest_merge',
309 309 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/merge',
310 310 repo_route=True, repo_forbid_when_archived=True)
311 311
312 312 config.add_route(
313 313 name='pullrequest_delete',
314 314 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/delete',
315 315 repo_route=True, repo_forbid_when_archived=True)
316 316
317 317 config.add_route(
318 318 name='pullrequest_comment_create',
319 319 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment',
320 320 repo_route=True)
321 321
322 322 config.add_route(
323 323 name='pullrequest_comment_delete',
324 324 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment/{comment_id}/delete',
325 325 repo_route=True, repo_accepted_types=['hg', 'git'])
326 326
327 327 # Artifacts, (EE feature)
328 328 config.add_route(
329 329 name='repo_artifacts_list',
330 330 pattern='/{repo_name:.*?[^/]}/artifacts', repo_route=True)
331 331
332 332 # Settings
333 333 config.add_route(
334 334 name='edit_repo',
335 335 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
336 336 # update is POST on edit_repo
337 337
338 338 # Settings advanced
339 339 config.add_route(
340 340 name='edit_repo_advanced',
341 341 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
342 342 config.add_route(
343 343 name='edit_repo_advanced_archive',
344 344 pattern='/{repo_name:.*?[^/]}/settings/advanced/archive', repo_route=True)
345 345 config.add_route(
346 346 name='edit_repo_advanced_delete',
347 347 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
348 348 config.add_route(
349 349 name='edit_repo_advanced_locking',
350 350 pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True)
351 351 config.add_route(
352 352 name='edit_repo_advanced_journal',
353 353 pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True)
354 354 config.add_route(
355 355 name='edit_repo_advanced_fork',
356 356 pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True)
357 357
358 358 config.add_route(
359 359 name='edit_repo_advanced_hooks',
360 360 pattern='/{repo_name:.*?[^/]}/settings/advanced/hooks', repo_route=True)
361 361
362 362 # Caches
363 363 config.add_route(
364 364 name='edit_repo_caches',
365 365 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
366 366
367 367 # Permissions
368 368 config.add_route(
369 369 name='edit_repo_perms',
370 370 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
371 371
372 372 config.add_route(
373 373 name='edit_repo_perms_set_private',
374 374 pattern='/{repo_name:.*?[^/]}/settings/permissions/set_private', repo_route=True)
375 375
376 376 # Permissions Branch (EE feature)
377 377 config.add_route(
378 378 name='edit_repo_perms_branch',
379 379 pattern='/{repo_name:.*?[^/]}/settings/branch_permissions', repo_route=True)
380 380 config.add_route(
381 381 name='edit_repo_perms_branch_delete',
382 382 pattern='/{repo_name:.*?[^/]}/settings/branch_permissions/{rule_id}/delete',
383 383 repo_route=True)
384 384
385 385 # Maintenance
386 386 config.add_route(
387 387 name='edit_repo_maintenance',
388 388 pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
389 389
390 390 config.add_route(
391 391 name='edit_repo_maintenance_execute',
392 392 pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
393 393
394 394 # Fields
395 395 config.add_route(
396 396 name='edit_repo_fields',
397 397 pattern='/{repo_name:.*?[^/]}/settings/fields', repo_route=True)
398 398 config.add_route(
399 399 name='edit_repo_fields_create',
400 400 pattern='/{repo_name:.*?[^/]}/settings/fields/create', repo_route=True)
401 401 config.add_route(
402 402 name='edit_repo_fields_delete',
403 403 pattern='/{repo_name:.*?[^/]}/settings/fields/{field_id}/delete', repo_route=True)
404 404
405 405 # Locking
406 406 config.add_route(
407 407 name='repo_edit_toggle_locking',
408 408 pattern='/{repo_name:.*?[^/]}/settings/toggle_locking', repo_route=True)
409 409
410 410 # Remote
411 411 config.add_route(
412 412 name='edit_repo_remote',
413 413 pattern='/{repo_name:.*?[^/]}/settings/remote', repo_route=True)
414 414 config.add_route(
415 415 name='edit_repo_remote_pull',
416 416 pattern='/{repo_name:.*?[^/]}/settings/remote/pull', repo_route=True)
417 417 config.add_route(
418 418 name='edit_repo_remote_push',
419 419 pattern='/{repo_name:.*?[^/]}/settings/remote/push', repo_route=True)
420 420
421 421 # Statistics
422 422 config.add_route(
423 423 name='edit_repo_statistics',
424 424 pattern='/{repo_name:.*?[^/]}/settings/statistics', repo_route=True)
425 425 config.add_route(
426 426 name='edit_repo_statistics_reset',
427 427 pattern='/{repo_name:.*?[^/]}/settings/statistics/update', repo_route=True)
428 428
429 429 # Issue trackers
430 430 config.add_route(
431 431 name='edit_repo_issuetracker',
432 432 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers', repo_route=True)
433 433 config.add_route(
434 434 name='edit_repo_issuetracker_test',
435 435 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/test', repo_route=True)
436 436 config.add_route(
437 437 name='edit_repo_issuetracker_delete',
438 438 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/delete', repo_route=True)
439 439 config.add_route(
440 440 name='edit_repo_issuetracker_update',
441 441 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/update', repo_route=True)
442 442
443 443 # VCS Settings
444 444 config.add_route(
445 445 name='edit_repo_vcs',
446 446 pattern='/{repo_name:.*?[^/]}/settings/vcs', repo_route=True)
447 447 config.add_route(
448 448 name='edit_repo_vcs_update',
449 449 pattern='/{repo_name:.*?[^/]}/settings/vcs/update', repo_route=True)
450 450
451 451 # svn pattern
452 452 config.add_route(
453 453 name='edit_repo_vcs_svn_pattern_delete',
454 454 pattern='/{repo_name:.*?[^/]}/settings/vcs/svn_pattern/delete', repo_route=True)
455 455
456 456 # Repo Review Rules (EE feature)
457 457 config.add_route(
458 458 name='repo_reviewers',
459 459 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
460 460
461 461 config.add_route(
462 462 name='repo_default_reviewers_data',
463 463 pattern='/{repo_name:.*?[^/]}/settings/review/default-reviewers', repo_route=True)
464 464
465 465 # Repo Automation (EE feature)
466 466 config.add_route(
467 467 name='repo_automation',
468 468 pattern='/{repo_name:.*?[^/]}/settings/automation', repo_route=True)
469 469
470 470 # Strip
471 471 config.add_route(
472 472 name='edit_repo_strip',
473 473 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
474 474
475 475 config.add_route(
476 476 name='strip_check',
477 477 pattern='/{repo_name:.*?[^/]}/settings/strip_check', repo_route=True)
478 478
479 479 config.add_route(
480 480 name='strip_execute',
481 481 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
482 482
483 483 # Audit logs
484 484 config.add_route(
485 485 name='edit_repo_audit_logs',
486 486 pattern='/{repo_name:.*?[^/]}/settings/audit_logs', repo_route=True)
487 487
488 # ATOM/RSS Feed
488 # ATOM/RSS Feed, shouldn't contain slashes for outlook compatibility
489 489 config.add_route(
490 490 name='rss_feed_home',
491 pattern='/{repo_name:.*?[^/]}/feed-rss', repo_route=True)
492
493 config.add_route(
494 name='atom_feed_home',
495 pattern='/{repo_name:.*?[^/]}/feed-atom', repo_route=True)
496
497 config.add_route(
498 name='rss_feed_home_old',
491 499 pattern='/{repo_name:.*?[^/]}/feed/rss', repo_route=True)
492 500
493 501 config.add_route(
494 name='atom_feed_home',
502 name='atom_feed_home_old',
495 503 pattern='/{repo_name:.*?[^/]}/feed/atom', repo_route=True)
496 504
497 505 # NOTE(marcink): needs to be at the end for catch-all
498 506 add_route_with_slash(
499 507 config,
500 508 name='repo_summary',
501 509 pattern='/{repo_name:.*?[^/]}', repo_route=True)
502 510
503 511 # Scan module for configuration decorators.
504 512 config.scan('.views', ignore='.tests')
@@ -1,99 +1,137 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-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 pytest
22 22 from rhodecode.model.auth_token import AuthTokenModel
23 23 from rhodecode.tests import TestController
24 24
25 25
26 26 def route_path(name, params=None, **kwargs):
27 27 import urllib
28 28
29 29 base_url = {
30 'rss_feed_home': '/{repo_name}/feed/rss',
31 'atom_feed_home': '/{repo_name}/feed/atom',
30 'rss_feed_home': '/{repo_name}/feed-rss',
31 'atom_feed_home': '/{repo_name}/feed-atom',
32 'rss_feed_home_old': '/{repo_name}/feed/rss',
33 'atom_feed_home_old': '/{repo_name}/feed/atom',
32 34 }[name].format(**kwargs)
33 35
34 36 if params:
35 37 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
36 38 return base_url
37 39
38 40
39 41 class TestFeedView(TestController):
40 42
41 43 @pytest.mark.parametrize("feed_type,response_types,content_type",[
42 44 ('rss', ['<rss version="2.0">'],
43 45 "application/rss+xml"),
44 46 ('atom', ['xmlns="http://www.w3.org/2005/Atom"', 'xml:lang="en-us"'],
45 47 "application/atom+xml"),
46 48 ])
47 49 def test_feed(self, backend, feed_type, response_types, content_type):
48 50 self.log_user()
49 51 response = self.app.get(
50 52 route_path('{}_feed_home'.format(feed_type),
51 53 repo_name=backend.repo_name))
52 54
53 55 for content in response_types:
54 56 response.mustcontain(content)
55 57
56 58 assert response.content_type == content_type
57 59
58 60 @pytest.mark.parametrize("feed_type, content_type", [
59 61 ('rss', "application/rss+xml"),
60 62 ('atom', "application/atom+xml")
61 63 ])
62 64 def test_feed_with_auth_token(
63 65 self, backend, user_admin, feed_type, content_type):
64 66 auth_token = user_admin.feed_token
65 67 assert auth_token != ''
66 68
67 69 response = self.app.get(
68 70 route_path(
69 71 '{}_feed_home'.format(feed_type),
70 72 repo_name=backend.repo_name,
71 73 params=dict(auth_token=auth_token)),
72 74 status=200)
73 75
74 76 assert response.content_type == content_type
75 77
78 @pytest.mark.parametrize("feed_type, content_type", [
79 ('rss', "application/rss+xml"),
80 ('atom', "application/atom+xml")
81 ])
82 def test_feed_with_auth_token_by_uid(
83 self, backend, user_admin, feed_type, content_type):
84 auth_token = user_admin.feed_token
85 assert auth_token != ''
86
87 response = self.app.get(
88 route_path(
89 '{}_feed_home'.format(feed_type),
90 repo_name='_{}'.format(backend.repo.repo_id),
91 params=dict(auth_token=auth_token)),
92 status=200)
93
94 assert response.content_type == content_type
95
96 @pytest.mark.parametrize("feed_type, content_type", [
97 ('rss', "application/rss+xml"),
98 ('atom', "application/atom+xml")
99 ])
100 def test_feed_old_urls_with_auth_token(
101 self, backend, user_admin, feed_type, content_type):
102 auth_token = user_admin.feed_token
103 assert auth_token != ''
104
105 response = self.app.get(
106 route_path(
107 '{}_feed_home_old'.format(feed_type),
108 repo_name=backend.repo_name,
109 params=dict(auth_token=auth_token)),
110 status=200)
111
112 assert response.content_type == content_type
113
76 114 @pytest.mark.parametrize("feed_type", ['rss', 'atom'])
77 115 def test_feed_with_auth_token_of_wrong_type(
78 116 self, backend, user_util, feed_type):
79 117 user = user_util.create_user()
80 118 auth_token = AuthTokenModel().create(
81 119 user.user_id, 'test-token', -1, AuthTokenModel.cls.ROLE_API)
82 120 auth_token = auth_token.api_key
83 121
84 122 self.app.get(
85 123 route_path(
86 124 '{}_feed_home'.format(feed_type),
87 125 repo_name=backend.repo_name,
88 126 params=dict(auth_token=auth_token)),
89 127 status=302)
90 128
91 129 auth_token = AuthTokenModel().create(
92 130 user.user_id, 'test-token', -1, AuthTokenModel.cls.ROLE_FEED)
93 131 auth_token = auth_token.api_key
94 132 self.app.get(
95 133 route_path(
96 134 '{}_feed_home'.format(feed_type),
97 135 repo_name=backend.repo_name,
98 136 params=dict(auth_token=auth_token)),
99 137 status=200)
@@ -1,238 +1,242 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2017-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 import pytz
21 21 import logging
22 22
23 23 from pyramid.view import view_config
24 24 from pyramid.response import Response
25 25 from webhelpers.feedgenerator import Rss201rev2Feed, Atom1Feed
26 26
27 27 from rhodecode.apps._base import RepoAppView
28 28 from rhodecode.lib import audit_logger
29 29 from rhodecode.lib import rc_cache
30 30 from rhodecode.lib import helpers as h
31 31 from rhodecode.lib.auth import (
32 32 LoginRequired, HasRepoPermissionAnyDecorator)
33 33 from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer
34 34 from rhodecode.lib.utils2 import str2bool, safe_int, md5_safe
35 35 from rhodecode.model.db import UserApiKeys, CacheKey
36 36
37 37 log = logging.getLogger(__name__)
38 38
39 39
40 40 class RepoFeedView(RepoAppView):
41 41 def load_default_context(self):
42 42 c = self._get_local_tmpl_context()
43
44
45 43 self._load_defaults()
46 44 return c
47 45
48 46 def _get_config(self):
49 47 import rhodecode
50 48 config = rhodecode.CONFIG
51 49
52 50 return {
53 51 'language': 'en-us',
54 52 'feed_ttl': '5', # TTL of feed,
55 53 'feed_include_diff':
56 54 str2bool(config.get('rss_include_diff', False)),
57 55 'feed_items_per_page':
58 56 safe_int(config.get('rss_items_per_page', 20)),
59 57 'feed_diff_limit':
60 58 # we need to protect from parsing huge diffs here other way
61 59 # we can kill the server
62 60 safe_int(config.get('rss_cut_off_limit', 32 * 1024)),
63 61 }
64 62
65 63 def _load_defaults(self):
66 64 _ = self.request.translate
67 65 config = self._get_config()
68 66 # common values for feeds
69 67 self.description = _('Changes on %s repository')
70 68 self.title = self.title = _('%s %s feed') % (self.db_repo_name, '%s')
71 69 self.language = config["language"]
72 70 self.ttl = config["feed_ttl"]
73 71 self.feed_include_diff = config['feed_include_diff']
74 72 self.feed_diff_limit = config['feed_diff_limit']
75 73 self.feed_items_per_page = config['feed_items_per_page']
76 74
77 75 def _changes(self, commit):
78 76 diff_processor = DiffProcessor(
79 77 commit.diff(), diff_limit=self.feed_diff_limit)
80 78 _parsed = diff_processor.prepare(inline_diff=False)
81 79 limited_diff = isinstance(_parsed, LimitedDiffContainer)
82 80
83 81 return diff_processor, _parsed, limited_diff
84 82
85 83 def _get_title(self, commit):
86 84 return h.shorter(commit.message, 160)
87 85
88 86 def _get_description(self, commit):
89 87 _renderer = self.request.get_partial_renderer(
90 88 'rhodecode:templates/feed/atom_feed_entry.mako')
91 89 diff_processor, parsed_diff, limited_diff = self._changes(commit)
92 90 filtered_parsed_diff, has_hidden_changes = self.path_filter.filter_patchset(parsed_diff)
93 91 return _renderer(
94 92 'body',
95 93 commit=commit,
96 94 parsed_diff=filtered_parsed_diff,
97 95 limited_diff=limited_diff,
98 96 feed_include_diff=self.feed_include_diff,
99 97 diff_processor=diff_processor,
100 98 has_hidden_changes=has_hidden_changes
101 99 )
102 100
103 101 def _set_timezone(self, date, tzinfo=pytz.utc):
104 102 if not getattr(date, "tzinfo", None):
105 103 date.replace(tzinfo=tzinfo)
106 104 return date
107 105
108 106 def _get_commits(self):
109 107 return list(self.rhodecode_vcs_repo[-self.feed_items_per_page:])
110 108
111 109 def uid(self, repo_id, commit_id):
112 110 return '{}:{}'.format(md5_safe(repo_id), md5_safe(commit_id))
113 111
114 112 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
115 113 @HasRepoPermissionAnyDecorator(
116 114 'repository.read', 'repository.write', 'repository.admin')
117 115 @view_config(
118 116 route_name='atom_feed_home', request_method='GET',
119 117 renderer=None)
118 @view_config(
119 route_name='atom_feed_home_old', request_method='GET',
120 renderer=None)
120 121 def atom(self):
121 122 """
122 123 Produce an atom-1.0 feed via feedgenerator module
123 124 """
124 125 self.load_default_context()
125 126
126 127 cache_namespace_uid = 'cache_repo_instance.{}_{}'.format(
127 128 self.db_repo.repo_id, CacheKey.CACHE_TYPE_FEED)
128 129 invalidation_namespace = CacheKey.REPO_INVALIDATION_NAMESPACE.format(
129 130 repo_id=self.db_repo.repo_id)
130 131
131 132 region = rc_cache.get_or_create_region('cache_repo_longterm',
132 133 cache_namespace_uid)
133 134
134 135 condition = not self.path_filter.is_enabled
135 136
136 137 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
137 138 condition=condition)
138 139 def generate_atom_feed(repo_id, _repo_name, _feed_type):
139 140 feed = Atom1Feed(
140 141 title=self.title % _repo_name,
141 142 link=h.route_url('repo_summary', repo_name=_repo_name),
142 143 description=self.description % _repo_name,
143 144 language=self.language,
144 145 ttl=self.ttl
145 146 )
146 147
147 148 for commit in reversed(self._get_commits()):
148 149 date = self._set_timezone(commit.date)
149 150 feed.add_item(
150 151 unique_id=self.uid(repo_id, commit.raw_id),
151 152 title=self._get_title(commit),
152 153 author_name=commit.author,
153 154 description=self._get_description(commit),
154 155 link=h.route_url(
155 156 'repo_commit', repo_name=_repo_name,
156 157 commit_id=commit.raw_id),
157 158 pubdate=date,)
158 159
159 160 return feed.mime_type, feed.writeString('utf-8')
160 161
161 162 inv_context_manager = rc_cache.InvalidationContext(
162 163 uid=cache_namespace_uid, invalidation_namespace=invalidation_namespace)
163 164 with inv_context_manager as invalidation_context:
164 165 args = (self.db_repo.repo_id, self.db_repo.repo_name, 'atom',)
165 166 # re-compute and store cache if we get invalidate signal
166 167 if invalidation_context.should_invalidate():
167 168 mime_type, feed = generate_atom_feed.refresh(*args)
168 169 else:
169 170 mime_type, feed = generate_atom_feed(*args)
170 171
171 172 log.debug('Repo ATOM feed computed in %.3fs',
172 173 inv_context_manager.compute_time)
173 174
174 175 response = Response(feed)
175 176 response.content_type = mime_type
176 177 return response
177 178
178 179 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
179 180 @HasRepoPermissionAnyDecorator(
180 181 'repository.read', 'repository.write', 'repository.admin')
181 182 @view_config(
182 183 route_name='rss_feed_home', request_method='GET',
183 184 renderer=None)
185 @view_config(
186 route_name='rss_feed_home_old', request_method='GET',
187 renderer=None)
184 188 def rss(self):
185 189 """
186 190 Produce an rss2 feed via feedgenerator module
187 191 """
188 192 self.load_default_context()
189 193
190 194 cache_namespace_uid = 'cache_repo_instance.{}_{}'.format(
191 195 self.db_repo.repo_id, CacheKey.CACHE_TYPE_FEED)
192 196 invalidation_namespace = CacheKey.REPO_INVALIDATION_NAMESPACE.format(
193 197 repo_id=self.db_repo.repo_id)
194 198 region = rc_cache.get_or_create_region('cache_repo_longterm',
195 199 cache_namespace_uid)
196 200
197 201 condition = not self.path_filter.is_enabled
198 202
199 203 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
200 204 condition=condition)
201 205 def generate_rss_feed(repo_id, _repo_name, _feed_type):
202 206 feed = Rss201rev2Feed(
203 207 title=self.title % _repo_name,
204 208 link=h.route_url('repo_summary', repo_name=_repo_name),
205 209 description=self.description % _repo_name,
206 210 language=self.language,
207 211 ttl=self.ttl
208 212 )
209 213
210 214 for commit in reversed(self._get_commits()):
211 215 date = self._set_timezone(commit.date)
212 216 feed.add_item(
213 217 unique_id=self.uid(repo_id, commit.raw_id),
214 218 title=self._get_title(commit),
215 219 author_name=commit.author,
216 220 description=self._get_description(commit),
217 221 link=h.route_url(
218 222 'repo_commit', repo_name=_repo_name,
219 223 commit_id=commit.raw_id),
220 224 pubdate=date,)
221 225
222 226 return feed.mime_type, feed.writeString('utf-8')
223 227
224 228 inv_context_manager = rc_cache.InvalidationContext(
225 229 uid=cache_namespace_uid, invalidation_namespace=invalidation_namespace)
226 230 with inv_context_manager as invalidation_context:
227 231 args = (self.db_repo.repo_id, self.db_repo.repo_name, 'rss',)
228 232 # re-compute and store cache if we get invalidate signal
229 233 if invalidation_context.should_invalidate():
230 234 mime_type, feed = generate_rss_feed.refresh(*args)
231 235 else:
232 236 mime_type, feed = generate_rss_feed(*args)
233 237 log.debug(
234 238 'Repo RSS feed computed in %.3fs', inv_context_manager.compute_time)
235 239
236 240 response = Response(feed)
237 241 response.content_type = mime_type
238 242 return response
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,376 +1,378 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('global_integrations_new', '/_admin/integrations/new', []);
18 18 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
19 19 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
20 20 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
21 21 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
22 22 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
23 23 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
24 24 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
25 25 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/_settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
26 26 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/_settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
27 27 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
28 28 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
29 29 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
30 30 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
31 31 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
32 32 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
33 33 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
34 34 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
35 35 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
36 36 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
37 37 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
38 38 pyroutes.register('admin_home', '/_admin', []);
39 39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
40 40 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
41 41 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
42 42 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
43 43 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
44 44 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
45 45 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
46 46 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
47 47 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
48 48 pyroutes.register('admin_settings_exception_tracker', '/_admin/settings/exceptions', []);
49 49 pyroutes.register('admin_settings_exception_tracker_delete_all', '/_admin/settings/exceptions/delete', []);
50 50 pyroutes.register('admin_settings_exception_tracker_show', '/_admin/settings/exceptions/%(exception_id)s', ['exception_id']);
51 51 pyroutes.register('admin_settings_exception_tracker_delete', '/_admin/settings/exceptions/%(exception_id)s/delete', ['exception_id']);
52 52 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
53 53 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
54 54 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
55 55 pyroutes.register('admin_settings_process_management_data', '/_admin/settings/process_management/data', []);
56 56 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
57 57 pyroutes.register('admin_settings_process_management_master_signal', '/_admin/settings/process_management/master_signal', []);
58 58 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
59 59 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
60 60 pyroutes.register('admin_settings', '/_admin/settings', []);
61 61 pyroutes.register('admin_settings_update', '/_admin/settings/update', []);
62 62 pyroutes.register('admin_settings_global', '/_admin/settings/global', []);
63 63 pyroutes.register('admin_settings_global_update', '/_admin/settings/global/update', []);
64 64 pyroutes.register('admin_settings_vcs', '/_admin/settings/vcs', []);
65 65 pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []);
66 66 pyroutes.register('admin_settings_vcs_svn_pattern_delete', '/_admin/settings/vcs/svn_pattern_delete', []);
67 67 pyroutes.register('admin_settings_mapping', '/_admin/settings/mapping', []);
68 68 pyroutes.register('admin_settings_mapping_update', '/_admin/settings/mapping/update', []);
69 69 pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []);
70 70 pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []);
71 71 pyroutes.register('admin_settings_issuetracker', '/_admin/settings/issue-tracker', []);
72 72 pyroutes.register('admin_settings_issuetracker_update', '/_admin/settings/issue-tracker/update', []);
73 73 pyroutes.register('admin_settings_issuetracker_test', '/_admin/settings/issue-tracker/test', []);
74 74 pyroutes.register('admin_settings_issuetracker_delete', '/_admin/settings/issue-tracker/delete', []);
75 75 pyroutes.register('admin_settings_email', '/_admin/settings/email', []);
76 76 pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []);
77 77 pyroutes.register('admin_settings_hooks', '/_admin/settings/hooks', []);
78 78 pyroutes.register('admin_settings_hooks_update', '/_admin/settings/hooks/update', []);
79 79 pyroutes.register('admin_settings_hooks_delete', '/_admin/settings/hooks/delete', []);
80 80 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
81 81 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
82 82 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
83 83 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
84 84 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
85 85 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
86 86 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
87 87 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
88 88 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
89 89 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
90 90 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
91 91 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
92 92 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
93 93 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
94 94 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
95 95 pyroutes.register('users', '/_admin/users', []);
96 96 pyroutes.register('users_data', '/_admin/users_data', []);
97 97 pyroutes.register('users_create', '/_admin/users/create', []);
98 98 pyroutes.register('users_new', '/_admin/users/new', []);
99 99 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
100 100 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
101 101 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
102 102 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
103 103 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
104 104 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
105 105 pyroutes.register('user_enable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_enable', ['user_id']);
106 106 pyroutes.register('user_disable_force_password_reset', '/_admin/users/%(user_id)s/password_reset_disable', ['user_id']);
107 107 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
108 108 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
109 109 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
110 110 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
111 111 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
112 112 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
113 113 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
114 114 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
115 115 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
116 116 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
117 117 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
118 118 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
119 119 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
120 120 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
121 121 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
122 122 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
123 123 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
124 124 pyroutes.register('edit_user_caches', '/_admin/users/%(user_id)s/edit/caches', ['user_id']);
125 125 pyroutes.register('edit_user_caches_update', '/_admin/users/%(user_id)s/edit/caches/update', ['user_id']);
126 126 pyroutes.register('user_groups', '/_admin/user_groups', []);
127 127 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
128 128 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
129 129 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
130 130 pyroutes.register('repos', '/_admin/repos', []);
131 131 pyroutes.register('repo_new', '/_admin/repos/new', []);
132 132 pyroutes.register('repo_create', '/_admin/repos/create', []);
133 133 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
134 134 pyroutes.register('repo_groups_data', '/_admin/repo_groups_data', []);
135 135 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
136 136 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
137 137 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
138 138 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
139 139 pyroutes.register('channelstream_proxy', '/_channelstream', []);
140 140 pyroutes.register('upload_file', '/_file_store/upload', []);
141 141 pyroutes.register('download_file', '/_file_store/download/%(fid)s', ['fid']);
142 142 pyroutes.register('logout', '/_admin/logout', []);
143 143 pyroutes.register('reset_password', '/_admin/password_reset', []);
144 144 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
145 145 pyroutes.register('home', '/', []);
146 146 pyroutes.register('user_autocomplete_data', '/_users', []);
147 147 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
148 148 pyroutes.register('repo_list_data', '/_repos', []);
149 149 pyroutes.register('repo_group_list_data', '/_repo_groups', []);
150 150 pyroutes.register('goto_switcher_data', '/_goto_data', []);
151 151 pyroutes.register('markup_preview', '/_markup_preview', []);
152 152 pyroutes.register('file_preview', '/_file_preview', []);
153 153 pyroutes.register('store_user_session_value', '/_store_session_attr', []);
154 154 pyroutes.register('journal', '/_admin/journal', []);
155 155 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
156 156 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
157 157 pyroutes.register('journal_public', '/_admin/public_journal', []);
158 158 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
159 159 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
160 160 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
161 161 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
162 162 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
163 163 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
164 164 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
165 165 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
166 166 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
167 167 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
168 168 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
169 169 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
170 170 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
171 171 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
172 172 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
173 173 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
174 174 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
175 175 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
176 176 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
177 177 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
178 178 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
179 179 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
180 180 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
181 181 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
182 182 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
183 183 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
184 184 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
185 185 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
186 186 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
187 187 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
188 188 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
189 189 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
190 190 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
191 191 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
192 192 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
193 193 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
194 194 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
195 195 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
196 196 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
197 197 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
198 198 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
199 199 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
200 200 pyroutes.register('repo_files_upload_file', '/%(repo_name)s/upload_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
201 201 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
202 202 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
203 203 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
204 204 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
205 205 pyroutes.register('repo_commits', '/%(repo_name)s/commits', ['repo_name']);
206 206 pyroutes.register('repo_commits_file', '/%(repo_name)s/commits/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
207 207 pyroutes.register('repo_commits_elements', '/%(repo_name)s/commits_elements', ['repo_name']);
208 208 pyroutes.register('repo_commits_elements_file', '/%(repo_name)s/commits_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
209 209 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
210 210 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
211 211 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
212 212 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']);
213 213 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
214 214 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
215 215 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
216 216 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
217 217 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
218 218 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
219 219 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
220 220 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
221 221 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
222 222 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
223 223 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
224 224 pyroutes.register('pullrequest_repo_targets', '/%(repo_name)s/pull-request/repo-targets', ['repo_name']);
225 225 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
226 226 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
227 227 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
228 228 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
229 229 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
230 230 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
231 231 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']);
232 232 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
233 233 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
234 234 pyroutes.register('edit_repo_advanced_archive', '/%(repo_name)s/settings/advanced/archive', ['repo_name']);
235 235 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
236 236 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
237 237 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
238 238 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
239 239 pyroutes.register('edit_repo_advanced_hooks', '/%(repo_name)s/settings/advanced/hooks', ['repo_name']);
240 240 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
241 241 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
242 242 pyroutes.register('edit_repo_perms_set_private', '/%(repo_name)s/settings/permissions/set_private', ['repo_name']);
243 243 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
244 244 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
245 245 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
246 246 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
247 247 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
248 248 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
249 249 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
250 250 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
251 251 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
252 252 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
253 253 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
254 254 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
255 255 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
256 256 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
257 257 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
258 258 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
259 259 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
260 260 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
261 261 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
262 262 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
263 263 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
264 264 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
265 265 pyroutes.register('edit_repo_audit_logs', '/%(repo_name)s/settings/audit_logs', ['repo_name']);
266 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
267 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
266 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed-rss', ['repo_name']);
267 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed-atom', ['repo_name']);
268 pyroutes.register('rss_feed_home_old', '/%(repo_name)s/feed/rss', ['repo_name']);
269 pyroutes.register('atom_feed_home_old', '/%(repo_name)s/feed/atom', ['repo_name']);
268 270 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
269 271 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
270 272 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
271 273 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
272 274 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
273 275 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
274 276 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
275 277 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
276 278 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
277 279 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
278 280 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
279 281 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
280 282 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
281 283 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
282 284 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
283 285 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
284 286 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
285 287 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
286 288 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
287 289 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
288 290 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
289 291 pyroutes.register('search', '/_admin/search', []);
290 292 pyroutes.register('search_repo', '/%(repo_name)s/_search', ['repo_name']);
291 293 pyroutes.register('search_repo_alt', '/%(repo_name)s/search', ['repo_name']);
292 294 pyroutes.register('search_repo_group', '/%(repo_group_name)s/_search', ['repo_group_name']);
293 295 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
294 296 pyroutes.register('user_group_profile', '/_profile_user_group/%(user_group_name)s', ['user_group_name']);
295 297 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
296 298 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
297 299 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
298 300 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
299 301 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
300 302 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
301 303 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
302 304 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
303 305 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
304 306 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
305 307 pyroutes.register('my_account_user_group_membership', '/_admin/my_account/user_group_membership', []);
306 308 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
307 309 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
308 310 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
309 311 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
310 312 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
311 313 pyroutes.register('my_account_bookmarks', '/_admin/my_account/bookmarks', []);
312 314 pyroutes.register('my_account_bookmarks_update', '/_admin/my_account/bookmarks/update', []);
313 315 pyroutes.register('my_account_goto_bookmark', '/_admin/my_account/bookmark/%(bookmark_id)s', ['bookmark_id']);
314 316 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
315 317 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
316 318 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
317 319 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
318 320 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
319 321 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
320 322 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
321 323 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
322 324 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
323 325 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
324 326 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
325 327 pyroutes.register('gists_show', '/_admin/gists', []);
326 328 pyroutes.register('gists_new', '/_admin/gists/new', []);
327 329 pyroutes.register('gists_create', '/_admin/gists/create', []);
328 330 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
329 331 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
330 332 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
331 333 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
332 334 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
333 335 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
334 336 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
335 337 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
336 338 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
337 339 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
338 340 pyroutes.register('apiv2', '/_admin/api', []);
339 341 pyroutes.register('admin_settings_license', '/_admin/settings/license', []);
340 342 pyroutes.register('admin_settings_license_unlock', '/_admin/settings/license_unlock', []);
341 343 pyroutes.register('login', '/_admin/login', []);
342 344 pyroutes.register('register', '/_admin/register', []);
343 345 pyroutes.register('repo_reviewers_review_rule_new', '/%(repo_name)s/settings/review/rules/new', ['repo_name']);
344 346 pyroutes.register('repo_reviewers_review_rule_edit', '/%(repo_name)s/settings/review/rules/%(rule_id)s', ['repo_name', 'rule_id']);
345 347 pyroutes.register('repo_reviewers_review_rule_delete', '/%(repo_name)s/settings/review/rules/%(rule_id)s/delete', ['repo_name', 'rule_id']);
346 348 pyroutes.register('plugin_admin_chat', '/_admin/plugin_admin_chat/%(action)s', ['action']);
347 349 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
348 350 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
349 351 pyroutes.register('admin_settings_scheduler_show_tasks', '/_admin/settings/scheduler/_tasks', []);
350 352 pyroutes.register('admin_settings_scheduler_show_all', '/_admin/settings/scheduler', []);
351 353 pyroutes.register('admin_settings_scheduler_new', '/_admin/settings/scheduler/new', []);
352 354 pyroutes.register('admin_settings_scheduler_create', '/_admin/settings/scheduler/create', []);
353 355 pyroutes.register('admin_settings_scheduler_edit', '/_admin/settings/scheduler/%(schedule_id)s', ['schedule_id']);
354 356 pyroutes.register('admin_settings_scheduler_update', '/_admin/settings/scheduler/%(schedule_id)s/update', ['schedule_id']);
355 357 pyroutes.register('admin_settings_scheduler_delete', '/_admin/settings/scheduler/%(schedule_id)s/delete', ['schedule_id']);
356 358 pyroutes.register('admin_settings_scheduler_execute', '/_admin/settings/scheduler/%(schedule_id)s/execute', ['schedule_id']);
357 359 pyroutes.register('admin_settings_automation', '/_admin/settings/automation', []);
358 360 pyroutes.register('admin_settings_automation_update', '/_admin/settings/automation/%(entry_id)s/update', ['entry_id']);
359 361 pyroutes.register('admin_permissions_branch', '/_admin/permissions/branch', []);
360 362 pyroutes.register('admin_permissions_branch_update', '/_admin/permissions/branch/update', []);
361 363 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
362 364 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
363 365 pyroutes.register('my_account_external_identity', '/_admin/my_account/external-identity', []);
364 366 pyroutes.register('my_account_external_identity_delete', '/_admin/my_account/external-identity/delete', []);
365 367 pyroutes.register('repo_artifacts_list', '/%(repo_name)s/artifacts', ['repo_name']);
366 368 pyroutes.register('repo_artifacts_data', '/%(repo_name)s/artifacts_data', ['repo_name']);
367 369 pyroutes.register('repo_artifacts_new', '/%(repo_name)s/artifacts/new', ['repo_name']);
368 370 pyroutes.register('repo_artifacts_get', '/%(repo_name)s/artifacts/download/%(uid)s', ['repo_name', 'uid']);
369 371 pyroutes.register('repo_artifacts_store', '/%(repo_name)s/artifacts/store', ['repo_name']);
370 372 pyroutes.register('repo_artifacts_delete', '/%(repo_name)s/artifacts/delete/%(uid)s', ['repo_name', 'uid']);
371 373 pyroutes.register('repo_automation', '/%(repo_name)s/settings/automation', ['repo_name']);
372 374 pyroutes.register('repo_automation_update', '/%(repo_name)s/settings/automation/%(entry_id)s/update', ['repo_name', 'entry_id']);
373 375 pyroutes.register('edit_repo_remote_push', '/%(repo_name)s/settings/remote/push', ['repo_name']);
374 376 pyroutes.register('edit_repo_perms_branch', '/%(repo_name)s/settings/branch_permissions', ['repo_name']);
375 377 pyroutes.register('edit_repo_perms_branch_delete', '/%(repo_name)s/settings/branch_permissions/%(rule_id)s/delete', ['repo_name', 'rule_id']);
376 378 }
@@ -1,974 +1,974 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="root.mako"/>
3 3
4 4 <%include file="/ejs_templates/templates.html"/>
5 5
6 6 <div class="outerwrapper">
7 7 <!-- HEADER -->
8 8 <div class="header">
9 9 <div id="header-inner" class="wrapper">
10 10 <div id="logo">
11 11 <div class="logo-wrapper">
12 12 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-60x60.png')}" alt="RhodeCode"/></a>
13 13 </div>
14 14 % if c.rhodecode_name:
15 15 <div class="branding">
16 16 <a href="${h.route_path('home')}">${h.branding(c.rhodecode_name)}</a>
17 17 </div>
18 18 % endif
19 19 </div>
20 20 <!-- MENU BAR NAV -->
21 21 ${self.menu_bar_nav()}
22 22 <!-- END MENU BAR NAV -->
23 23 </div>
24 24 </div>
25 25 ${self.menu_bar_subnav()}
26 26 <!-- END HEADER -->
27 27
28 28 <!-- CONTENT -->
29 29 <div id="content" class="wrapper">
30 30
31 31 <rhodecode-toast id="notifications"></rhodecode-toast>
32 32
33 33 <div class="main">
34 34 ${next.main()}
35 35 </div>
36 36 </div>
37 37 <!-- END CONTENT -->
38 38
39 39 </div>
40 40 <!-- FOOTER -->
41 41 <div id="footer">
42 42 <div id="footer-inner" class="title wrapper">
43 43 <div>
44 44 <p class="footer-link-right">
45 45 % if c.visual.show_version:
46 46 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
47 47 % endif
48 48 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
49 49 % if c.visual.rhodecode_support_url:
50 50 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
51 51 % endif
52 52 </p>
53 53 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
54 54 <p class="server-instance" style="display:${sid}">
55 55 ## display hidden instance ID if specially defined
56 56 % if c.rhodecode_instanceid:
57 57 ${_('RhodeCode instance id: {}').format(c.rhodecode_instanceid)}
58 58 % endif
59 59 </p>
60 60 </div>
61 61 </div>
62 62 </div>
63 63
64 64 <!-- END FOOTER -->
65 65
66 66 ### MAKO DEFS ###
67 67
68 68 <%def name="menu_bar_subnav()">
69 69 </%def>
70 70
71 71 <%def name="breadcrumbs(class_='breadcrumbs')">
72 72 <div class="${class_}">
73 73 ${self.breadcrumbs_links()}
74 74 </div>
75 75 </%def>
76 76
77 77 <%def name="admin_menu(active=None)">
78 78 <%
79 79 def is_active(selected):
80 80 if selected == active:
81 81 return "active"
82 82 %>
83 83
84 84 <div id="context-bar">
85 85 <div class="wrapper">
86 86 <div class="title">
87 87 <div class="title-content">
88 88 <div class="title-main">
89 89 % if c.is_super_admin:
90 90 ${_('Super Admin Panel')}
91 91 % else:
92 92 ${_('Delegated Admin Panel')}
93 93 % endif
94 94 </div>
95 95 </div>
96 96 </div>
97 97
98 98 <ul id="context-pages" class="navigation horizontal-list">
99 99
100 100 ## super admin case
101 101 % if c.is_super_admin:
102 102 <li class="${is_active('audit_logs')}"><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
103 103 <li class="${is_active('repositories')}"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
104 104 <li class="${is_active('repository_groups')}"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
105 105 <li class="${is_active('users')}"><a href="${h.route_path('users')}">${_('Users')}</a></li>
106 106 <li class="${is_active('user_groups')}"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
107 107 <li class="${is_active('permissions')}"><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
108 108 <li class="${is_active('authentication')}"><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
109 109 <li class="${is_active('integrations')}"><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
110 110 <li class="${is_active('defaults')}"><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
111 111 <li class="${is_active('settings')}"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li>
112 112
113 113 ## delegated admin
114 114 % elif c.is_delegated_admin:
115 115 <%
116 116 repositories=c.auth_user.repositories_admin or c.can_create_repo
117 117 repository_groups=c.auth_user.repository_groups_admin or c.can_create_repo_group
118 118 user_groups=c.auth_user.user_groups_admin or c.can_create_user_group
119 119 %>
120 120
121 121 %if repositories:
122 122 <li class="${is_active('repositories')} local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
123 123 %endif
124 124 %if repository_groups:
125 125 <li class="${is_active('repository_groups')} local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
126 126 %endif
127 127 %if user_groups:
128 128 <li class="${is_active('user_groups')} local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
129 129 %endif
130 130 % endif
131 131 </ul>
132 132
133 133 </div>
134 134 <div class="clear"></div>
135 135 </div>
136 136 </%def>
137 137
138 138 <%def name="dt_info_panel(elements)">
139 139 <dl class="dl-horizontal">
140 140 %for dt, dd, title, show_items in elements:
141 141 <dt>${dt}:</dt>
142 142 <dd title="${h.tooltip(title)}">
143 143 %if callable(dd):
144 144 ## allow lazy evaluation of elements
145 145 ${dd()}
146 146 %else:
147 147 ${dd}
148 148 %endif
149 149 %if show_items:
150 150 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
151 151 %endif
152 152 </dd>
153 153
154 154 %if show_items:
155 155 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
156 156 %for item in show_items:
157 157 <dt></dt>
158 158 <dd>${item}</dd>
159 159 %endfor
160 160 </div>
161 161 %endif
162 162
163 163 %endfor
164 164 </dl>
165 165 </%def>
166 166
167 167 <%def name="gravatar(email, size=16)">
168 168 <%
169 169 if (size > 16):
170 170 gravatar_class = 'gravatar gravatar-large'
171 171 else:
172 172 gravatar_class = 'gravatar'
173 173 %>
174 174 <%doc>
175 175 TODO: johbo: For now we serve double size images to make it smooth
176 176 for retina. This is how it worked until now. Should be replaced
177 177 with a better solution at some point.
178 178 </%doc>
179 179 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
180 180 </%def>
181 181
182 182
183 183 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
184 184 <% email = h.email_or_none(contact) %>
185 185 <div class="rc-user tooltip" title="${h.tooltip(h.author_string(email))}">
186 186 ${self.gravatar(email, size)}
187 187 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
188 188 </div>
189 189 </%def>
190 190
191 191
192 192 <%def name="repo_page_title(repo_instance)">
193 193 <div class="title-content repo-title">
194 194
195 195 <div class="title-main">
196 196 ## SVN/HG/GIT icons
197 197 %if h.is_hg(repo_instance):
198 198 <i class="icon-hg"></i>
199 199 %endif
200 200 %if h.is_git(repo_instance):
201 201 <i class="icon-git"></i>
202 202 %endif
203 203 %if h.is_svn(repo_instance):
204 204 <i class="icon-svn"></i>
205 205 %endif
206 206
207 207 ## public/private
208 208 %if repo_instance.private:
209 209 <i class="icon-repo-private"></i>
210 210 %else:
211 211 <i class="icon-repo-public"></i>
212 212 %endif
213 213
214 214 ## repo name with group name
215 215 ${h.breadcrumb_repo_link(repo_instance)}
216 216
217 217 ## Context Actions
218 218 <div class="pull-right">
219 219 %if c.rhodecode_user.username != h.DEFAULT_USER:
220 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_name, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
220 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
221 221
222 222 <a href="#WatchRepo" onclick="toggleFollowingRepo(this, templateContext.repo_id); return false" title="${_('Watch this Repository and actions on it in your personalized journal')}" class="btn btn-sm ${('watching' if c.repository_is_user_following else '')}">
223 223 % if c.repository_is_user_following:
224 224 <i class="icon-eye-off"></i>${_('Unwatch')}
225 225 % else:
226 226 <i class="icon-eye"></i>${_('Watch')}
227 227 % endif
228 228
229 229 </a>
230 230 %else:
231 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_name)}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
231 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid)}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
232 232 %endif
233 233 </div>
234 234
235 235 </div>
236 236
237 237 ## FORKED
238 238 %if repo_instance.fork:
239 239 <p class="discreet">
240 240 <i class="icon-code-fork"></i> ${_('Fork of')}
241 241 ${h.link_to_if(c.has_origin_repo_read_perm,repo_instance.fork.repo_name, h.route_path('repo_summary', repo_name=repo_instance.fork.repo_name))}
242 242 </p>
243 243 %endif
244 244
245 245 ## IMPORTED FROM REMOTE
246 246 %if repo_instance.clone_uri:
247 247 <p class="discreet">
248 248 <i class="icon-code-fork"></i> ${_('Clone from')}
249 249 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
250 250 </p>
251 251 %endif
252 252
253 253 ## LOCKING STATUS
254 254 %if repo_instance.locked[0]:
255 255 <p class="locking_locked discreet">
256 256 <i class="icon-repo-lock"></i>
257 257 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
258 258 </p>
259 259 %elif repo_instance.enable_locking:
260 260 <p class="locking_unlocked discreet">
261 261 <i class="icon-repo-unlock"></i>
262 262 ${_('Repository not locked. Pull repository to lock it.')}
263 263 </p>
264 264 %endif
265 265
266 266 </div>
267 267 </%def>
268 268
269 269 <%def name="repo_menu(active=None)">
270 270 <%
271 271 def is_active(selected):
272 272 if selected == active:
273 273 return "active"
274 274 %>
275 275 % if c.rhodecode_db_repo.archived:
276 276 <div class="alert alert-warning text-center">
277 277 <strong>${_('This repository has been archived. It is now read-only.')}</strong>
278 278 </div>
279 279 % endif
280 280
281 281 <!--- REPO CONTEXT BAR -->
282 282 <div id="context-bar">
283 283 <div class="wrapper">
284 284
285 285 <div class="title">
286 286 ${self.repo_page_title(c.rhodecode_db_repo)}
287 287 </div>
288 288
289 289 <ul id="context-pages" class="navigation horizontal-list">
290 290 <li class="${is_active('summary')}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
291 291 <li class="${is_active('commits')}"><a class="menulink" href="${h.route_path('repo_commits', repo_name=c.repo_name)}"><div class="menulabel">${_('Commits')}</div></a></li>
292 292 <li class="${is_active('files')}"><a class="menulink" href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.rhodecode_db_repo.landing_rev[1], f_path='')}"><div class="menulabel">${_('Files')}</div></a></li>
293 293 <li class="${is_active('compare')}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
294 294
295 295 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
296 296 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
297 297 <li class="${is_active('showpullrequest')}">
298 298 <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${h.tooltip(_('Show Pull Requests for %s') % c.repo_name)}">
299 299 <div class="menulabel">
300 300 %if c.repository_pull_requests == 1:
301 301 ${_('Pull Request')} ${c.repository_pull_requests}
302 302 %else:
303 303 ${_('Pull Requests')} ${c.repository_pull_requests}
304 304 %endif
305 305 </div>
306 306 </a>
307 307 </li>
308 308 %endif
309 309
310 310 <li class="${is_active('artifacts')}"><a class="menulink" href="${h.route_path('repo_artifacts_list',repo_name=c.repo_name)}"><div class="menulabel">${_('Artifacts')} (BETA)</div></a></li>
311 311
312 312 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
313 313 <li class="${is_active('settings')}"><a class="menulink" href="${h.route_path('edit_repo',repo_name=c.repo_name)}"><div class="menulabel">${_('Repository Settings')}</div></a></li>
314 314 %endif
315 315
316 316 ## determine if we have "any" option available
317 317 <%
318 318 can_lock = h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking
319 319 has_actions = (c.rhodecode_user.username != h.DEFAULT_USER and c.rhodecode_db_repo.repo_type in ['git','hg'] ) or can_lock
320 320 %>
321 321 <li class="${is_active('options')}">
322 322 % if has_actions:
323 323 <a class="menulink dropdown">
324 324 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
325 325 </a>
326 326 <ul class="submenu">
327 327 <li><a href="${h.route_path('repo_fork_new',repo_name=c.repo_name)}">${_('Fork this repository')}</a></li>
328 328 <li><a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
329 329 %if can_lock:
330 330 %if c.rhodecode_db_repo.locked[0]:
331 331 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock Repository')}</a></li>
332 332 %else:
333 333 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock Repository')}</a></li>
334 334 %endif
335 335 %endif
336 336 </ul>
337 337 % else:
338 338 <a class="menulink disabled">
339 339 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
340 340 </a>
341 341 % endif
342 342 </li>
343 343
344 344 </ul>
345 345 </div>
346 346 <div class="clear"></div>
347 347 </div>
348 348
349 349 <!--- REPO END CONTEXT BAR -->
350 350
351 351 </%def>
352 352
353 353 <%def name="repo_group_page_title(repo_group_instance)">
354 354 <div class="title-content">
355 355 <div class="title-main">
356 356 ## Repository Group icon
357 357 <i class="icon-repo-group"></i>
358 358
359 359 ## repo name with group name
360 360 ${h.breadcrumb_repo_group_link(repo_group_instance)}
361 361 </div>
362 362
363 363 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
364 364 <div class="repo-group-desc discreet">
365 365 ${dt.repo_group_desc(repo_group_instance.description_safe, repo_group_instance.personal, c.visual.stylify_metatags)}
366 366 </div>
367 367
368 368 </div>
369 369 </%def>
370 370
371 371 <%def name="repo_group_menu(active=None)">
372 372 <%
373 373 def is_active(selected):
374 374 if selected == active:
375 375 return "active"
376 376
377 377 gr_name = c.repo_group.group_name if c.repo_group else None
378 378 # create repositories with write permission on group is set to true
379 379 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
380 380 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
381 381 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
382 382
383 383 %>
384 384
385 385 <!--- REPO GROUP CONTEXT BAR -->
386 386 <div id="context-bar">
387 387 <div class="wrapper">
388 388 <div class="title">
389 389 ${self.repo_group_page_title(c.repo_group)}
390 390 </div>
391 391
392 392 <ul id="context-pages" class="navigation horizontal-list">
393 393 <li class="${is_active('home')}"><a class="menulink" href="${h.route_path('repo_group_home', repo_group_name=c.repo_group.group_name)}"><div class="menulabel">${_('Group Home')}</div></a></li>
394 394 % if c.is_super_admin or group_admin:
395 395 <li class="${is_active('settings')}"><a class="menulink" href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}"><div class="menulabel">${_('Group Settings')}</div></a></li>
396 396 % endif
397 397 ## determine if we have "any" option available
398 398 <%
399 399 can_create_repos = c.is_super_admin or group_admin or (group_write and create_on_write)
400 400 can_create_repo_groups = c.is_super_admin or group_admin
401 401 has_actions = can_create_repos or can_create_repo_groups
402 402 %>
403 403 <li class="${is_active('options')}">
404 404 % if has_actions:
405 405 <a class="menulink dropdown">
406 406 <div class="menulabel">${_('Options')} <div class="show_more"></div></div>
407 407 </a>
408 408 <ul class="submenu">
409 409 %if can_create_repos:
410 410 <li><a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}">${_('Add Repository')}</a></li>
411 411 %endif
412 412 %if can_create_repo_groups:
413 413 <li><a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.repo_group.group_id))}">${_(u'Add Repository Group')}</a></li>
414 414 %endif
415 415 </ul>
416 416 % else:
417 417 <a class="menulink disabled">
418 418 <div class="menulabel">${_('Options')} <div class="show_more"></div></div>
419 419 </a>
420 420 % endif
421 421 </li>
422 422 </ul>
423 423 </div>
424 424 <div class="clear"></div>
425 425 </div>
426 426
427 427 <!--- REPO GROUP CONTEXT BAR -->
428 428
429 429 </%def>
430 430
431 431
432 432 <%def name="usermenu(active=False)">
433 433 ## USER MENU
434 434 <li id="quick_login_li" class="${'active' if active else ''}">
435 435 % if c.rhodecode_user.username == h.DEFAULT_USER:
436 436 <a id="quick_login_link" class="menulink childs" href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">
437 437 ${gravatar(c.rhodecode_user.email, 20)}
438 438 <span class="user">
439 439 <span>${_('Sign in')}</span>
440 440 </span>
441 441 </a>
442 442 % else:
443 443 ## logged in user
444 444 <a id="quick_login_link" class="menulink childs">
445 445 ${gravatar(c.rhodecode_user.email, 20)}
446 446 <span class="user">
447 447 <span class="menu_link_user">${c.rhodecode_user.username}</span>
448 448 <div class="show_more"></div>
449 449 </span>
450 450 </a>
451 451 ## subnav with menu for logged in user
452 452 <div class="user-menu submenu">
453 453 <div id="quick_login">
454 454 %if c.rhodecode_user.username != h.DEFAULT_USER:
455 455 <div class="">
456 456 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
457 457 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
458 458 <div class="email">${c.rhodecode_user.email}</div>
459 459 </div>
460 460 <div class="">
461 461 <ol class="links">
462 462 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
463 463 % if c.rhodecode_user.personal_repo_group:
464 464 <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
465 465 % endif
466 466 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
467 467 ## bookmark-items
468 468 <li class="bookmark-items">
469 469 ${_('Bookmarks')}
470 470 <div class="pull-right">
471 471 <a href="${h.route_path('my_account_bookmarks')}">${_('Manage')}</a>
472 472 </div>
473 473 </li>
474 474 % if not c.bookmark_items:
475 475 <li>
476 476 <a href="${h.route_path('my_account_bookmarks')}">${_('No Bookmarks yet.')}</a>
477 477 </li>
478 478 % endif
479 479 % for item in c.bookmark_items:
480 480 <li>
481 481 % if item.repository:
482 482 <div>
483 483 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
484 484 <code>${item.position}</code>
485 485 % if item.repository.repo_type == 'hg':
486 486 <i class="icon-hg" title="${_('Repository')}" style="font-size: 16px"></i>
487 487 % elif item.repository.repo_type == 'git':
488 488 <i class="icon-git" title="${_('Repository')}" style="font-size: 16px"></i>
489 489 % elif item.repository.repo_type == 'svn':
490 490 <i class="icon-svn" title="${_('Repository')}" style="font-size: 16px"></i>
491 491 % endif
492 492 ${(item.title or h.shorter(item.repository.repo_name, 30))}
493 493 </a>
494 494 </div>
495 495 % elif item.repository_group:
496 496 <div>
497 497 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
498 498 <code>${item.position}</code>
499 499 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
500 500 ${(item.title or h.shorter(item.repository_group.group_name, 30))}
501 501 </a>
502 502 </div>
503 503 % else:
504 504 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
505 505 <code>${item.position}</code>
506 506 ${item.title}
507 507 </a>
508 508 % endif
509 509 </li>
510 510 % endfor
511 511
512 512 <li class="logout">
513 513 ${h.secure_form(h.route_path('logout'), request=request)}
514 514 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
515 515 ${h.end_form()}
516 516 </li>
517 517 </ol>
518 518 </div>
519 519 %endif
520 520 </div>
521 521 </div>
522 522 ## unread counter
523 523 <div class="pill_container">
524 524 <a class="menu_link_notifications ${'empty' if c.unread_notifications == 0 else ''}" href="${h.route_path('notifications_show_all')}">${c.unread_notifications}</a>
525 525 </div>
526 526 % endif
527 527 </li>
528 528 </%def>
529 529
530 530 <%def name="menu_items(active=None)">
531 531 <%
532 532 def is_active(selected):
533 533 if selected == active:
534 534 return "active"
535 535 return ""
536 536 %>
537 537
538 538 <ul id="quick" class="main_nav navigation horizontal-list">
539 539 ## notice box for important system messages
540 540 <li style="display: none">
541 541 <a class="notice-box" href="#openNotice" onclick="showNoticeBox(); return false">
542 542 <div class="menulabel-notice" >
543 543 0
544 544 </div>
545 545 </a>
546 546 </li>
547 547
548 548 ## Main filter
549 549 <li>
550 550 <div class="menulabel main_filter_box">
551 551 <div class="main_filter_input_box">
552 552 <ul class="searchItems">
553 553
554 554 % if c.template_context['search_context']['repo_id']:
555 555 <li class="searchTag searchTagFilter searchTagHidable" >
556 556 ##<a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">
557 557 <span class="tag">
558 558 This repo
559 559 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
560 560 </span>
561 561 ##</a>
562 562 </li>
563 563 % elif c.template_context['search_context']['repo_group_id']:
564 564 <li class="searchTag searchTagFilter searchTagHidable">
565 565 ##<a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">
566 566 <span class="tag">
567 567 This group
568 568 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
569 569 </span>
570 570 ##</a>
571 571 </li>
572 572 % endif
573 573
574 574 <li class="searchTagInput">
575 575 <input class="main_filter_input" id="main_filter" size="25" type="text" name="main_filter" placeholder="${_('search / go to...')}" value="" />
576 576 </li>
577 577 <li class="searchTag searchTagHelp">
578 578 <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a>
579 579 </li>
580 580 </ul>
581 581 </div>
582 582 </div>
583 583
584 584 <div id="main_filter_help" style="display: none">
585 585 - Use '/' key to quickly access this field.
586 586
587 587 - Enter a name of repository, or repository group for quick search.
588 588
589 589 - Prefix query to allow special search:
590 590
591 591 user:admin, to search for usernames, always global
592 592
593 593 user_group:devops, to search for user groups, always global
594 594
595 595 commit:efced4, to search for commits, scoped to repositories or groups
596 596
597 597 file:models.py, to search for file paths, scoped to repositories or groups
598 598
599 599 % if c.template_context['search_context']['repo_id']:
600 600 For advanced full text search visit: <a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">repository search</a>
601 601 % elif c.template_context['search_context']['repo_group_id']:
602 602 For advanced full text search visit: <a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">repository group search</a>
603 603 % else:
604 604 For advanced full text search visit: <a href="${h.route_path('search')}">global search</a>
605 605 % endif
606 606 </div>
607 607 </li>
608 608
609 609 ## ROOT MENU
610 610 <li class="${is_active('home')}">
611 611 <a class="menulink" title="${_('Home')}" href="${h.route_path('home')}">
612 612 <div class="menulabel">${_('Home')}</div>
613 613 </a>
614 614 </li>
615 615
616 616 %if c.rhodecode_user.username != h.DEFAULT_USER:
617 617 <li class="${is_active('journal')}">
618 618 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
619 619 <div class="menulabel">${_('Journal')}</div>
620 620 </a>
621 621 </li>
622 622 %else:
623 623 <li class="${is_active('journal')}">
624 624 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
625 625 <div class="menulabel">${_('Public journal')}</div>
626 626 </a>
627 627 </li>
628 628 %endif
629 629
630 630 <li class="${is_active('gists')}">
631 631 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
632 632 <div class="menulabel">${_('Gists')}</div>
633 633 </a>
634 634 </li>
635 635
636 636 % if c.is_super_admin or c.is_delegated_admin:
637 637 <li class="${is_active('admin')}">
638 638 <a class="menulink childs" title="${_('Admin settings')}" href="${h.route_path('admin_home')}">
639 639 <div class="menulabel">${_('Admin')} </div>
640 640 </a>
641 641 </li>
642 642 % endif
643 643
644 644 ## render extra user menu
645 645 ${usermenu(active=(active=='my_account'))}
646 646
647 647 % if c.debug_style:
648 648 <li>
649 649 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
650 650 <div class="menulabel">${_('[Style]')}</div>
651 651 </a>
652 652 </li>
653 653 % endif
654 654 </ul>
655 655
656 656 <script type="text/javascript">
657 657 var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True";
658 658
659 659 var formatRepoResult = function(result, container, query, escapeMarkup) {
660 660 return function(data, escapeMarkup) {
661 661 if (!data.repo_id){
662 662 return data.text; // optgroup text Repositories
663 663 }
664 664
665 665 var tmpl = '';
666 666 var repoType = data['repo_type'];
667 667 var repoName = data['text'];
668 668
669 669 if(data && data.type == 'repo'){
670 670 if(repoType === 'hg'){
671 671 tmpl += '<i class="icon-hg"></i> ';
672 672 }
673 673 else if(repoType === 'git'){
674 674 tmpl += '<i class="icon-git"></i> ';
675 675 }
676 676 else if(repoType === 'svn'){
677 677 tmpl += '<i class="icon-svn"></i> ';
678 678 }
679 679 if(data['private']){
680 680 tmpl += '<i class="icon-lock" ></i> ';
681 681 }
682 682 else if(visualShowPublicIcon){
683 683 tmpl += '<i class="icon-unlock-alt"></i> ';
684 684 }
685 685 }
686 686 tmpl += escapeMarkup(repoName);
687 687 return tmpl;
688 688
689 689 }(result, escapeMarkup);
690 690 };
691 691
692 692 var formatRepoGroupResult = function(result, container, query, escapeMarkup) {
693 693 return function(data, escapeMarkup) {
694 694 if (!data.repo_group_id){
695 695 return data.text; // optgroup text Repositories
696 696 }
697 697
698 698 var tmpl = '';
699 699 var repoGroupName = data['text'];
700 700
701 701 if(data){
702 702
703 703 tmpl += '<i class="icon-repo-group"></i> ';
704 704
705 705 }
706 706 tmpl += escapeMarkup(repoGroupName);
707 707 return tmpl;
708 708
709 709 }(result, escapeMarkup);
710 710 };
711 711
712 712 var escapeRegExChars = function (value) {
713 713 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
714 714 };
715 715
716 716 var getRepoIcon = function(repo_type) {
717 717 if (repo_type === 'hg') {
718 718 return '<i class="icon-hg"></i> ';
719 719 }
720 720 else if (repo_type === 'git') {
721 721 return '<i class="icon-git"></i> ';
722 722 }
723 723 else if (repo_type === 'svn') {
724 724 return '<i class="icon-svn"></i> ';
725 725 }
726 726 return ''
727 727 };
728 728
729 729 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
730 730
731 731 if (value.split(':').length === 2) {
732 732 value = value.split(':')[1]
733 733 }
734 734
735 735 var searchType = data['type'];
736 736 var searchSubType = data['subtype'];
737 737 var valueDisplay = data['value_display'];
738 738
739 739 var pattern = '(' + escapeRegExChars(value) + ')';
740 740
741 741 valueDisplay = Select2.util.escapeMarkup(valueDisplay);
742 742
743 743 // highlight match
744 744 if (searchType != 'text') {
745 745 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
746 746 }
747 747
748 748 var icon = '';
749 749
750 750 if (searchType === 'hint') {
751 751 icon += '<i class="icon-repo-group"></i> ';
752 752 }
753 753 // full text search/hints
754 754 else if (searchType === 'search') {
755 755 icon += '<i class="icon-more"></i> ';
756 756 if (searchSubType !== undefined && searchSubType == 'repo') {
757 757 valueDisplay += '<div class="pull-right tag">repository</div>';
758 758 }
759 759 else if (searchSubType !== undefined && searchSubType == 'repo_group') {
760 760 valueDisplay += '<div class="pull-right tag">repo group</div>';
761 761 }
762 762 }
763 763 // repository
764 764 else if (searchType === 'repo') {
765 765
766 766 var repoIcon = getRepoIcon(data['repo_type']);
767 767 icon += repoIcon;
768 768
769 769 if (data['private']) {
770 770 icon += '<i class="icon-lock" ></i> ';
771 771 }
772 772 else if (visualShowPublicIcon) {
773 773 icon += '<i class="icon-unlock-alt"></i> ';
774 774 }
775 775 }
776 776 // repository groups
777 777 else if (searchType === 'repo_group') {
778 778 icon += '<i class="icon-repo-group"></i> ';
779 779 }
780 780 // user group
781 781 else if (searchType === 'user_group') {
782 782 icon += '<i class="icon-group"></i> ';
783 783 }
784 784 // user
785 785 else if (searchType === 'user') {
786 786 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
787 787 }
788 788 // commit
789 789 else if (searchType === 'commit') {
790 790 var repo_data = data['repo_data'];
791 791 var repoIcon = getRepoIcon(repo_data['repository_type']);
792 792 if (repoIcon) {
793 793 icon += repoIcon;
794 794 } else {
795 795 icon += '<i class="icon-tag"></i>';
796 796 }
797 797 }
798 798 // file
799 799 else if (searchType === 'file') {
800 800 var repo_data = data['repo_data'];
801 801 var repoIcon = getRepoIcon(repo_data['repository_type']);
802 802 if (repoIcon) {
803 803 icon += repoIcon;
804 804 } else {
805 805 icon += '<i class="icon-tag"></i>';
806 806 }
807 807 }
808 808 // generic text
809 809 else if (searchType === 'text') {
810 810 icon = '';
811 811 }
812 812
813 813 var tmpl = '<div class="ac-container-wrap">{0}{1}</div>';
814 814 return tmpl.format(icon, valueDisplay);
815 815 };
816 816
817 817 var handleSelect = function(element, suggestion) {
818 818 if (suggestion.type === "hint") {
819 819 // we skip action
820 820 $('#main_filter').focus();
821 821 }
822 822 else if (suggestion.type === "text") {
823 823 // we skip action
824 824 $('#main_filter').focus();
825 825
826 826 } else {
827 827 window.location = suggestion['url'];
828 828 }
829 829 };
830 830
831 831 var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) {
832 832 if (queryLowerCase.split(':').length === 2) {
833 833 queryLowerCase = queryLowerCase.split(':')[1]
834 834 }
835 835 if (suggestion.type === "text") {
836 836 // special case we don't want to "skip" display for
837 837 return true
838 838 }
839 839 return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1;
840 840 };
841 841
842 842 var cleanContext = {
843 843 repo_view_type: null,
844 844
845 845 repo_id: null,
846 846 repo_name: "",
847 847
848 848 repo_group_id: null,
849 849 repo_group_name: null
850 850 };
851 851 var removeGoToFilter = function () {
852 852 $('.searchTagHidable').hide();
853 853 $('#main_filter').autocomplete(
854 854 'setOptions', {params:{search_context: cleanContext}});
855 855 };
856 856
857 857 $('#main_filter').autocomplete({
858 858 serviceUrl: pyroutes.url('goto_switcher_data'),
859 859 params: {
860 860 "search_context": templateContext.search_context
861 861 },
862 862 minChars:2,
863 863 maxHeight:400,
864 864 deferRequestBy: 300, //miliseconds
865 865 tabDisabled: true,
866 866 autoSelectFirst: false,
867 867 containerClass: 'autocomplete-qfilter-suggestions',
868 868 formatResult: autocompleteMainFilterFormatResult,
869 869 lookupFilter: autocompleteMainFilterResult,
870 870 onSelect: function (element, suggestion) {
871 871 handleSelect(element, suggestion);
872 872 return false;
873 873 },
874 874 onSearchError: function (element, query, jqXHR, textStatus, errorThrown) {
875 875 if (jqXHR !== 'abort') {
876 876 alert("Error during search.\nError code: {0}".format(textStatus));
877 877 window.location = '';
878 878 }
879 879 }
880 880 });
881 881
882 882 showMainFilterBox = function () {
883 883 $('#main_filter_help').toggle();
884 884 };
885 885
886 886 $('#main_filter').on('keydown.autocomplete', function (e) {
887 887
888 888 var BACKSPACE = 8;
889 889 var el = $(e.currentTarget);
890 890 if(e.which === BACKSPACE){
891 891 var inputVal = el.val();
892 892 if (inputVal === ""){
893 893 removeGoToFilter()
894 894 }
895 895 }
896 896 });
897 897
898 898 </script>
899 899 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
900 900 </%def>
901 901
902 902 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
903 903 <div class="modal-dialog">
904 904 <div class="modal-content">
905 905 <div class="modal-header">
906 906 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
907 907 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
908 908 </div>
909 909 <div class="modal-body">
910 910 <div class="block-left">
911 911 <table class="keyboard-mappings">
912 912 <tbody>
913 913 <tr>
914 914 <th></th>
915 915 <th>${_('Site-wide shortcuts')}</th>
916 916 </tr>
917 917 <%
918 918 elems = [
919 919 ('/', 'Use quick search box'),
920 920 ('g h', 'Goto home page'),
921 921 ('g g', 'Goto my private gists page'),
922 922 ('g G', 'Goto my public gists page'),
923 923 ('g 0-9', 'Goto bookmarked items from 0-9'),
924 924 ('n r', 'New repository page'),
925 925 ('n g', 'New gist page'),
926 926 ]
927 927 %>
928 928 %for key, desc in elems:
929 929 <tr>
930 930 <td class="keys">
931 931 <span class="key tag">${key}</span>
932 932 </td>
933 933 <td>${desc}</td>
934 934 </tr>
935 935 %endfor
936 936 </tbody>
937 937 </table>
938 938 </div>
939 939 <div class="block-left">
940 940 <table class="keyboard-mappings">
941 941 <tbody>
942 942 <tr>
943 943 <th></th>
944 944 <th>${_('Repositories')}</th>
945 945 </tr>
946 946 <%
947 947 elems = [
948 948 ('g s', 'Goto summary page'),
949 949 ('g c', 'Goto changelog page'),
950 950 ('g f', 'Goto files page'),
951 951 ('g F', 'Goto files page with file search activated'),
952 952 ('g p', 'Goto pull requests page'),
953 953 ('g o', 'Goto repository settings'),
954 954 ('g O', 'Goto repository permissions settings'),
955 955 ]
956 956 %>
957 957 %for key, desc in elems:
958 958 <tr>
959 959 <td class="keys">
960 960 <span class="key tag">${key}</span>
961 961 </td>
962 962 <td>${desc}</td>
963 963 </tr>
964 964 %endfor
965 965 </tbody>
966 966 </table>
967 967 </div>
968 968 </div>
969 969 <div class="modal-footer">
970 970 </div>
971 971 </div><!-- /.modal-content -->
972 972 </div><!-- /.modal-dialog -->
973 973 </div><!-- /.modal -->
974 974
@@ -1,28 +1,28 b''
1 1 <%inherit file="/base/base.mako"/>
2 2
3 3 <%def name="title()">
4 4 ## represents page title
5 ${_('%s Summary') % c.repo_name}
5 ${_('{} Summary').format(c.repo_name)}
6 6 %if c.rhodecode_name:
7 7 &middot; ${h.branding(c.rhodecode_name)}
8 8 %endif
9 9 </%def>
10 10
11 11
12 12 <%def name="head_extra()">
13 <link href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_name, _query=dict(auth_token=c.rhodecode_user.feed_token))}" rel="alternate" title="${h.tooltip(_('%s ATOM feed') % c.repo_name)}" type="application/atom+xml" />
14 <link href="${h.route_path('rss_feed_home', repo_name=c.rhodecode_db_repo.repo_name, _query=dict(auth_token=c.rhodecode_user.feed_token))}" rel="alternate" title="${h.tooltip(_('%s RSS feed') % c.repo_name)}" type="application/rss+xml" />
13 <link href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid, _query=dict(auth_token=c.rhodecode_user.feed_token))}" rel="alternate" title="${h.tooltip(_('%s ATOM feed') % c.repo_name)}" type="application/atom+xml" />
14 <link href="${h.route_path('rss_feed_home', repo_name=c.rhodecode_db_repo.repo_uid, _query=dict(auth_token=c.rhodecode_user.feed_token))}" rel="alternate" title="${h.tooltip(_('%s RSS feed') % c.repo_name)}" type="application/rss+xml" />
15 15 </%def>
16 16
17 17
18 18 <%def name="menu_bar_nav()">
19 19 ${self.menu_items(active='repositories')}
20 20 </%def>
21 21
22 22
23 23 <%def name="breadcrumbs_links()"></%def>
24 24
25 25
26 26 <%def name="main()">
27 27 ${next.main()}
28 28 </%def>
General Comments 0
You need to be logged in to leave comments. Login now