##// END OF EJS Templates
diffs: compare overhaul....
marcink -
r1259:8e9f93ec default
parent child Browse files
Show More

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

1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,1169 +1,1169 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 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 """
22 22 Routes configuration
23 23
24 24 The more specific and detailed routes should be defined first so they
25 25 may take precedent over the more generic routes. For more information
26 26 refer to the routes manual at http://routes.groovie.org/docs/
27 27
28 28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 29 and _route_name variable which uses some of stored naming here to do redirects.
30 30 """
31 31 import os
32 32 import re
33 33 from routes import Mapper
34 34
35 35 from rhodecode.config import routing_links
36 36
37 37 # prefix for non repository related links needs to be prefixed with `/`
38 38 ADMIN_PREFIX = '/_admin'
39 39 STATIC_FILE_PREFIX = '/_static'
40 40
41 41 # Default requirements for URL parts
42 42 URL_NAME_REQUIREMENTS = {
43 43 # group name can have a slash in them, but they must not end with a slash
44 44 'group_name': r'.*?[^/]',
45 45 'repo_group_name': r'.*?[^/]',
46 46 # repo names can have a slash in them, but they must not end with a slash
47 47 'repo_name': r'.*?[^/]',
48 48 # file path eats up everything at the end
49 49 'f_path': r'.*',
50 50 # reference types
51 51 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
52 52 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
53 53 }
54 54
55 55
56 56 def add_route_requirements(route_path, requirements):
57 57 """
58 58 Adds regex requirements to pyramid routes using a mapping dict
59 59
60 60 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
61 61 '/{action}/{id:\d+}'
62 62
63 63 """
64 64 for key, regex in requirements.items():
65 65 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
66 66 return route_path
67 67
68 68
69 69 class JSRoutesMapper(Mapper):
70 70 """
71 71 Wrapper for routes.Mapper to make pyroutes compatible url definitions
72 72 """
73 73 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
74 74 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
75 75 def __init__(self, *args, **kw):
76 76 super(JSRoutesMapper, self).__init__(*args, **kw)
77 77 self._jsroutes = []
78 78
79 79 def connect(self, *args, **kw):
80 80 """
81 81 Wrapper for connect to take an extra argument jsroute=True
82 82
83 83 :param jsroute: boolean, if True will add the route to the pyroutes list
84 84 """
85 85 if kw.pop('jsroute', False):
86 86 if not self._named_route_regex.match(args[0]):
87 87 raise Exception('only named routes can be added to pyroutes')
88 88 self._jsroutes.append(args[0])
89 89
90 90 super(JSRoutesMapper, self).connect(*args, **kw)
91 91
92 92 def _extract_route_information(self, route):
93 93 """
94 94 Convert a route into tuple(name, path, args), eg:
95 95 ('user_profile', '/profile/%(username)s', ['username'])
96 96 """
97 97 routepath = route.routepath
98 98 def replace(matchobj):
99 99 if matchobj.group(1):
100 100 return "%%(%s)s" % matchobj.group(1).split(':')[0]
101 101 else:
102 102 return "%%(%s)s" % matchobj.group(2)
103 103
104 104 routepath = self._argument_prog.sub(replace, routepath)
105 105 return (
106 106 route.name,
107 107 routepath,
108 108 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
109 109 for arg in self._argument_prog.findall(route.routepath)]
110 110 )
111 111
112 112 def jsroutes(self):
113 113 """
114 114 Return a list of pyroutes.js compatible routes
115 115 """
116 116 for route_name in self._jsroutes:
117 117 yield self._extract_route_information(self._routenames[route_name])
118 118
119 119
120 120 def make_map(config):
121 121 """Create, configure and return the routes Mapper"""
122 122 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
123 123 always_scan=config['debug'])
124 124 rmap.minimization = False
125 125 rmap.explicit = False
126 126
127 127 from rhodecode.lib.utils2 import str2bool
128 128 from rhodecode.model import repo, repo_group
129 129
130 130 def check_repo(environ, match_dict):
131 131 """
132 132 check for valid repository for proper 404 handling
133 133
134 134 :param environ:
135 135 :param match_dict:
136 136 """
137 137 repo_name = match_dict.get('repo_name')
138 138
139 139 if match_dict.get('f_path'):
140 140 # fix for multiple initial slashes that causes errors
141 141 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
142 142 repo_model = repo.RepoModel()
143 143 by_name_match = repo_model.get_by_repo_name(repo_name)
144 144 # if we match quickly from database, short circuit the operation,
145 145 # and validate repo based on the type.
146 146 if by_name_match:
147 147 return True
148 148
149 149 by_id_match = repo_model.get_repo_by_id(repo_name)
150 150 if by_id_match:
151 151 repo_name = by_id_match.repo_name
152 152 match_dict['repo_name'] = repo_name
153 153 return True
154 154
155 155 return False
156 156
157 157 def check_group(environ, match_dict):
158 158 """
159 159 check for valid repository group path for proper 404 handling
160 160
161 161 :param environ:
162 162 :param match_dict:
163 163 """
164 164 repo_group_name = match_dict.get('group_name')
165 165 repo_group_model = repo_group.RepoGroupModel()
166 166 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
167 167 if by_name_match:
168 168 return True
169 169
170 170 return False
171 171
172 172 def check_user_group(environ, match_dict):
173 173 """
174 174 check for valid user group for proper 404 handling
175 175
176 176 :param environ:
177 177 :param match_dict:
178 178 """
179 179 return True
180 180
181 181 def check_int(environ, match_dict):
182 182 return match_dict.get('id').isdigit()
183 183
184 184
185 185 #==========================================================================
186 186 # CUSTOM ROUTES HERE
187 187 #==========================================================================
188 188
189 189 # MAIN PAGE
190 190 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
191 191 rmap.connect('goto_switcher_data', '/_goto_data', controller='home',
192 192 action='goto_switcher_data')
193 193 rmap.connect('repo_list_data', '/_repos', controller='home',
194 194 action='repo_list_data')
195 195
196 196 rmap.connect('user_autocomplete_data', '/_users', controller='home',
197 197 action='user_autocomplete_data', jsroute=True)
198 198 rmap.connect('user_group_autocomplete_data', '/_user_groups', controller='home',
199 199 action='user_group_autocomplete_data', jsroute=True)
200 200
201 201 rmap.connect(
202 202 'user_profile', '/_profiles/{username}', controller='users',
203 203 action='user_profile')
204 204
205 205 # TODO: johbo: Static links, to be replaced by our redirection mechanism
206 206 rmap.connect('rst_help',
207 207 'http://docutils.sourceforge.net/docs/user/rst/quickref.html',
208 208 _static=True)
209 209 rmap.connect('markdown_help',
210 210 'http://daringfireball.net/projects/markdown/syntax',
211 211 _static=True)
212 212 rmap.connect('rhodecode_official', 'https://rhodecode.com', _static=True)
213 213 rmap.connect('rhodecode_support', 'https://rhodecode.com/help/', _static=True)
214 214 rmap.connect('rhodecode_translations', 'https://rhodecode.com/translate/enterprise', _static=True)
215 215 # TODO: anderson - making this a static link since redirect won't play
216 216 # nice with POST requests
217 217 rmap.connect('enterprise_license_convert_from_old',
218 218 'https://rhodecode.com/u/license-upgrade',
219 219 _static=True)
220 220
221 221 routing_links.connect_redirection_links(rmap)
222 222
223 223 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
224 224 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
225 225
226 226 # ADMIN REPOSITORY ROUTES
227 227 with rmap.submapper(path_prefix=ADMIN_PREFIX,
228 228 controller='admin/repos') as m:
229 229 m.connect('repos', '/repos',
230 230 action='create', conditions={'method': ['POST']})
231 231 m.connect('repos', '/repos',
232 232 action='index', conditions={'method': ['GET']})
233 233 m.connect('new_repo', '/create_repository', jsroute=True,
234 234 action='create_repository', conditions={'method': ['GET']})
235 235 m.connect('/repos/{repo_name}',
236 236 action='update', conditions={'method': ['PUT'],
237 237 'function': check_repo},
238 238 requirements=URL_NAME_REQUIREMENTS)
239 239 m.connect('delete_repo', '/repos/{repo_name}',
240 240 action='delete', conditions={'method': ['DELETE']},
241 241 requirements=URL_NAME_REQUIREMENTS)
242 242 m.connect('repo', '/repos/{repo_name}',
243 243 action='show', conditions={'method': ['GET'],
244 244 'function': check_repo},
245 245 requirements=URL_NAME_REQUIREMENTS)
246 246
247 247 # ADMIN REPOSITORY GROUPS ROUTES
248 248 with rmap.submapper(path_prefix=ADMIN_PREFIX,
249 249 controller='admin/repo_groups') as m:
250 250 m.connect('repo_groups', '/repo_groups',
251 251 action='create', conditions={'method': ['POST']})
252 252 m.connect('repo_groups', '/repo_groups',
253 253 action='index', conditions={'method': ['GET']})
254 254 m.connect('new_repo_group', '/repo_groups/new',
255 255 action='new', conditions={'method': ['GET']})
256 256 m.connect('update_repo_group', '/repo_groups/{group_name}',
257 257 action='update', conditions={'method': ['PUT'],
258 258 'function': check_group},
259 259 requirements=URL_NAME_REQUIREMENTS)
260 260
261 261 # EXTRAS REPO GROUP ROUTES
262 262 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
263 263 action='edit',
264 264 conditions={'method': ['GET'], 'function': check_group},
265 265 requirements=URL_NAME_REQUIREMENTS)
266 266 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
267 267 action='edit',
268 268 conditions={'method': ['PUT'], 'function': check_group},
269 269 requirements=URL_NAME_REQUIREMENTS)
270 270
271 271 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
272 272 action='edit_repo_group_advanced',
273 273 conditions={'method': ['GET'], 'function': check_group},
274 274 requirements=URL_NAME_REQUIREMENTS)
275 275 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
276 276 action='edit_repo_group_advanced',
277 277 conditions={'method': ['PUT'], 'function': check_group},
278 278 requirements=URL_NAME_REQUIREMENTS)
279 279
280 280 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
281 281 action='edit_repo_group_perms',
282 282 conditions={'method': ['GET'], 'function': check_group},
283 283 requirements=URL_NAME_REQUIREMENTS)
284 284 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
285 285 action='update_perms',
286 286 conditions={'method': ['PUT'], 'function': check_group},
287 287 requirements=URL_NAME_REQUIREMENTS)
288 288
289 289 m.connect('delete_repo_group', '/repo_groups/{group_name}',
290 290 action='delete', conditions={'method': ['DELETE'],
291 291 'function': check_group},
292 292 requirements=URL_NAME_REQUIREMENTS)
293 293
294 294 # ADMIN USER ROUTES
295 295 with rmap.submapper(path_prefix=ADMIN_PREFIX,
296 296 controller='admin/users') as m:
297 297 m.connect('users', '/users',
298 298 action='create', conditions={'method': ['POST']})
299 299 m.connect('users', '/users',
300 300 action='index', conditions={'method': ['GET']})
301 301 m.connect('new_user', '/users/new',
302 302 action='new', conditions={'method': ['GET']})
303 303 m.connect('update_user', '/users/{user_id}',
304 304 action='update', conditions={'method': ['PUT']})
305 305 m.connect('delete_user', '/users/{user_id}',
306 306 action='delete', conditions={'method': ['DELETE']})
307 307 m.connect('edit_user', '/users/{user_id}/edit',
308 308 action='edit', conditions={'method': ['GET']}, jsroute=True)
309 309 m.connect('user', '/users/{user_id}',
310 310 action='show', conditions={'method': ['GET']})
311 311 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
312 312 action='reset_password', conditions={'method': ['POST']})
313 313 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
314 314 action='create_personal_repo_group', conditions={'method': ['POST']})
315 315
316 316 # EXTRAS USER ROUTES
317 317 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
318 318 action='edit_advanced', conditions={'method': ['GET']})
319 319 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
320 320 action='update_advanced', conditions={'method': ['PUT']})
321 321
322 322 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
323 323 action='edit_auth_tokens', conditions={'method': ['GET']})
324 324 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
325 325 action='add_auth_token', conditions={'method': ['PUT']})
326 326 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
327 327 action='delete_auth_token', conditions={'method': ['DELETE']})
328 328
329 329 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
330 330 action='edit_global_perms', conditions={'method': ['GET']})
331 331 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
332 332 action='update_global_perms', conditions={'method': ['PUT']})
333 333
334 334 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
335 335 action='edit_perms_summary', conditions={'method': ['GET']})
336 336
337 337 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
338 338 action='edit_emails', conditions={'method': ['GET']})
339 339 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
340 340 action='add_email', conditions={'method': ['PUT']})
341 341 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
342 342 action='delete_email', conditions={'method': ['DELETE']})
343 343
344 344 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
345 345 action='edit_ips', conditions={'method': ['GET']})
346 346 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
347 347 action='add_ip', conditions={'method': ['PUT']})
348 348 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
349 349 action='delete_ip', conditions={'method': ['DELETE']})
350 350
351 351 # ADMIN USER GROUPS REST ROUTES
352 352 with rmap.submapper(path_prefix=ADMIN_PREFIX,
353 353 controller='admin/user_groups') as m:
354 354 m.connect('users_groups', '/user_groups',
355 355 action='create', conditions={'method': ['POST']})
356 356 m.connect('users_groups', '/user_groups',
357 357 action='index', conditions={'method': ['GET']})
358 358 m.connect('new_users_group', '/user_groups/new',
359 359 action='new', conditions={'method': ['GET']})
360 360 m.connect('update_users_group', '/user_groups/{user_group_id}',
361 361 action='update', conditions={'method': ['PUT']})
362 362 m.connect('delete_users_group', '/user_groups/{user_group_id}',
363 363 action='delete', conditions={'method': ['DELETE']})
364 364 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
365 365 action='edit', conditions={'method': ['GET']},
366 366 function=check_user_group)
367 367
368 368 # EXTRAS USER GROUP ROUTES
369 369 m.connect('edit_user_group_global_perms',
370 370 '/user_groups/{user_group_id}/edit/global_permissions',
371 371 action='edit_global_perms', conditions={'method': ['GET']})
372 372 m.connect('edit_user_group_global_perms',
373 373 '/user_groups/{user_group_id}/edit/global_permissions',
374 374 action='update_global_perms', conditions={'method': ['PUT']})
375 375 m.connect('edit_user_group_perms_summary',
376 376 '/user_groups/{user_group_id}/edit/permissions_summary',
377 377 action='edit_perms_summary', conditions={'method': ['GET']})
378 378
379 379 m.connect('edit_user_group_perms',
380 380 '/user_groups/{user_group_id}/edit/permissions',
381 381 action='edit_perms', conditions={'method': ['GET']})
382 382 m.connect('edit_user_group_perms',
383 383 '/user_groups/{user_group_id}/edit/permissions',
384 384 action='update_perms', conditions={'method': ['PUT']})
385 385
386 386 m.connect('edit_user_group_advanced',
387 387 '/user_groups/{user_group_id}/edit/advanced',
388 388 action='edit_advanced', conditions={'method': ['GET']})
389 389
390 390 m.connect('edit_user_group_members',
391 391 '/user_groups/{user_group_id}/edit/members', jsroute=True,
392 392 action='user_group_members', conditions={'method': ['GET']})
393 393
394 394 # ADMIN PERMISSIONS ROUTES
395 395 with rmap.submapper(path_prefix=ADMIN_PREFIX,
396 396 controller='admin/permissions') as m:
397 397 m.connect('admin_permissions_application', '/permissions/application',
398 398 action='permission_application_update', conditions={'method': ['POST']})
399 399 m.connect('admin_permissions_application', '/permissions/application',
400 400 action='permission_application', conditions={'method': ['GET']})
401 401
402 402 m.connect('admin_permissions_global', '/permissions/global',
403 403 action='permission_global_update', conditions={'method': ['POST']})
404 404 m.connect('admin_permissions_global', '/permissions/global',
405 405 action='permission_global', conditions={'method': ['GET']})
406 406
407 407 m.connect('admin_permissions_object', '/permissions/object',
408 408 action='permission_objects_update', conditions={'method': ['POST']})
409 409 m.connect('admin_permissions_object', '/permissions/object',
410 410 action='permission_objects', conditions={'method': ['GET']})
411 411
412 412 m.connect('admin_permissions_ips', '/permissions/ips',
413 413 action='permission_ips', conditions={'method': ['POST']})
414 414 m.connect('admin_permissions_ips', '/permissions/ips',
415 415 action='permission_ips', conditions={'method': ['GET']})
416 416
417 417 m.connect('admin_permissions_overview', '/permissions/overview',
418 418 action='permission_perms', conditions={'method': ['GET']})
419 419
420 420 # ADMIN DEFAULTS REST ROUTES
421 421 with rmap.submapper(path_prefix=ADMIN_PREFIX,
422 422 controller='admin/defaults') as m:
423 423 m.connect('admin_defaults_repositories', '/defaults/repositories',
424 424 action='update_repository_defaults', conditions={'method': ['POST']})
425 425 m.connect('admin_defaults_repositories', '/defaults/repositories',
426 426 action='index', conditions={'method': ['GET']})
427 427
428 428 # ADMIN DEBUG STYLE ROUTES
429 429 if str2bool(config.get('debug_style')):
430 430 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
431 431 controller='debug_style') as m:
432 432 m.connect('debug_style_home', '',
433 433 action='index', conditions={'method': ['GET']})
434 434 m.connect('debug_style_template', '/t/{t_path}',
435 435 action='template', conditions={'method': ['GET']})
436 436
437 437 # ADMIN SETTINGS ROUTES
438 438 with rmap.submapper(path_prefix=ADMIN_PREFIX,
439 439 controller='admin/settings') as m:
440 440
441 441 # default
442 442 m.connect('admin_settings', '/settings',
443 443 action='settings_global_update',
444 444 conditions={'method': ['POST']})
445 445 m.connect('admin_settings', '/settings',
446 446 action='settings_global', conditions={'method': ['GET']})
447 447
448 448 m.connect('admin_settings_vcs', '/settings/vcs',
449 449 action='settings_vcs_update',
450 450 conditions={'method': ['POST']})
451 451 m.connect('admin_settings_vcs', '/settings/vcs',
452 452 action='settings_vcs',
453 453 conditions={'method': ['GET']})
454 454 m.connect('admin_settings_vcs', '/settings/vcs',
455 455 action='delete_svn_pattern',
456 456 conditions={'method': ['DELETE']})
457 457
458 458 m.connect('admin_settings_mapping', '/settings/mapping',
459 459 action='settings_mapping_update',
460 460 conditions={'method': ['POST']})
461 461 m.connect('admin_settings_mapping', '/settings/mapping',
462 462 action='settings_mapping', conditions={'method': ['GET']})
463 463
464 464 m.connect('admin_settings_global', '/settings/global',
465 465 action='settings_global_update',
466 466 conditions={'method': ['POST']})
467 467 m.connect('admin_settings_global', '/settings/global',
468 468 action='settings_global', conditions={'method': ['GET']})
469 469
470 470 m.connect('admin_settings_visual', '/settings/visual',
471 471 action='settings_visual_update',
472 472 conditions={'method': ['POST']})
473 473 m.connect('admin_settings_visual', '/settings/visual',
474 474 action='settings_visual', conditions={'method': ['GET']})
475 475
476 476 m.connect('admin_settings_issuetracker',
477 477 '/settings/issue-tracker', action='settings_issuetracker',
478 478 conditions={'method': ['GET']})
479 479 m.connect('admin_settings_issuetracker_save',
480 480 '/settings/issue-tracker/save',
481 481 action='settings_issuetracker_save',
482 482 conditions={'method': ['POST']})
483 483 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
484 484 action='settings_issuetracker_test',
485 485 conditions={'method': ['POST']})
486 486 m.connect('admin_issuetracker_delete',
487 487 '/settings/issue-tracker/delete',
488 488 action='settings_issuetracker_delete',
489 489 conditions={'method': ['DELETE']})
490 490
491 491 m.connect('admin_settings_email', '/settings/email',
492 492 action='settings_email_update',
493 493 conditions={'method': ['POST']})
494 494 m.connect('admin_settings_email', '/settings/email',
495 495 action='settings_email', conditions={'method': ['GET']})
496 496
497 497 m.connect('admin_settings_hooks', '/settings/hooks',
498 498 action='settings_hooks_update',
499 499 conditions={'method': ['POST', 'DELETE']})
500 500 m.connect('admin_settings_hooks', '/settings/hooks',
501 501 action='settings_hooks', conditions={'method': ['GET']})
502 502
503 503 m.connect('admin_settings_search', '/settings/search',
504 504 action='settings_search', conditions={'method': ['GET']})
505 505
506 506 m.connect('admin_settings_system', '/settings/system',
507 507 action='settings_system', conditions={'method': ['GET']})
508 508
509 509 m.connect('admin_settings_system_update', '/settings/system/updates',
510 510 action='settings_system_update', conditions={'method': ['GET']})
511 511
512 512 m.connect('admin_settings_supervisor', '/settings/supervisor',
513 513 action='settings_supervisor', conditions={'method': ['GET']})
514 514 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
515 515 action='settings_supervisor_log', conditions={'method': ['GET']})
516 516
517 517 m.connect('admin_settings_labs', '/settings/labs',
518 518 action='settings_labs_update',
519 519 conditions={'method': ['POST']})
520 520 m.connect('admin_settings_labs', '/settings/labs',
521 521 action='settings_labs', conditions={'method': ['GET']})
522 522
523 523 # ADMIN MY ACCOUNT
524 524 with rmap.submapper(path_prefix=ADMIN_PREFIX,
525 525 controller='admin/my_account') as m:
526 526
527 527 m.connect('my_account', '/my_account',
528 528 action='my_account', conditions={'method': ['GET']})
529 529 m.connect('my_account_edit', '/my_account/edit',
530 530 action='my_account_edit', conditions={'method': ['GET']})
531 531 m.connect('my_account', '/my_account',
532 532 action='my_account_update', conditions={'method': ['POST']})
533 533
534 534 m.connect('my_account_password', '/my_account/password',
535 535 action='my_account_password', conditions={'method': ['GET', 'POST']})
536 536
537 537 m.connect('my_account_repos', '/my_account/repos',
538 538 action='my_account_repos', conditions={'method': ['GET']})
539 539
540 540 m.connect('my_account_watched', '/my_account/watched',
541 541 action='my_account_watched', conditions={'method': ['GET']})
542 542
543 543 m.connect('my_account_pullrequests', '/my_account/pull_requests',
544 544 action='my_account_pullrequests', conditions={'method': ['GET']})
545 545
546 546 m.connect('my_account_perms', '/my_account/perms',
547 547 action='my_account_perms', conditions={'method': ['GET']})
548 548
549 549 m.connect('my_account_emails', '/my_account/emails',
550 550 action='my_account_emails', conditions={'method': ['GET']})
551 551 m.connect('my_account_emails', '/my_account/emails',
552 552 action='my_account_emails_add', conditions={'method': ['POST']})
553 553 m.connect('my_account_emails', '/my_account/emails',
554 554 action='my_account_emails_delete', conditions={'method': ['DELETE']})
555 555
556 556 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
557 557 action='my_account_auth_tokens', conditions={'method': ['GET']})
558 558 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
559 559 action='my_account_auth_tokens_add', conditions={'method': ['POST']})
560 560 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
561 561 action='my_account_auth_tokens_delete', conditions={'method': ['DELETE']})
562 562 m.connect('my_account_notifications', '/my_account/notifications',
563 563 action='my_notifications',
564 564 conditions={'method': ['GET']})
565 565 m.connect('my_account_notifications_toggle_visibility',
566 566 '/my_account/toggle_visibility',
567 567 action='my_notifications_toggle_visibility',
568 568 conditions={'method': ['POST']})
569 569
570 570 # NOTIFICATION REST ROUTES
571 571 with rmap.submapper(path_prefix=ADMIN_PREFIX,
572 572 controller='admin/notifications') as m:
573 573 m.connect('notifications', '/notifications',
574 574 action='index', conditions={'method': ['GET']})
575 575 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
576 576 action='mark_all_read', conditions={'method': ['POST']})
577 577 m.connect('/notifications/{notification_id}',
578 578 action='update', conditions={'method': ['PUT']})
579 579 m.connect('/notifications/{notification_id}',
580 580 action='delete', conditions={'method': ['DELETE']})
581 581 m.connect('notification', '/notifications/{notification_id}',
582 582 action='show', conditions={'method': ['GET']})
583 583
584 584 # ADMIN GIST
585 585 with rmap.submapper(path_prefix=ADMIN_PREFIX,
586 586 controller='admin/gists') as m:
587 587 m.connect('gists', '/gists',
588 588 action='create', conditions={'method': ['POST']})
589 589 m.connect('gists', '/gists', jsroute=True,
590 590 action='index', conditions={'method': ['GET']})
591 591 m.connect('new_gist', '/gists/new', jsroute=True,
592 592 action='new', conditions={'method': ['GET']})
593 593
594 594 m.connect('/gists/{gist_id}',
595 595 action='delete', conditions={'method': ['DELETE']})
596 596 m.connect('edit_gist', '/gists/{gist_id}/edit',
597 597 action='edit_form', conditions={'method': ['GET']})
598 598 m.connect('edit_gist', '/gists/{gist_id}/edit',
599 599 action='edit', conditions={'method': ['POST']})
600 600 m.connect(
601 601 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
602 602 action='check_revision', conditions={'method': ['GET']})
603 603
604 604 m.connect('gist', '/gists/{gist_id}',
605 605 action='show', conditions={'method': ['GET']})
606 606 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
607 607 revision='tip',
608 608 action='show', conditions={'method': ['GET']})
609 609 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
610 610 revision='tip',
611 611 action='show', conditions={'method': ['GET']})
612 612 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
613 613 revision='tip',
614 614 action='show', conditions={'method': ['GET']},
615 615 requirements=URL_NAME_REQUIREMENTS)
616 616
617 617 # ADMIN MAIN PAGES
618 618 with rmap.submapper(path_prefix=ADMIN_PREFIX,
619 619 controller='admin/admin') as m:
620 620 m.connect('admin_home', '', action='index')
621 621 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
622 622 action='add_repo')
623 623 m.connect(
624 624 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
625 625 action='pull_requests')
626 626 m.connect(
627 627 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
628 628 action='pull_requests')
629 629 m.connect(
630 630 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
631 631 action='pull_requests')
632 632
633 633 # USER JOURNAL
634 634 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
635 635 controller='journal', action='index')
636 636 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
637 637 controller='journal', action='journal_rss')
638 638 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
639 639 controller='journal', action='journal_atom')
640 640
641 641 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
642 642 controller='journal', action='public_journal')
643 643
644 644 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
645 645 controller='journal', action='public_journal_rss')
646 646
647 647 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
648 648 controller='journal', action='public_journal_rss')
649 649
650 650 rmap.connect('public_journal_atom',
651 651 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
652 652 action='public_journal_atom')
653 653
654 654 rmap.connect('public_journal_atom_old',
655 655 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
656 656 action='public_journal_atom')
657 657
658 658 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
659 659 controller='journal', action='toggle_following', jsroute=True,
660 660 conditions={'method': ['POST']})
661 661
662 662 # FULL TEXT SEARCH
663 663 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
664 664 controller='search')
665 665 rmap.connect('search_repo_home', '/{repo_name}/search',
666 666 controller='search',
667 667 action='index',
668 668 conditions={'function': check_repo},
669 669 requirements=URL_NAME_REQUIREMENTS)
670 670
671 671 # FEEDS
672 672 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
673 673 controller='feed', action='rss',
674 674 conditions={'function': check_repo},
675 675 requirements=URL_NAME_REQUIREMENTS)
676 676
677 677 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
678 678 controller='feed', action='atom',
679 679 conditions={'function': check_repo},
680 680 requirements=URL_NAME_REQUIREMENTS)
681 681
682 682 #==========================================================================
683 683 # REPOSITORY ROUTES
684 684 #==========================================================================
685 685
686 686 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
687 687 controller='admin/repos', action='repo_creating',
688 688 requirements=URL_NAME_REQUIREMENTS)
689 689 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
690 690 controller='admin/repos', action='repo_check',
691 691 requirements=URL_NAME_REQUIREMENTS)
692 692
693 693 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
694 694 controller='summary', action='repo_stats',
695 695 conditions={'function': check_repo},
696 696 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
697 697
698 698 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
699 699 controller='summary', action='repo_refs_data', jsroute=True,
700 700 requirements=URL_NAME_REQUIREMENTS)
701 701 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
702 702 controller='summary', action='repo_refs_changelog_data',
703 703 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
704 704 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
705 705 controller='summary', action='repo_default_reviewers_data',
706 706 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
707 707
708 708 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
709 709 controller='changeset', revision='tip', jsroute=True,
710 710 conditions={'function': check_repo},
711 711 requirements=URL_NAME_REQUIREMENTS)
712 712 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
713 713 controller='changeset', revision='tip', action='changeset_children',
714 714 conditions={'function': check_repo},
715 715 requirements=URL_NAME_REQUIREMENTS)
716 716 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
717 717 controller='changeset', revision='tip', action='changeset_parents',
718 718 conditions={'function': check_repo},
719 719 requirements=URL_NAME_REQUIREMENTS)
720 720
721 721 # repo edit options
722 722 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
723 723 controller='admin/repos', action='edit',
724 724 conditions={'method': ['GET'], 'function': check_repo},
725 725 requirements=URL_NAME_REQUIREMENTS)
726 726
727 727 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
728 728 jsroute=True,
729 729 controller='admin/repos', action='edit_permissions',
730 730 conditions={'method': ['GET'], 'function': check_repo},
731 731 requirements=URL_NAME_REQUIREMENTS)
732 732 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
733 733 controller='admin/repos', action='edit_permissions_update',
734 734 conditions={'method': ['PUT'], 'function': check_repo},
735 735 requirements=URL_NAME_REQUIREMENTS)
736 736
737 737 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
738 738 controller='admin/repos', action='edit_fields',
739 739 conditions={'method': ['GET'], 'function': check_repo},
740 740 requirements=URL_NAME_REQUIREMENTS)
741 741 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
742 742 controller='admin/repos', action='create_repo_field',
743 743 conditions={'method': ['PUT'], 'function': check_repo},
744 744 requirements=URL_NAME_REQUIREMENTS)
745 745 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
746 746 controller='admin/repos', action='delete_repo_field',
747 747 conditions={'method': ['DELETE'], 'function': check_repo},
748 748 requirements=URL_NAME_REQUIREMENTS)
749 749
750 750 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
751 751 controller='admin/repos', action='edit_advanced',
752 752 conditions={'method': ['GET'], 'function': check_repo},
753 753 requirements=URL_NAME_REQUIREMENTS)
754 754
755 755 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
756 756 controller='admin/repos', action='edit_advanced_locking',
757 757 conditions={'method': ['PUT'], 'function': check_repo},
758 758 requirements=URL_NAME_REQUIREMENTS)
759 759 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
760 760 controller='admin/repos', action='toggle_locking',
761 761 conditions={'method': ['GET'], 'function': check_repo},
762 762 requirements=URL_NAME_REQUIREMENTS)
763 763
764 764 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
765 765 controller='admin/repos', action='edit_advanced_journal',
766 766 conditions={'method': ['PUT'], 'function': check_repo},
767 767 requirements=URL_NAME_REQUIREMENTS)
768 768
769 769 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
770 770 controller='admin/repos', action='edit_advanced_fork',
771 771 conditions={'method': ['PUT'], 'function': check_repo},
772 772 requirements=URL_NAME_REQUIREMENTS)
773 773
774 774 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
775 775 controller='admin/repos', action='edit_caches_form',
776 776 conditions={'method': ['GET'], 'function': check_repo},
777 777 requirements=URL_NAME_REQUIREMENTS)
778 778 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
779 779 controller='admin/repos', action='edit_caches',
780 780 conditions={'method': ['PUT'], 'function': check_repo},
781 781 requirements=URL_NAME_REQUIREMENTS)
782 782
783 783 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
784 784 controller='admin/repos', action='edit_remote_form',
785 785 conditions={'method': ['GET'], 'function': check_repo},
786 786 requirements=URL_NAME_REQUIREMENTS)
787 787 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
788 788 controller='admin/repos', action='edit_remote',
789 789 conditions={'method': ['PUT'], 'function': check_repo},
790 790 requirements=URL_NAME_REQUIREMENTS)
791 791
792 792 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
793 793 controller='admin/repos', action='edit_statistics_form',
794 794 conditions={'method': ['GET'], 'function': check_repo},
795 795 requirements=URL_NAME_REQUIREMENTS)
796 796 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
797 797 controller='admin/repos', action='edit_statistics',
798 798 conditions={'method': ['PUT'], 'function': check_repo},
799 799 requirements=URL_NAME_REQUIREMENTS)
800 800 rmap.connect('repo_settings_issuetracker',
801 801 '/{repo_name}/settings/issue-tracker',
802 802 controller='admin/repos', action='repo_issuetracker',
803 803 conditions={'method': ['GET'], 'function': check_repo},
804 804 requirements=URL_NAME_REQUIREMENTS)
805 805 rmap.connect('repo_issuetracker_test',
806 806 '/{repo_name}/settings/issue-tracker/test',
807 807 controller='admin/repos', action='repo_issuetracker_test',
808 808 conditions={'method': ['POST'], 'function': check_repo},
809 809 requirements=URL_NAME_REQUIREMENTS)
810 810 rmap.connect('repo_issuetracker_delete',
811 811 '/{repo_name}/settings/issue-tracker/delete',
812 812 controller='admin/repos', action='repo_issuetracker_delete',
813 813 conditions={'method': ['DELETE'], 'function': check_repo},
814 814 requirements=URL_NAME_REQUIREMENTS)
815 815 rmap.connect('repo_issuetracker_save',
816 816 '/{repo_name}/settings/issue-tracker/save',
817 817 controller='admin/repos', action='repo_issuetracker_save',
818 818 conditions={'method': ['POST'], 'function': check_repo},
819 819 requirements=URL_NAME_REQUIREMENTS)
820 820 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
821 821 controller='admin/repos', action='repo_settings_vcs_update',
822 822 conditions={'method': ['POST'], 'function': check_repo},
823 823 requirements=URL_NAME_REQUIREMENTS)
824 824 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
825 825 controller='admin/repos', action='repo_settings_vcs',
826 826 conditions={'method': ['GET'], 'function': check_repo},
827 827 requirements=URL_NAME_REQUIREMENTS)
828 828 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
829 829 controller='admin/repos', action='repo_delete_svn_pattern',
830 830 conditions={'method': ['DELETE'], 'function': check_repo},
831 831 requirements=URL_NAME_REQUIREMENTS)
832 832 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
833 833 controller='admin/repos', action='repo_settings_pullrequest',
834 834 conditions={'method': ['GET', 'POST'], 'function': check_repo},
835 835 requirements=URL_NAME_REQUIREMENTS)
836 836
837 837 # still working url for backward compat.
838 838 rmap.connect('raw_changeset_home_depraced',
839 839 '/{repo_name}/raw-changeset/{revision}',
840 840 controller='changeset', action='changeset_raw',
841 841 revision='tip', conditions={'function': check_repo},
842 842 requirements=URL_NAME_REQUIREMENTS)
843 843
844 844 # new URLs
845 845 rmap.connect('changeset_raw_home',
846 846 '/{repo_name}/changeset-diff/{revision}',
847 847 controller='changeset', action='changeset_raw',
848 848 revision='tip', conditions={'function': check_repo},
849 849 requirements=URL_NAME_REQUIREMENTS)
850 850
851 851 rmap.connect('changeset_patch_home',
852 852 '/{repo_name}/changeset-patch/{revision}',
853 853 controller='changeset', action='changeset_patch',
854 854 revision='tip', conditions={'function': check_repo},
855 855 requirements=URL_NAME_REQUIREMENTS)
856 856
857 857 rmap.connect('changeset_download_home',
858 858 '/{repo_name}/changeset-download/{revision}',
859 859 controller='changeset', action='changeset_download',
860 860 revision='tip', conditions={'function': check_repo},
861 861 requirements=URL_NAME_REQUIREMENTS)
862 862
863 863 rmap.connect('changeset_comment',
864 864 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
865 865 controller='changeset', revision='tip', action='comment',
866 866 conditions={'function': check_repo},
867 867 requirements=URL_NAME_REQUIREMENTS)
868 868
869 869 rmap.connect('changeset_comment_preview',
870 870 '/{repo_name}/changeset/comment/preview', jsroute=True,
871 871 controller='changeset', action='preview_comment',
872 872 conditions={'function': check_repo, 'method': ['POST']},
873 873 requirements=URL_NAME_REQUIREMENTS)
874 874
875 875 rmap.connect('changeset_comment_delete',
876 876 '/{repo_name}/changeset/comment/{comment_id}/delete',
877 877 controller='changeset', action='delete_comment',
878 878 conditions={'function': check_repo, 'method': ['DELETE']},
879 879 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
880 880
881 881 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
882 882 controller='changeset', action='changeset_info',
883 883 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
884 884
885 885 rmap.connect('compare_home',
886 886 '/{repo_name}/compare',
887 887 controller='compare', action='index',
888 888 conditions={'function': check_repo},
889 889 requirements=URL_NAME_REQUIREMENTS)
890 890
891 891 rmap.connect('compare_url',
892 892 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
893 893 controller='compare', action='compare',
894 894 conditions={'function': check_repo},
895 895 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
896 896
897 897 rmap.connect('pullrequest_home',
898 898 '/{repo_name}/pull-request/new', controller='pullrequests',
899 899 action='index', conditions={'function': check_repo,
900 900 'method': ['GET']},
901 901 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
902 902
903 903 rmap.connect('pullrequest',
904 904 '/{repo_name}/pull-request/new', controller='pullrequests',
905 905 action='create', conditions={'function': check_repo,
906 906 'method': ['POST']},
907 907 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
908 908
909 909 rmap.connect('pullrequest_repo_refs',
910 910 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
911 911 controller='pullrequests',
912 912 action='get_repo_refs',
913 913 conditions={'function': check_repo, 'method': ['GET']},
914 914 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
915 915
916 916 rmap.connect('pullrequest_repo_destinations',
917 917 '/{repo_name}/pull-request/repo-destinations',
918 918 controller='pullrequests',
919 919 action='get_repo_destinations',
920 920 conditions={'function': check_repo, 'method': ['GET']},
921 921 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
922 922
923 923 rmap.connect('pullrequest_show',
924 924 '/{repo_name}/pull-request/{pull_request_id}',
925 925 controller='pullrequests',
926 926 action='show', conditions={'function': check_repo,
927 927 'method': ['GET']},
928 928 requirements=URL_NAME_REQUIREMENTS)
929 929
930 930 rmap.connect('pullrequest_update',
931 931 '/{repo_name}/pull-request/{pull_request_id}',
932 932 controller='pullrequests',
933 933 action='update', conditions={'function': check_repo,
934 934 'method': ['PUT']},
935 935 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
936 936
937 937 rmap.connect('pullrequest_merge',
938 938 '/{repo_name}/pull-request/{pull_request_id}',
939 939 controller='pullrequests',
940 940 action='merge', conditions={'function': check_repo,
941 941 'method': ['POST']},
942 942 requirements=URL_NAME_REQUIREMENTS)
943 943
944 944 rmap.connect('pullrequest_delete',
945 945 '/{repo_name}/pull-request/{pull_request_id}',
946 946 controller='pullrequests',
947 947 action='delete', conditions={'function': check_repo,
948 948 'method': ['DELETE']},
949 949 requirements=URL_NAME_REQUIREMENTS)
950 950
951 951 rmap.connect('pullrequest_show_all',
952 952 '/{repo_name}/pull-request',
953 953 controller='pullrequests',
954 954 action='show_all', conditions={'function': check_repo,
955 955 'method': ['GET']},
956 956 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
957 957
958 958 rmap.connect('pullrequest_comment',
959 959 '/{repo_name}/pull-request-comment/{pull_request_id}',
960 960 controller='pullrequests',
961 961 action='comment', conditions={'function': check_repo,
962 962 'method': ['POST']},
963 963 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
964 964
965 965 rmap.connect('pullrequest_comment_delete',
966 966 '/{repo_name}/pull-request-comment/{comment_id}/delete',
967 967 controller='pullrequests', action='delete_comment',
968 968 conditions={'function': check_repo, 'method': ['DELETE']},
969 969 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
970 970
971 971 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
972 972 controller='summary', conditions={'function': check_repo},
973 973 requirements=URL_NAME_REQUIREMENTS)
974 974
975 975 rmap.connect('branches_home', '/{repo_name}/branches',
976 976 controller='branches', conditions={'function': check_repo},
977 977 requirements=URL_NAME_REQUIREMENTS)
978 978
979 979 rmap.connect('tags_home', '/{repo_name}/tags',
980 980 controller='tags', conditions={'function': check_repo},
981 981 requirements=URL_NAME_REQUIREMENTS)
982 982
983 983 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
984 984 controller='bookmarks', conditions={'function': check_repo},
985 985 requirements=URL_NAME_REQUIREMENTS)
986 986
987 987 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
988 988 controller='changelog', conditions={'function': check_repo},
989 989 requirements=URL_NAME_REQUIREMENTS)
990 990
991 991 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
992 992 controller='changelog', action='changelog_summary',
993 993 conditions={'function': check_repo},
994 994 requirements=URL_NAME_REQUIREMENTS)
995 995
996 996 rmap.connect('changelog_file_home',
997 997 '/{repo_name}/changelog/{revision}/{f_path}',
998 998 controller='changelog', f_path=None,
999 999 conditions={'function': check_repo},
1000 1000 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1001 1001
1002 1002 rmap.connect('changelog_details', '/{repo_name}/changelog_details/{cs}',
1003 1003 controller='changelog', action='changelog_details',
1004 1004 conditions={'function': check_repo},
1005 1005 requirements=URL_NAME_REQUIREMENTS)
1006 1006
1007 1007 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
1008 1008 controller='files', revision='tip', f_path='',
1009 1009 conditions={'function': check_repo},
1010 1010 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1011 1011
1012 1012 rmap.connect('files_home_simple_catchrev',
1013 1013 '/{repo_name}/files/{revision}',
1014 1014 controller='files', revision='tip', f_path='',
1015 1015 conditions={'function': check_repo},
1016 1016 requirements=URL_NAME_REQUIREMENTS)
1017 1017
1018 1018 rmap.connect('files_home_simple_catchall',
1019 1019 '/{repo_name}/files',
1020 1020 controller='files', revision='tip', f_path='',
1021 1021 conditions={'function': check_repo},
1022 1022 requirements=URL_NAME_REQUIREMENTS)
1023 1023
1024 1024 rmap.connect('files_history_home',
1025 1025 '/{repo_name}/history/{revision}/{f_path}',
1026 1026 controller='files', action='history', revision='tip', f_path='',
1027 1027 conditions={'function': check_repo},
1028 1028 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1029 1029
1030 1030 rmap.connect('files_authors_home',
1031 1031 '/{repo_name}/authors/{revision}/{f_path}',
1032 1032 controller='files', action='authors', revision='tip', f_path='',
1033 1033 conditions={'function': check_repo},
1034 1034 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1035 1035
1036 1036 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
1037 1037 controller='files', action='diff', f_path='',
1038 1038 conditions={'function': check_repo},
1039 1039 requirements=URL_NAME_REQUIREMENTS)
1040 1040
1041 1041 rmap.connect('files_diff_2way_home',
1042 1042 '/{repo_name}/diff-2way/{f_path}',
1043 1043 controller='files', action='diff_2way', f_path='',
1044 1044 conditions={'function': check_repo},
1045 1045 requirements=URL_NAME_REQUIREMENTS)
1046 1046
1047 1047 rmap.connect('files_rawfile_home',
1048 1048 '/{repo_name}/rawfile/{revision}/{f_path}',
1049 1049 controller='files', action='rawfile', revision='tip',
1050 1050 f_path='', conditions={'function': check_repo},
1051 1051 requirements=URL_NAME_REQUIREMENTS)
1052 1052
1053 1053 rmap.connect('files_raw_home',
1054 1054 '/{repo_name}/raw/{revision}/{f_path}',
1055 1055 controller='files', action='raw', revision='tip', f_path='',
1056 1056 conditions={'function': check_repo},
1057 1057 requirements=URL_NAME_REQUIREMENTS)
1058 1058
1059 1059 rmap.connect('files_render_home',
1060 1060 '/{repo_name}/render/{revision}/{f_path}',
1061 1061 controller='files', action='index', revision='tip', f_path='',
1062 1062 rendered=True, conditions={'function': check_repo},
1063 1063 requirements=URL_NAME_REQUIREMENTS)
1064 1064
1065 1065 rmap.connect('files_annotate_home',
1066 1066 '/{repo_name}/annotate/{revision}/{f_path}',
1067 1067 controller='files', action='index', revision='tip',
1068 1068 f_path='', annotate=True, conditions={'function': check_repo},
1069 requirements=URL_NAME_REQUIREMENTS)
1069 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1070 1070
1071 1071 rmap.connect('files_edit',
1072 1072 '/{repo_name}/edit/{revision}/{f_path}',
1073 1073 controller='files', action='edit', revision='tip',
1074 1074 f_path='',
1075 1075 conditions={'function': check_repo, 'method': ['POST']},
1076 1076 requirements=URL_NAME_REQUIREMENTS)
1077 1077
1078 1078 rmap.connect('files_edit_home',
1079 1079 '/{repo_name}/edit/{revision}/{f_path}',
1080 1080 controller='files', action='edit_home', revision='tip',
1081 1081 f_path='', conditions={'function': check_repo},
1082 1082 requirements=URL_NAME_REQUIREMENTS)
1083 1083
1084 1084 rmap.connect('files_add',
1085 1085 '/{repo_name}/add/{revision}/{f_path}',
1086 1086 controller='files', action='add', revision='tip',
1087 1087 f_path='',
1088 1088 conditions={'function': check_repo, 'method': ['POST']},
1089 1089 requirements=URL_NAME_REQUIREMENTS)
1090 1090
1091 1091 rmap.connect('files_add_home',
1092 1092 '/{repo_name}/add/{revision}/{f_path}',
1093 1093 controller='files', action='add_home', revision='tip',
1094 1094 f_path='', conditions={'function': check_repo},
1095 1095 requirements=URL_NAME_REQUIREMENTS)
1096 1096
1097 1097 rmap.connect('files_delete',
1098 1098 '/{repo_name}/delete/{revision}/{f_path}',
1099 1099 controller='files', action='delete', revision='tip',
1100 1100 f_path='',
1101 1101 conditions={'function': check_repo, 'method': ['POST']},
1102 1102 requirements=URL_NAME_REQUIREMENTS)
1103 1103
1104 1104 rmap.connect('files_delete_home',
1105 1105 '/{repo_name}/delete/{revision}/{f_path}',
1106 1106 controller='files', action='delete_home', revision='tip',
1107 1107 f_path='', conditions={'function': check_repo},
1108 1108 requirements=URL_NAME_REQUIREMENTS)
1109 1109
1110 1110 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1111 1111 controller='files', action='archivefile',
1112 1112 conditions={'function': check_repo},
1113 1113 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1114 1114
1115 1115 rmap.connect('files_nodelist_home',
1116 1116 '/{repo_name}/nodelist/{revision}/{f_path}',
1117 1117 controller='files', action='nodelist',
1118 1118 conditions={'function': check_repo},
1119 1119 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1120 1120
1121 1121 rmap.connect('files_nodetree_full',
1122 1122 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1123 1123 controller='files', action='nodetree_full',
1124 1124 conditions={'function': check_repo},
1125 1125 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1126 1126
1127 1127 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1128 1128 controller='forks', action='fork_create',
1129 1129 conditions={'function': check_repo, 'method': ['POST']},
1130 1130 requirements=URL_NAME_REQUIREMENTS)
1131 1131
1132 1132 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1133 1133 controller='forks', action='fork',
1134 1134 conditions={'function': check_repo},
1135 1135 requirements=URL_NAME_REQUIREMENTS)
1136 1136
1137 1137 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1138 1138 controller='forks', action='forks',
1139 1139 conditions={'function': check_repo},
1140 1140 requirements=URL_NAME_REQUIREMENTS)
1141 1141
1142 1142 rmap.connect('repo_followers_home', '/{repo_name}/followers',
1143 1143 controller='followers', action='followers',
1144 1144 conditions={'function': check_repo},
1145 1145 requirements=URL_NAME_REQUIREMENTS)
1146 1146
1147 1147 # must be here for proper group/repo catching pattern
1148 1148 _connect_with_slash(
1149 1149 rmap, 'repo_group_home', '/{group_name}',
1150 1150 controller='home', action='index_repo_group',
1151 1151 conditions={'function': check_group},
1152 1152 requirements=URL_NAME_REQUIREMENTS)
1153 1153
1154 1154 # catch all, at the end
1155 1155 _connect_with_slash(
1156 1156 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1157 1157 controller='summary', action='index',
1158 1158 conditions={'function': check_repo},
1159 1159 requirements=URL_NAME_REQUIREMENTS)
1160 1160
1161 1161 return rmap
1162 1162
1163 1163
1164 1164 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1165 1165 """
1166 1166 Connect a route with an optional trailing slash in `path`.
1167 1167 """
1168 1168 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1169 1169 mapper.connect(name, path, *args, **kwargs)
@@ -1,264 +1,270 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2016 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 """
22 22 Compare controller for showing differences between two commits/refs/tags etc.
23 23 """
24 24
25 25 import logging
26 26
27 27 from webob.exc import HTTPBadRequest
28 28 from pylons import request, tmpl_context as c, url
29 29 from pylons.controllers.util import redirect
30 30 from pylons.i18n.translation import _
31 31
32 32 from rhodecode.controllers.utils import parse_path_ref, get_commit_from_ref_name
33 33 from rhodecode.lib import helpers as h
34 34 from rhodecode.lib import diffs, codeblocks
35 35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 36 from rhodecode.lib.base import BaseRepoController, render
37 37 from rhodecode.lib.utils import safe_str
38 38 from rhodecode.lib.utils2 import safe_unicode, str2bool
39 39 from rhodecode.lib.vcs.exceptions import (
40 40 EmptyRepositoryError, RepositoryError, RepositoryRequirementError,
41 41 NodeDoesNotExistError)
42 42 from rhodecode.model.db import Repository, ChangesetStatus
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46
47 47 class CompareController(BaseRepoController):
48 48
49 49 def __before__(self):
50 50 super(CompareController, self).__before__()
51 51
52 52 def _get_commit_or_redirect(
53 53 self, ref, ref_type, repo, redirect_after=True, partial=False):
54 54 """
55 55 This is a safe way to get a commit. If an error occurs it
56 56 redirects to a commit with a proper message. If partial is set
57 57 then it does not do redirect raise and throws an exception instead.
58 58 """
59 59 try:
60 60 return get_commit_from_ref_name(repo, safe_str(ref), ref_type)
61 61 except EmptyRepositoryError:
62 62 if not redirect_after:
63 63 return repo.scm_instance().EMPTY_COMMIT
64 64 h.flash(h.literal(_('There are no commits yet')),
65 65 category='warning')
66 66 redirect(url('summary_home', repo_name=repo.repo_name))
67 67
68 68 except RepositoryError as e:
69 69 msg = safe_str(e)
70 70 log.exception(msg)
71 71 h.flash(msg, category='warning')
72 72 if not partial:
73 73 redirect(h.url('summary_home', repo_name=repo.repo_name))
74 74 raise HTTPBadRequest()
75 75
76 76 @LoginRequired()
77 77 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
78 78 'repository.admin')
79 79 def index(self, repo_name):
80 80 c.compare_home = True
81 81 c.commit_ranges = []
82 82 c.diffset = None
83 83 c.limited_diff = False
84 84 source_repo = c.rhodecode_db_repo.repo_name
85 85 target_repo = request.GET.get('target_repo', source_repo)
86 86 c.source_repo = Repository.get_by_repo_name(source_repo)
87 87 c.target_repo = Repository.get_by_repo_name(target_repo)
88 88 c.source_ref = c.target_ref = _('Select commit')
89 89 c.source_ref_type = ""
90 90 c.target_ref_type = ""
91 91 c.commit_statuses = ChangesetStatus.STATUSES
92 92 c.preview_mode = False
93 c.file_path = None
93 94 return render('compare/compare_diff.html')
94 95
95 96 @LoginRequired()
96 97 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
97 98 'repository.admin')
98 99 def compare(self, repo_name, source_ref_type, source_ref,
99 100 target_ref_type, target_ref):
100 101 # source_ref will be evaluated in source_repo
101 102 source_repo_name = c.rhodecode_db_repo.repo_name
102 103 source_path, source_id = parse_path_ref(source_ref)
103 104
104 105 # target_ref will be evaluated in target_repo
105 106 target_repo_name = request.GET.get('target_repo', source_repo_name)
106 target_path, target_id = parse_path_ref(target_ref)
107 target_path, target_id = parse_path_ref(
108 target_ref, default_path=request.GET.get('f_path', ''))
107 109
110 c.file_path = target_path
108 111 c.commit_statuses = ChangesetStatus.STATUSES
109 112
110 113 # if merge is True
111 114 # Show what changes since the shared ancestor commit of target/source
112 115 # the source would get if it was merged with target. Only commits
113 116 # which are in target but not in source will be shown.
114 117 merge = str2bool(request.GET.get('merge'))
115 118 # if merge is False
116 119 # Show a raw diff of source/target refs even if no ancestor exists
117 120
118
119 121 # c.fulldiff disables cut_off_limit
120 122 c.fulldiff = str2bool(request.GET.get('fulldiff'))
121 123
122 124 # if partial, returns just compare_commits.html (commits log)
123 125 partial = request.is_xhr
124 126
125 127 # swap url for compare_diff page
126 128 c.swap_url = h.url(
127 129 'compare_url',
128 130 repo_name=target_repo_name,
129 131 source_ref_type=target_ref_type,
130 132 source_ref=target_ref,
131 133 target_repo=source_repo_name,
132 134 target_ref_type=source_ref_type,
133 135 target_ref=source_ref,
134 merge=merge and '1' or '')
136 merge=merge and '1' or '',
137 f_path=target_path)
135 138
136 139 source_repo = Repository.get_by_repo_name(source_repo_name)
137 140 target_repo = Repository.get_by_repo_name(target_repo_name)
138 141
139 142 if source_repo is None:
140 143 msg = _('Could not find the original repo: %(repo)s') % {
141 144 'repo': source_repo}
142 145
143 146 log.error(msg)
144 147 h.flash(msg, category='error')
145 148 return redirect(url('compare_home', repo_name=c.repo_name))
146 149
147 150 if target_repo is None:
148 151 msg = _('Could not find the other repo: %(repo)s') % {
149 152 'repo': target_repo_name}
150 153 log.error(msg)
151 154 h.flash(msg, category='error')
152 155 return redirect(url('compare_home', repo_name=c.repo_name))
153 156
154 source_alias = source_repo.scm_instance().alias
155 target_alias = target_repo.scm_instance().alias
157 source_scm = source_repo.scm_instance()
158 target_scm = target_repo.scm_instance()
159
160 source_alias = source_scm.alias
161 target_alias = target_scm.alias
156 162 if source_alias != target_alias:
157 163 msg = _('The comparison of two different kinds of remote repos '
158 164 'is not available')
159 165 log.error(msg)
160 166 h.flash(msg, category='error')
161 167 return redirect(url('compare_home', repo_name=c.repo_name))
162 168
163 169 source_commit = self._get_commit_or_redirect(
164 170 ref=source_id, ref_type=source_ref_type, repo=source_repo,
165 171 partial=partial)
166 172 target_commit = self._get_commit_or_redirect(
167 173 ref=target_id, ref_type=target_ref_type, repo=target_repo,
168 174 partial=partial)
169 175
170 176 c.compare_home = False
171 177 c.source_repo = source_repo
172 178 c.target_repo = target_repo
173 179 c.source_ref = source_ref
174 180 c.target_ref = target_ref
175 181 c.source_ref_type = source_ref_type
176 182 c.target_ref_type = target_ref_type
177 183
178 source_scm = source_repo.scm_instance()
179 target_scm = target_repo.scm_instance()
180
181 184 pre_load = ["author", "branch", "date", "message"]
182 185 c.ancestor = None
183 186 try:
184 187 c.commit_ranges = source_scm.compare(
185 188 source_commit.raw_id, target_commit.raw_id,
186 189 target_scm, merge, pre_load=pre_load)
187 190 if merge:
188 191 c.ancestor = source_scm.get_common_ancestor(
189 192 source_commit.raw_id, target_commit.raw_id, target_scm)
190 193 except RepositoryRequirementError:
191 194 msg = _('Could not compare repos with different '
192 195 'large file settings')
193 196 log.error(msg)
194 197 if partial:
195 198 return msg
196 199 h.flash(msg, category='error')
197 200 return redirect(url('compare_home', repo_name=c.repo_name))
198 201
199 202 c.statuses = c.rhodecode_db_repo.statuses(
200 203 [x.raw_id for x in c.commit_ranges])
201 204
202 205 if partial: # for PR ajax commits loader
203 206 if not c.ancestor:
204 207 return '' # cannot merge if there is no ancestor
205 208 return render('compare/compare_commits.html')
206 209
207 210 if c.ancestor:
208 211 # case we want a simple diff without incoming commits,
209 212 # previewing what will be merged.
210 213 # Make the diff on target repo (which is known to have target_ref)
211 214 log.debug('Using ancestor %s as source_ref instead of %s'
212 215 % (c.ancestor, source_ref))
213 216 source_repo = target_repo
214 217 source_commit = target_repo.get_commit(commit_id=c.ancestor)
215 218
216 219 # diff_limit will cut off the whole diff if the limit is applied
217 220 # otherwise it will just hide the big files from the front-end
218 221 diff_limit = self.cut_off_limit_diff
219 222 file_limit = self.cut_off_limit_file
220 223
221 224 log.debug('calculating diff between '
222 225 'source_ref:%s and target_ref:%s for repo `%s`',
223 226 source_commit, target_commit,
224 227 safe_unicode(source_repo.scm_instance().path))
225 228
226 229 if source_commit.repository != target_commit.repository:
227 230 msg = _(
228 231 "Repositories unrelated. "
229 232 "Cannot compare commit %(commit1)s from repository %(repo1)s "
230 233 "with commit %(commit2)s from repository %(repo2)s.") % {
231 234 'commit1': h.show_id(source_commit),
232 235 'repo1': source_repo.repo_name,
233 236 'commit2': h.show_id(target_commit),
234 237 'repo2': target_repo.repo_name,
235 238 }
236 239 h.flash(msg, category='error')
237 240 raise HTTPBadRequest()
238 241
239 242 txtdiff = source_repo.scm_instance().get_diff(
240 243 commit1=source_commit, commit2=target_commit,
241 path1=source_path, path=target_path)
244 path=target_path, path1=source_path)
245
242 246 diff_processor = diffs.DiffProcessor(
243 247 txtdiff, format='newdiff', diff_limit=diff_limit,
244 248 file_limit=file_limit, show_full_diff=c.fulldiff)
245 249 _parsed = diff_processor.prepare()
246 250
247 251 def _node_getter(commit):
248 252 """ Returns a function that returns a node for a commit or None """
249 253 def get_node(fname):
250 254 try:
251 255 return commit.get_node(fname)
252 256 except NodeDoesNotExistError:
253 257 return None
254 258 return get_node
255 259
256 260 c.diffset = codeblocks.DiffSet(
257 261 repo_name=source_repo.repo_name,
258 262 source_node_getter=_node_getter(source_commit),
259 263 target_node_getter=_node_getter(target_commit),
260 264 ).render_patchset(_parsed, source_ref, target_ref)
261 265
262 266 c.preview_mode = merge
267 c.source_commit = source_commit
268 c.target_commit = target_commit
263 269
264 270 return render('compare/compare_diff.html')
@@ -1,1127 +1,1061 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 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 """
22 22 Files controller for RhodeCode Enterprise
23 23 """
24 24
25 25 import itertools
26 26 import logging
27 27 import os
28 28 import shutil
29 29 import tempfile
30 30
31 31 from pylons import request, response, tmpl_context as c, url
32 32 from pylons.i18n.translation import _
33 33 from pylons.controllers.util import redirect
34 34 from webob.exc import HTTPNotFound, HTTPBadRequest
35 35
36 36 from rhodecode.controllers.utils import parse_path_ref
37 37 from rhodecode.lib import diffs, helpers as h, caches
38 38 from rhodecode.lib.compat import OrderedDict
39 39 from rhodecode.lib.codeblocks import (
40 40 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
41 41 from rhodecode.lib.utils import jsonify, action_logger
42 42 from rhodecode.lib.utils2 import (
43 43 convert_line_endings, detect_mode, safe_str, str2bool)
44 44 from rhodecode.lib.auth import (
45 45 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired, XHRRequired)
46 46 from rhodecode.lib.base import BaseRepoController, render
47 47 from rhodecode.lib.vcs import path as vcspath
48 48 from rhodecode.lib.vcs.backends.base import EmptyCommit
49 49 from rhodecode.lib.vcs.conf import settings
50 50 from rhodecode.lib.vcs.exceptions import (
51 51 RepositoryError, CommitDoesNotExistError, EmptyRepositoryError,
52 52 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,
53 53 NodeDoesNotExistError, CommitError, NodeError)
54 54 from rhodecode.lib.vcs.nodes import FileNode
55 55
56 56 from rhodecode.model.repo import RepoModel
57 57 from rhodecode.model.scm import ScmModel
58 58 from rhodecode.model.db import Repository
59 59
60 60 from rhodecode.controllers.changeset import (
61 61 _ignorews_url, _context_url, get_line_ctx, get_ignore_ws)
62 62 from rhodecode.lib.exceptions import NonRelativePathError
63 63
64 64 log = logging.getLogger(__name__)
65 65
66 66
67 67 class FilesController(BaseRepoController):
68 68
69 69 def __before__(self):
70 70 super(FilesController, self).__before__()
71 71 c.cut_off_limit = self.cut_off_limit_file
72 72
73 73 def _get_default_encoding(self):
74 74 enc_list = getattr(c, 'default_encodings', [])
75 75 return enc_list[0] if enc_list else 'UTF-8'
76 76
77 77 def __get_commit_or_redirect(self, commit_id, repo_name,
78 78 redirect_after=True):
79 79 """
80 80 This is a safe way to get commit. If an error occurs it redirects to
81 81 tip with proper message
82 82
83 83 :param commit_id: id of commit to fetch
84 84 :param repo_name: repo name to redirect after
85 85 :param redirect_after: toggle redirection
86 86 """
87 87 try:
88 88 return c.rhodecode_repo.get_commit(commit_id)
89 89 except EmptyRepositoryError:
90 90 if not redirect_after:
91 91 return None
92 92 url_ = url('files_add_home',
93 93 repo_name=c.repo_name,
94 94 revision=0, f_path='', anchor='edit')
95 95 if h.HasRepoPermissionAny(
96 96 'repository.write', 'repository.admin')(c.repo_name):
97 97 add_new = h.link_to(
98 98 _('Click here to add a new file.'),
99 99 url_, class_="alert-link")
100 100 else:
101 101 add_new = ""
102 102 h.flash(h.literal(
103 103 _('There are no files yet. %s') % add_new), category='warning')
104 104 redirect(h.url('summary_home', repo_name=repo_name))
105 105 except (CommitDoesNotExistError, LookupError):
106 106 msg = _('No such commit exists for this repository')
107 107 h.flash(msg, category='error')
108 108 raise HTTPNotFound()
109 109 except RepositoryError as e:
110 110 h.flash(safe_str(e), category='error')
111 111 raise HTTPNotFound()
112 112
113 113 def __get_filenode_or_redirect(self, repo_name, commit, path):
114 114 """
115 115 Returns file_node, if error occurs or given path is directory,
116 116 it'll redirect to top level path
117 117
118 118 :param repo_name: repo_name
119 119 :param commit: given commit
120 120 :param path: path to lookup
121 121 """
122 122 try:
123 123 file_node = commit.get_node(path)
124 124 if file_node.is_dir():
125 125 raise RepositoryError('The given path is a directory')
126 126 except CommitDoesNotExistError:
127 127 msg = _('No such commit exists for this repository')
128 128 log.exception(msg)
129 129 h.flash(msg, category='error')
130 130 raise HTTPNotFound()
131 131 except RepositoryError as e:
132 132 h.flash(safe_str(e), category='error')
133 133 raise HTTPNotFound()
134 134
135 135 return file_node
136 136
137 137 def __get_tree_cache_manager(self, repo_name, namespace_type):
138 138 _namespace = caches.get_repo_namespace_key(namespace_type, repo_name)
139 139 return caches.get_cache_manager('repo_cache_long', _namespace)
140 140
141 141 def _get_tree_at_commit(self, repo_name, commit_id, f_path,
142 142 full_load=False, force=False):
143 143 def _cached_tree():
144 144 log.debug('Generating cached file tree for %s, %s, %s',
145 145 repo_name, commit_id, f_path)
146 146 c.full_load = full_load
147 147 return render('files/files_browser_tree.html')
148 148
149 149 cache_manager = self.__get_tree_cache_manager(
150 150 repo_name, caches.FILE_TREE)
151 151
152 152 cache_key = caches.compute_key_from_params(
153 153 repo_name, commit_id, f_path)
154 154
155 155 if force:
156 156 # we want to force recompute of caches
157 157 cache_manager.remove_value(cache_key)
158 158
159 159 return cache_manager.get(cache_key, createfunc=_cached_tree)
160 160
161 161 def _get_nodelist_at_commit(self, repo_name, commit_id, f_path):
162 162 def _cached_nodes():
163 163 log.debug('Generating cached nodelist for %s, %s, %s',
164 164 repo_name, commit_id, f_path)
165 165 _d, _f = ScmModel().get_nodes(
166 166 repo_name, commit_id, f_path, flat=False)
167 167 return _d + _f
168 168
169 169 cache_manager = self.__get_tree_cache_manager(
170 170 repo_name, caches.FILE_SEARCH_TREE_META)
171 171
172 172 cache_key = caches.compute_key_from_params(
173 173 repo_name, commit_id, f_path)
174 174 return cache_manager.get(cache_key, createfunc=_cached_nodes)
175 175
176 176 @LoginRequired()
177 177 @HasRepoPermissionAnyDecorator(
178 178 'repository.read', 'repository.write', 'repository.admin')
179 179 def index(
180 180 self, repo_name, revision, f_path, annotate=False, rendered=False):
181 181 commit_id = revision
182 182
183 183 # redirect to given commit_id from form if given
184 184 get_commit_id = request.GET.get('at_rev', None)
185 185 if get_commit_id:
186 186 self.__get_commit_or_redirect(get_commit_id, repo_name)
187 187
188 188 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
189 189 c.branch = request.GET.get('branch', None)
190 190 c.f_path = f_path
191 191 c.annotate = annotate
192 192 # default is false, but .rst/.md files later are autorendered, we can
193 193 # overwrite autorendering by setting this GET flag
194 194 c.renderer = rendered or not request.GET.get('no-render', False)
195 195
196 196 # prev link
197 197 try:
198 198 prev_commit = c.commit.prev(c.branch)
199 199 c.prev_commit = prev_commit
200 200 c.url_prev = url('files_home', repo_name=c.repo_name,
201 201 revision=prev_commit.raw_id, f_path=f_path)
202 202 if c.branch:
203 203 c.url_prev += '?branch=%s' % c.branch
204 204 except (CommitDoesNotExistError, VCSError):
205 205 c.url_prev = '#'
206 206 c.prev_commit = EmptyCommit()
207 207
208 208 # next link
209 209 try:
210 210 next_commit = c.commit.next(c.branch)
211 211 c.next_commit = next_commit
212 212 c.url_next = url('files_home', repo_name=c.repo_name,
213 213 revision=next_commit.raw_id, f_path=f_path)
214 214 if c.branch:
215 215 c.url_next += '?branch=%s' % c.branch
216 216 except (CommitDoesNotExistError, VCSError):
217 217 c.url_next = '#'
218 218 c.next_commit = EmptyCommit()
219 219
220 220 # files or dirs
221 221 try:
222 222 c.file = c.commit.get_node(f_path)
223 223 c.file_author = True
224 224 c.file_tree = ''
225 225 if c.file.is_file():
226 226 c.file_source_page = 'true'
227 227 c.file_last_commit = c.file.last_commit
228 228 if c.file.size < self.cut_off_limit_file:
229 229 if c.annotate: # annotation has precedence over renderer
230 230 c.annotated_lines = filenode_as_annotated_lines_tokens(
231 231 c.file
232 232 )
233 233 else:
234 234 c.renderer = (
235 235 c.renderer and h.renderer_from_filename(c.file.path)
236 236 )
237 237 if not c.renderer:
238 238 c.lines = filenode_as_lines_tokens(c.file)
239 239
240 240 c.on_branch_head = self._is_valid_head(
241 241 commit_id, c.rhodecode_repo)
242 242 c.branch_or_raw_id = c.commit.branch or c.commit.raw_id
243 243
244 244 author = c.file_last_commit.author
245 245 c.authors = [(h.email(author),
246 246 h.person(author, 'username_or_name_or_email'))]
247 247 else:
248 248 c.file_source_page = 'false'
249 249 c.authors = []
250 250 c.file_tree = self._get_tree_at_commit(
251 251 repo_name, c.commit.raw_id, f_path)
252 252
253 253 except RepositoryError as e:
254 254 h.flash(safe_str(e), category='error')
255 255 raise HTTPNotFound()
256 256
257 257 if request.environ.get('HTTP_X_PJAX'):
258 258 return render('files/files_pjax.html')
259 259
260 260 return render('files/files.html')
261 261
262 262 @LoginRequired()
263 263 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
264 264 'repository.admin')
265 265 @jsonify
266 266 def history(self, repo_name, revision, f_path):
267 267 commit = self.__get_commit_or_redirect(revision, repo_name)
268 268 f_path = f_path
269 269 _file = commit.get_node(f_path)
270 270 if _file.is_file():
271 271 file_history, _hist = self._get_node_history(commit, f_path)
272 272
273 273 res = []
274 274 for obj in file_history:
275 275 res.append({
276 276 'text': obj[1],
277 277 'children': [{'id': o[0], 'text': o[1]} for o in obj[0]]
278 278 })
279 279
280 280 data = {
281 281 'more': False,
282 282 'results': res
283 283 }
284 284 return data
285 285
286 286 @LoginRequired()
287 287 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
288 288 'repository.admin')
289 289 def authors(self, repo_name, revision, f_path):
290 290 commit = self.__get_commit_or_redirect(revision, repo_name)
291 291 file_node = commit.get_node(f_path)
292 292 if file_node.is_file():
293 293 c.file_last_commit = file_node.last_commit
294 294 if request.GET.get('annotate') == '1':
295 295 # use _hist from annotation if annotation mode is on
296 296 commit_ids = set(x[1] for x in file_node.annotate)
297 297 _hist = (
298 298 c.rhodecode_repo.get_commit(commit_id)
299 299 for commit_id in commit_ids)
300 300 else:
301 301 _f_history, _hist = self._get_node_history(commit, f_path)
302 302 c.file_author = False
303 303 c.authors = []
304 304 for author in set(commit.author for commit in _hist):
305 305 c.authors.append((
306 306 h.email(author),
307 307 h.person(author, 'username_or_name_or_email')))
308 308 return render('files/file_authors_box.html')
309 309
310 310 @LoginRequired()
311 311 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
312 312 'repository.admin')
313 313 def rawfile(self, repo_name, revision, f_path):
314 314 """
315 315 Action for download as raw
316 316 """
317 317 commit = self.__get_commit_or_redirect(revision, repo_name)
318 318 file_node = self.__get_filenode_or_redirect(repo_name, commit, f_path)
319 319
320 320 response.content_disposition = 'attachment; filename=%s' % \
321 321 safe_str(f_path.split(Repository.NAME_SEP)[-1])
322 322
323 323 response.content_type = file_node.mimetype
324 324 charset = self._get_default_encoding()
325 325 if charset:
326 326 response.charset = charset
327 327
328 328 return file_node.content
329 329
330 330 @LoginRequired()
331 331 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
332 332 'repository.admin')
333 333 def raw(self, repo_name, revision, f_path):
334 334 """
335 335 Action for show as raw, some mimetypes are "rendered",
336 336 those include images, icons.
337 337 """
338 338 commit = self.__get_commit_or_redirect(revision, repo_name)
339 339 file_node = self.__get_filenode_or_redirect(repo_name, commit, f_path)
340 340
341 341 raw_mimetype_mapping = {
342 342 # map original mimetype to a mimetype used for "show as raw"
343 343 # you can also provide a content-disposition to override the
344 344 # default "attachment" disposition.
345 345 # orig_type: (new_type, new_dispo)
346 346
347 347 # show images inline:
348 348 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
349 349 # for example render an SVG with javascript inside or even render
350 350 # HTML.
351 351 'image/x-icon': ('image/x-icon', 'inline'),
352 352 'image/png': ('image/png', 'inline'),
353 353 'image/gif': ('image/gif', 'inline'),
354 354 'image/jpeg': ('image/jpeg', 'inline'),
355 355 }
356 356
357 357 mimetype = file_node.mimetype
358 358 try:
359 359 mimetype, dispo = raw_mimetype_mapping[mimetype]
360 360 except KeyError:
361 361 # we don't know anything special about this, handle it safely
362 362 if file_node.is_binary:
363 363 # do same as download raw for binary files
364 364 mimetype, dispo = 'application/octet-stream', 'attachment'
365 365 else:
366 366 # do not just use the original mimetype, but force text/plain,
367 367 # otherwise it would serve text/html and that might be unsafe.
368 368 # Note: underlying vcs library fakes text/plain mimetype if the
369 369 # mimetype can not be determined and it thinks it is not
370 370 # binary.This might lead to erroneous text display in some
371 371 # cases, but helps in other cases, like with text files
372 372 # without extension.
373 373 mimetype, dispo = 'text/plain', 'inline'
374 374
375 375 if dispo == 'attachment':
376 376 dispo = 'attachment; filename=%s' % safe_str(
377 377 f_path.split(os.sep)[-1])
378 378
379 379 response.content_disposition = dispo
380 380 response.content_type = mimetype
381 381 charset = self._get_default_encoding()
382 382 if charset:
383 383 response.charset = charset
384 384 return file_node.content
385 385
386 386 @CSRFRequired()
387 387 @LoginRequired()
388 388 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
389 389 def delete(self, repo_name, revision, f_path):
390 390 commit_id = revision
391 391
392 392 repo = c.rhodecode_db_repo
393 393 if repo.enable_locking and repo.locked[0]:
394 394 h.flash(_('This repository has been locked by %s on %s')
395 395 % (h.person_by_id(repo.locked[0]),
396 396 h.format_date(h.time_to_datetime(repo.locked[1]))),
397 397 'warning')
398 398 return redirect(h.url('files_home',
399 399 repo_name=repo_name, revision='tip'))
400 400
401 401 if not self._is_valid_head(commit_id, repo.scm_instance()):
402 402 h.flash(_('You can only delete files with revision '
403 403 'being a valid branch '), category='warning')
404 404 return redirect(h.url('files_home',
405 405 repo_name=repo_name, revision='tip',
406 406 f_path=f_path))
407 407
408 408 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
409 409 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
410 410
411 411 c.default_message = _(
412 412 'Deleted file %s via RhodeCode Enterprise') % (f_path)
413 413 c.f_path = f_path
414 414 node_path = f_path
415 415 author = c.rhodecode_user.full_contact
416 416 message = request.POST.get('message') or c.default_message
417 417 try:
418 418 nodes = {
419 419 node_path: {
420 420 'content': ''
421 421 }
422 422 }
423 423 self.scm_model.delete_nodes(
424 424 user=c.rhodecode_user.user_id, repo=c.rhodecode_db_repo,
425 425 message=message,
426 426 nodes=nodes,
427 427 parent_commit=c.commit,
428 428 author=author,
429 429 )
430 430
431 431 h.flash(_('Successfully deleted file %s') % f_path,
432 432 category='success')
433 433 except Exception:
434 434 msg = _('Error occurred during commit')
435 435 log.exception(msg)
436 436 h.flash(msg, category='error')
437 437 return redirect(url('changeset_home',
438 438 repo_name=c.repo_name, revision='tip'))
439 439
440 440 @LoginRequired()
441 441 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
442 442 def delete_home(self, repo_name, revision, f_path):
443 443 commit_id = revision
444 444
445 445 repo = c.rhodecode_db_repo
446 446 if repo.enable_locking and repo.locked[0]:
447 447 h.flash(_('This repository has been locked by %s on %s')
448 448 % (h.person_by_id(repo.locked[0]),
449 449 h.format_date(h.time_to_datetime(repo.locked[1]))),
450 450 'warning')
451 451 return redirect(h.url('files_home',
452 452 repo_name=repo_name, revision='tip'))
453 453
454 454 if not self._is_valid_head(commit_id, repo.scm_instance()):
455 455 h.flash(_('You can only delete files with revision '
456 456 'being a valid branch '), category='warning')
457 457 return redirect(h.url('files_home',
458 458 repo_name=repo_name, revision='tip',
459 459 f_path=f_path))
460 460
461 461 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
462 462 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
463 463
464 464 c.default_message = _(
465 465 'Deleted file %s via RhodeCode Enterprise') % (f_path)
466 466 c.f_path = f_path
467 467
468 468 return render('files/files_delete.html')
469 469
470 470 @CSRFRequired()
471 471 @LoginRequired()
472 472 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
473 473 def edit(self, repo_name, revision, f_path):
474 474 commit_id = revision
475 475
476 476 repo = c.rhodecode_db_repo
477 477 if repo.enable_locking and repo.locked[0]:
478 478 h.flash(_('This repository has been locked by %s on %s')
479 479 % (h.person_by_id(repo.locked[0]),
480 480 h.format_date(h.time_to_datetime(repo.locked[1]))),
481 481 'warning')
482 482 return redirect(h.url('files_home',
483 483 repo_name=repo_name, revision='tip'))
484 484
485 485 if not self._is_valid_head(commit_id, repo.scm_instance()):
486 486 h.flash(_('You can only edit files with revision '
487 487 'being a valid branch '), category='warning')
488 488 return redirect(h.url('files_home',
489 489 repo_name=repo_name, revision='tip',
490 490 f_path=f_path))
491 491
492 492 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
493 493 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
494 494
495 495 if c.file.is_binary:
496 496 return redirect(url('files_home', repo_name=c.repo_name,
497 497 revision=c.commit.raw_id, f_path=f_path))
498 498 c.default_message = _(
499 499 'Edited file %s via RhodeCode Enterprise') % (f_path)
500 500 c.f_path = f_path
501 501 old_content = c.file.content
502 502 sl = old_content.splitlines(1)
503 503 first_line = sl[0] if sl else ''
504 504
505 505 # modes: 0 - Unix, 1 - Mac, 2 - DOS
506 506 mode = detect_mode(first_line, 0)
507 507 content = convert_line_endings(request.POST.get('content', ''), mode)
508 508
509 509 message = request.POST.get('message') or c.default_message
510 510 org_f_path = c.file.unicode_path
511 511 filename = request.POST['filename']
512 512 org_filename = c.file.name
513 513
514 514 if content == old_content and filename == org_filename:
515 515 h.flash(_('No changes'), category='warning')
516 516 return redirect(url('changeset_home', repo_name=c.repo_name,
517 517 revision='tip'))
518 518 try:
519 519 mapping = {
520 520 org_f_path: {
521 521 'org_filename': org_f_path,
522 522 'filename': os.path.join(c.file.dir_path, filename),
523 523 'content': content,
524 524 'lexer': '',
525 525 'op': 'mod',
526 526 }
527 527 }
528 528
529 529 ScmModel().update_nodes(
530 530 user=c.rhodecode_user.user_id,
531 531 repo=c.rhodecode_db_repo,
532 532 message=message,
533 533 nodes=mapping,
534 534 parent_commit=c.commit,
535 535 )
536 536
537 537 h.flash(_('Successfully committed to %s') % f_path,
538 538 category='success')
539 539 except Exception:
540 540 msg = _('Error occurred during commit')
541 541 log.exception(msg)
542 542 h.flash(msg, category='error')
543 543 return redirect(url('changeset_home',
544 544 repo_name=c.repo_name, revision='tip'))
545 545
546 546 @LoginRequired()
547 547 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
548 548 def edit_home(self, repo_name, revision, f_path):
549 549 commit_id = revision
550 550
551 551 repo = c.rhodecode_db_repo
552 552 if repo.enable_locking and repo.locked[0]:
553 553 h.flash(_('This repository has been locked by %s on %s')
554 554 % (h.person_by_id(repo.locked[0]),
555 555 h.format_date(h.time_to_datetime(repo.locked[1]))),
556 556 'warning')
557 557 return redirect(h.url('files_home',
558 558 repo_name=repo_name, revision='tip'))
559 559
560 560 if not self._is_valid_head(commit_id, repo.scm_instance()):
561 561 h.flash(_('You can only edit files with revision '
562 562 'being a valid branch '), category='warning')
563 563 return redirect(h.url('files_home',
564 564 repo_name=repo_name, revision='tip',
565 565 f_path=f_path))
566 566
567 567 c.commit = self.__get_commit_or_redirect(commit_id, repo_name)
568 568 c.file = self.__get_filenode_or_redirect(repo_name, c.commit, f_path)
569 569
570 570 if c.file.is_binary:
571 571 return redirect(url('files_home', repo_name=c.repo_name,
572 572 revision=c.commit.raw_id, f_path=f_path))
573 573 c.default_message = _(
574 574 'Edited file %s via RhodeCode Enterprise') % (f_path)
575 575 c.f_path = f_path
576 576
577 577 return render('files/files_edit.html')
578 578
579 579 def _is_valid_head(self, commit_id, repo):
580 580 # check if commit is a branch identifier- basically we cannot
581 581 # create multiple heads via file editing
582 582 valid_heads = repo.branches.keys() + repo.branches.values()
583 583
584 584 if h.is_svn(repo) and not repo.is_empty():
585 585 # Note: Subversion only has one head, we add it here in case there
586 586 # is no branch matched.
587 587 valid_heads.append(repo.get_commit(commit_idx=-1).raw_id)
588 588
589 589 # check if commit is a branch name or branch hash
590 590 return commit_id in valid_heads
591 591
592 592 @CSRFRequired()
593 593 @LoginRequired()
594 594 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
595 595 def add(self, repo_name, revision, f_path):
596 596 repo = Repository.get_by_repo_name(repo_name)
597 597 if repo.enable_locking and repo.locked[0]:
598 598 h.flash(_('This repository has been locked by %s on %s')
599 599 % (h.person_by_id(repo.locked[0]),
600 600 h.format_date(h.time_to_datetime(repo.locked[1]))),
601 601 'warning')
602 602 return redirect(h.url('files_home',
603 603 repo_name=repo_name, revision='tip'))
604 604
605 605 r_post = request.POST
606 606
607 607 c.commit = self.__get_commit_or_redirect(
608 608 revision, repo_name, redirect_after=False)
609 609 if c.commit is None:
610 610 c.commit = EmptyCommit(alias=c.rhodecode_repo.alias)
611 611 c.default_message = (_('Added file via RhodeCode Enterprise'))
612 612 c.f_path = f_path
613 613 unix_mode = 0
614 614 content = convert_line_endings(r_post.get('content', ''), unix_mode)
615 615
616 616 message = r_post.get('message') or c.default_message
617 617 filename = r_post.get('filename')
618 618 location = r_post.get('location', '') # dir location
619 619 file_obj = r_post.get('upload_file', None)
620 620
621 621 if file_obj is not None and hasattr(file_obj, 'filename'):
622 622 filename = file_obj.filename
623 623 content = file_obj.file
624 624
625 625 if hasattr(content, 'file'):
626 626 # non posix systems store real file under file attr
627 627 content = content.file
628 628
629 629 # If there's no commit, redirect to repo summary
630 630 if type(c.commit) is EmptyCommit:
631 631 redirect_url = "summary_home"
632 632 else:
633 633 redirect_url = "changeset_home"
634 634
635 635 if not filename:
636 636 h.flash(_('No filename'), category='warning')
637 637 return redirect(url(redirect_url, repo_name=c.repo_name,
638 638 revision='tip'))
639 639
640 640 # extract the location from filename,
641 641 # allows using foo/bar.txt syntax to create subdirectories
642 642 subdir_loc = filename.rsplit('/', 1)
643 643 if len(subdir_loc) == 2:
644 644 location = os.path.join(location, subdir_loc[0])
645 645
646 646 # strip all crap out of file, just leave the basename
647 647 filename = os.path.basename(filename)
648 648 node_path = os.path.join(location, filename)
649 649 author = c.rhodecode_user.full_contact
650 650
651 651 try:
652 652 nodes = {
653 653 node_path: {
654 654 'content': content
655 655 }
656 656 }
657 657 self.scm_model.create_nodes(
658 658 user=c.rhodecode_user.user_id,
659 659 repo=c.rhodecode_db_repo,
660 660 message=message,
661 661 nodes=nodes,
662 662 parent_commit=c.commit,
663 663 author=author,
664 664 )
665 665
666 666 h.flash(_('Successfully committed to %s') % node_path,
667 667 category='success')
668 668 except NonRelativePathError as e:
669 669 h.flash(_(
670 670 'The location specified must be a relative path and must not '
671 671 'contain .. in the path'), category='warning')
672 672 return redirect(url('changeset_home', repo_name=c.repo_name,
673 673 revision='tip'))
674 674 except (NodeError, NodeAlreadyExistsError) as e:
675 675 h.flash(_(e), category='error')
676 676 except Exception:
677 677 msg = _('Error occurred during commit')
678 678 log.exception(msg)
679 679 h.flash(msg, category='error')
680 680 return redirect(url('changeset_home',
681 681 repo_name=c.repo_name, revision='tip'))
682 682
683 683 @LoginRequired()
684 684 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
685 685 def add_home(self, repo_name, revision, f_path):
686 686
687 687 repo = Repository.get_by_repo_name(repo_name)
688 688 if repo.enable_locking and repo.locked[0]:
689 689 h.flash(_('This repository has been locked by %s on %s')
690 690 % (h.person_by_id(repo.locked[0]),
691 691 h.format_date(h.time_to_datetime(repo.locked[1]))),
692 692 'warning')
693 693 return redirect(h.url('files_home',
694 694 repo_name=repo_name, revision='tip'))
695 695
696 696 c.commit = self.__get_commit_or_redirect(
697 697 revision, repo_name, redirect_after=False)
698 698 if c.commit is None:
699 699 c.commit = EmptyCommit(alias=c.rhodecode_repo.alias)
700 700 c.default_message = (_('Added file via RhodeCode Enterprise'))
701 701 c.f_path = f_path
702 702
703 703 return render('files/files_add.html')
704 704
705 705 @LoginRequired()
706 706 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
707 707 'repository.admin')
708 708 def archivefile(self, repo_name, fname):
709 709 fileformat = None
710 710 commit_id = None
711 711 ext = None
712 712 subrepos = request.GET.get('subrepos') == 'true'
713 713
714 714 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
715 715 archive_spec = fname.split(ext_data[1])
716 716 if len(archive_spec) == 2 and archive_spec[1] == '':
717 717 fileformat = a_type or ext_data[1]
718 718 commit_id = archive_spec[0]
719 719 ext = ext_data[1]
720 720
721 721 dbrepo = RepoModel().get_by_repo_name(repo_name)
722 722 if not dbrepo.enable_downloads:
723 723 return _('Downloads disabled')
724 724
725 725 try:
726 726 commit = c.rhodecode_repo.get_commit(commit_id)
727 727 content_type = settings.ARCHIVE_SPECS[fileformat][0]
728 728 except CommitDoesNotExistError:
729 729 return _('Unknown revision %s') % commit_id
730 730 except EmptyRepositoryError:
731 731 return _('Empty repository')
732 732 except KeyError:
733 733 return _('Unknown archive type')
734 734
735 735 # archive cache
736 736 from rhodecode import CONFIG
737 737
738 738 archive_name = '%s-%s%s%s' % (
739 739 safe_str(repo_name.replace('/', '_')),
740 740 '-sub' if subrepos else '',
741 741 safe_str(commit.short_id), ext)
742 742
743 743 use_cached_archive = False
744 744 archive_cache_enabled = CONFIG.get(
745 745 'archive_cache_dir') and not request.GET.get('no_cache')
746 746
747 747 if archive_cache_enabled:
748 748 # check if we it's ok to write
749 749 if not os.path.isdir(CONFIG['archive_cache_dir']):
750 750 os.makedirs(CONFIG['archive_cache_dir'])
751 751 cached_archive_path = os.path.join(
752 752 CONFIG['archive_cache_dir'], archive_name)
753 753 if os.path.isfile(cached_archive_path):
754 754 log.debug('Found cached archive in %s', cached_archive_path)
755 755 fd, archive = None, cached_archive_path
756 756 use_cached_archive = True
757 757 else:
758 758 log.debug('Archive %s is not yet cached', archive_name)
759 759
760 760 if not use_cached_archive:
761 761 # generate new archive
762 762 fd, archive = tempfile.mkstemp()
763 763 log.debug('Creating new temp archive in %s' % (archive,))
764 764 try:
765 765 commit.archive_repo(archive, kind=fileformat, subrepos=subrepos)
766 766 except ImproperArchiveTypeError:
767 767 return _('Unknown archive type')
768 768 if archive_cache_enabled:
769 769 # if we generated the archive and we have cache enabled
770 770 # let's use this for future
771 771 log.debug('Storing new archive in %s' % (cached_archive_path,))
772 772 shutil.move(archive, cached_archive_path)
773 773 archive = cached_archive_path
774 774
775 775 def get_chunked_archive(archive):
776 776 with open(archive, 'rb') as stream:
777 777 while True:
778 778 data = stream.read(16 * 1024)
779 779 if not data:
780 780 if fd: # fd means we used temporary file
781 781 os.close(fd)
782 782 if not archive_cache_enabled:
783 783 log.debug('Destroying temp archive %s', archive)
784 784 os.remove(archive)
785 785 break
786 786 yield data
787 787
788 788 # store download action
789 789 action_logger(user=c.rhodecode_user,
790 790 action='user_downloaded_archive:%s' % archive_name,
791 791 repo=repo_name, ipaddr=self.ip_addr, commit=True)
792 792 response.content_disposition = str(
793 793 'attachment; filename=%s' % archive_name)
794 794 response.content_type = str(content_type)
795 795
796 796 return get_chunked_archive(archive)
797 797
798 798 @LoginRequired()
799 799 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
800 800 'repository.admin')
801 801 def diff(self, repo_name, f_path):
802 ignore_whitespace = request.GET.get('ignorews') == '1'
803 line_context = request.GET.get('context', 3)
802
803 c.action = request.GET.get('diff')
804 804 diff1 = request.GET.get('diff1', '')
805 diff2 = request.GET.get('diff2', '')
805 806
806 807 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
807 808
808 diff2 = request.GET.get('diff2', '')
809 c.action = request.GET.get('diff')
810 c.no_changes = diff1 == diff2
811 c.f_path = f_path
812 c.big_diff = False
813 c.ignorews_url = _ignorews_url
814 c.context_url = _context_url
815 c.changes = OrderedDict()
816 c.changes[diff2] = []
809 ignore_whitespace = str2bool(request.GET.get('ignorews'))
810 line_context = request.GET.get('context', 3)
817 811
818 812 if not any((diff1, diff2)):
819 813 h.flash(
820 814 'Need query parameter "diff1" or "diff2" to generate a diff.',
821 815 category='error')
822 816 raise HTTPBadRequest()
823 817
824 # special case if we want a show commit_id only, it's impl here
825 # to reduce JS and callbacks
826
827 if request.GET.get('show_rev') and diff1:
828 if str2bool(request.GET.get('annotate', 'False')):
829 _url = url('files_annotate_home', repo_name=c.repo_name,
830 revision=diff1, f_path=path1)
831 else:
832 _url = url('files_home', repo_name=c.repo_name,
833 revision=diff1, f_path=path1)
834
835 return redirect(_url)
818 if c.action not in ['download', 'raw']:
819 # redirect to new view if we render diff
820 return redirect(
821 url('compare_url', repo_name=repo_name,
822 source_ref_type='rev',
823 source_ref=diff1,
824 target_repo=c.repo_name,
825 target_ref_type='rev',
826 target_ref=diff2,
827 f_path=f_path))
836 828
837 829 try:
838 830 node1 = self._get_file_node(diff1, path1)
839 831 node2 = self._get_file_node(diff2, f_path)
840 832 except (RepositoryError, NodeError):
841 833 log.exception("Exception while trying to get node from repository")
842 834 return redirect(url(
843 835 'files_home', repo_name=c.repo_name, f_path=f_path))
844 836
845 837 if all(isinstance(node.commit, EmptyCommit)
846 838 for node in (node1, node2)):
847 839 raise HTTPNotFound
848 840
849 841 c.commit_1 = node1.commit
850 842 c.commit_2 = node2.commit
851 843
852 844 if c.action == 'download':
853 845 _diff = diffs.get_gitdiff(node1, node2,
854 846 ignore_whitespace=ignore_whitespace,
855 847 context=line_context)
856 848 diff = diffs.DiffProcessor(_diff, format='gitdiff')
857 849
858 850 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
859 851 response.content_type = 'text/plain'
860 852 response.content_disposition = (
861 853 'attachment; filename=%s' % (diff_name,)
862 854 )
863 855 charset = self._get_default_encoding()
864 856 if charset:
865 857 response.charset = charset
866 858 return diff.as_raw()
867 859
868 860 elif c.action == 'raw':
869 861 _diff = diffs.get_gitdiff(node1, node2,
870 862 ignore_whitespace=ignore_whitespace,
871 863 context=line_context)
872 864 diff = diffs.DiffProcessor(_diff, format='gitdiff')
873 865 response.content_type = 'text/plain'
874 866 charset = self._get_default_encoding()
875 867 if charset:
876 868 response.charset = charset
877 869 return diff.as_raw()
878 870
879 871 else:
880 fid = h.FID(diff2, node2.path)
881 line_context_lcl = get_line_ctx(fid, request.GET)
882 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
883
884 __, commit1, commit2, diff, st, data = diffs.wrapped_diff(
885 filenode_old=node1,
886 filenode_new=node2,
887 diff_limit=self.cut_off_limit_diff,
888 file_limit=self.cut_off_limit_file,
889 show_full_diff=request.GET.get('fulldiff'),
890 ignore_whitespace=ign_whitespace_lcl,
891 line_context=line_context_lcl,)
892
893 c.lines_added = data['stats']['added'] if data else 0
894 c.lines_deleted = data['stats']['deleted'] if data else 0
895 c.files = [data]
896 c.commit_ranges = [c.commit_1, c.commit_2]
897 c.ancestor = None
898 c.statuses = []
899 c.target_repo = c.rhodecode_db_repo
900 c.filename1 = node1.path
901 c.filename = node2.path
902 c.binary_file = node1.is_binary or node2.is_binary
903 operation = data['operation'] if data else ''
904
905 commit_changes = {
906 # TODO: it's passing the old file to the diff to keep the
907 # standard but this is not being used for this template,
908 # but might need both files in the future or a more standard
909 # way to work with that
910 'fid': [commit1, commit2, operation,
911 c.filename, diff, st, data]
912 }
913
914 c.changes = commit_changes
915
916 return render('files/file_diff.html')
872 return redirect(
873 url('compare_url', repo_name=repo_name,
874 source_ref_type='rev',
875 source_ref=diff1,
876 target_repo=c.repo_name,
877 target_ref_type='rev',
878 target_ref=diff2,
879 f_path=f_path))
917 880
918 881 @LoginRequired()
919 882 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
920 883 'repository.admin')
921 884 def diff_2way(self, repo_name, f_path):
885 """
886 Kept only to make OLD links work
887 """
922 888 diff1 = request.GET.get('diff1', '')
923 889 diff2 = request.GET.get('diff2', '')
924 890
925 nodes = []
926 unknown_commits = []
927 for commit in [diff1, diff2]:
928 try:
929 nodes.append(self._get_file_node(commit, f_path))
930 except (RepositoryError, NodeError):
931 log.exception('%(commit)s does not exist' % {'commit': commit})
932 unknown_commits.append(commit)
933 h.flash(h.literal(
934 _('Commit %(commit)s does not exist.') % {'commit': commit}
935 ), category='error')
936
937 if unknown_commits:
938 return redirect(url('files_home', repo_name=c.repo_name,
939 f_path=f_path))
940
941 if all(isinstance(node.commit, EmptyCommit) for node in nodes):
942 raise HTTPNotFound
943
944 node1, node2 = nodes
891 if not any((diff1, diff2)):
892 h.flash(
893 'Need query parameter "diff1" or "diff2" to generate a diff.',
894 category='error')
895 raise HTTPBadRequest()
945 896
946 f_gitdiff = diffs.get_gitdiff(node1, node2, ignore_whitespace=False)
947 diff_processor = diffs.DiffProcessor(f_gitdiff, format='gitdiff')
948 diff_data = diff_processor.prepare()
949
950 if not diff_data or diff_data[0]['raw_diff'] == '':
951 h.flash(h.literal(_('%(file_path)s has not changed '
952 'between %(commit_1)s and %(commit_2)s.') % {
953 'file_path': f_path,
954 'commit_1': node1.commit.id,
955 'commit_2': node2.commit.id
956 }), category='error')
957 return redirect(url('files_home', repo_name=c.repo_name,
958 f_path=f_path))
959
960 c.diff_data = diff_data[0]
961 c.FID = h.FID(diff2, node2.path)
962 # cleanup some unneeded data
963 del c.diff_data['raw_diff']
964 del c.diff_data['chunks']
965
966 c.node1 = node1
967 c.commit_1 = node1.commit
968 c.node2 = node2
969 c.commit_2 = node2.commit
970
971 return render('files/diff_2way.html')
897 return redirect(
898 url('compare_url', repo_name=repo_name,
899 source_ref_type='rev',
900 source_ref=diff1,
901 target_repo=c.repo_name,
902 target_ref_type='rev',
903 target_ref=diff2,
904 f_path=f_path,
905 diffmode='sideside'))
972 906
973 907 def _get_file_node(self, commit_id, f_path):
974 908 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
975 909 commit = c.rhodecode_repo.get_commit(commit_id=commit_id)
976 910 try:
977 911 node = commit.get_node(f_path)
978 912 if node.is_dir():
979 913 raise NodeError('%s path is a %s not a file'
980 914 % (node, type(node)))
981 915 except NodeDoesNotExistError:
982 916 commit = EmptyCommit(
983 917 commit_id=commit_id,
984 918 idx=commit.idx,
985 919 repo=commit.repository,
986 920 alias=commit.repository.alias,
987 921 message=commit.message,
988 922 author=commit.author,
989 923 date=commit.date)
990 924 node = FileNode(f_path, '', commit=commit)
991 925 else:
992 926 commit = EmptyCommit(
993 927 repo=c.rhodecode_repo,
994 928 alias=c.rhodecode_repo.alias)
995 929 node = FileNode(f_path, '', commit=commit)
996 930 return node
997 931
998 932 def _get_node_history(self, commit, f_path, commits=None):
999 933 """
1000 934 get commit history for given node
1001 935
1002 936 :param commit: commit to calculate history
1003 937 :param f_path: path for node to calculate history for
1004 938 :param commits: if passed don't calculate history and take
1005 939 commits defined in this list
1006 940 """
1007 941 # calculate history based on tip
1008 942 tip = c.rhodecode_repo.get_commit()
1009 943 if commits is None:
1010 944 pre_load = ["author", "branch"]
1011 945 try:
1012 946 commits = tip.get_file_history(f_path, pre_load=pre_load)
1013 947 except (NodeDoesNotExistError, CommitError):
1014 948 # this node is not present at tip!
1015 949 commits = commit.get_file_history(f_path, pre_load=pre_load)
1016 950
1017 951 history = []
1018 952 commits_group = ([], _("Changesets"))
1019 953 for commit in commits:
1020 954 branch = ' (%s)' % commit.branch if commit.branch else ''
1021 955 n_desc = 'r%s:%s%s' % (commit.idx, commit.short_id, branch)
1022 956 commits_group[0].append((commit.raw_id, n_desc,))
1023 957 history.append(commits_group)
1024 958
1025 959 symbolic_reference = self._symbolic_reference
1026 960
1027 961 if c.rhodecode_repo.alias == 'svn':
1028 962 adjusted_f_path = self._adjust_file_path_for_svn(
1029 963 f_path, c.rhodecode_repo)
1030 964 if adjusted_f_path != f_path:
1031 965 log.debug(
1032 966 'Recognized svn tag or branch in file "%s", using svn '
1033 967 'specific symbolic references', f_path)
1034 968 f_path = adjusted_f_path
1035 969 symbolic_reference = self._symbolic_reference_svn
1036 970
1037 971 branches = self._create_references(
1038 972 c.rhodecode_repo.branches, symbolic_reference, f_path)
1039 973 branches_group = (branches, _("Branches"))
1040 974
1041 975 tags = self._create_references(
1042 976 c.rhodecode_repo.tags, symbolic_reference, f_path)
1043 977 tags_group = (tags, _("Tags"))
1044 978
1045 979 history.append(branches_group)
1046 980 history.append(tags_group)
1047 981
1048 982 return history, commits
1049 983
1050 984 def _adjust_file_path_for_svn(self, f_path, repo):
1051 985 """
1052 986 Computes the relative path of `f_path`.
1053 987
1054 988 This is mainly based on prefix matching of the recognized tags and
1055 989 branches in the underlying repository.
1056 990 """
1057 991 tags_and_branches = itertools.chain(
1058 992 repo.branches.iterkeys(),
1059 993 repo.tags.iterkeys())
1060 994 tags_and_branches = sorted(tags_and_branches, key=len, reverse=True)
1061 995
1062 996 for name in tags_and_branches:
1063 997 if f_path.startswith(name + '/'):
1064 998 f_path = vcspath.relpath(f_path, name)
1065 999 break
1066 1000 return f_path
1067 1001
1068 1002 def _create_references(
1069 1003 self, branches_or_tags, symbolic_reference, f_path):
1070 1004 items = []
1071 1005 for name, commit_id in branches_or_tags.items():
1072 1006 sym_ref = symbolic_reference(commit_id, name, f_path)
1073 1007 items.append((sym_ref, name))
1074 1008 return items
1075 1009
1076 1010 def _symbolic_reference(self, commit_id, name, f_path):
1077 1011 return commit_id
1078 1012
1079 1013 def _symbolic_reference_svn(self, commit_id, name, f_path):
1080 1014 new_f_path = vcspath.join(name, f_path)
1081 1015 return u'%s@%s' % (new_f_path, commit_id)
1082 1016
1083 1017 @LoginRequired()
1084 1018 @XHRRequired()
1085 1019 @HasRepoPermissionAnyDecorator(
1086 1020 'repository.read', 'repository.write', 'repository.admin')
1087 1021 @jsonify
1088 1022 def nodelist(self, repo_name, revision, f_path):
1089 1023 commit = self.__get_commit_or_redirect(revision, repo_name)
1090 1024
1091 1025 metadata = self._get_nodelist_at_commit(
1092 1026 repo_name, commit.raw_id, f_path)
1093 1027 return {'nodes': metadata}
1094 1028
1095 1029 @LoginRequired()
1096 1030 @XHRRequired()
1097 1031 @HasRepoPermissionAnyDecorator(
1098 1032 'repository.read', 'repository.write', 'repository.admin')
1099 1033 def nodetree_full(self, repo_name, commit_id, f_path):
1100 1034 """
1101 1035 Returns rendered html of file tree that contains commit date,
1102 1036 author, revision for the specified combination of
1103 1037 repo, commit_id and file path
1104 1038
1105 1039 :param repo_name: name of the repository
1106 1040 :param commit_id: commit_id of file tree
1107 1041 :param f_path: file path of the requested directory
1108 1042 """
1109 1043
1110 1044 commit = self.__get_commit_or_redirect(commit_id, repo_name)
1111 1045 try:
1112 1046 dir_node = commit.get_node(f_path)
1113 1047 except RepositoryError as e:
1114 1048 return 'error {}'.format(safe_str(e))
1115 1049
1116 1050 if dir_node.is_file():
1117 1051 return ''
1118 1052
1119 1053 c.file = dir_node
1120 1054 c.commit = commit
1121 1055
1122 1056 # using force=True here, make a little trick. We flush the cache and
1123 1057 # compute it using the same key as without full_load, so the fully
1124 1058 # loaded cached tree is now returned instead of partial
1125 1059 return self._get_tree_at_commit(
1126 1060 repo_name, commit.raw_id, dir_node.path, full_load=True,
1127 1061 force=True)
@@ -1,106 +1,107 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 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 """
22 22 Utilities to be shared by multiple controllers.
23 23
24 24 Should only contain utilities to be shared in the controller layer.
25 25 """
26 26
27 27 from rhodecode.lib import helpers as h
28 28 from rhodecode.lib.vcs.exceptions import RepositoryError
29 29
30
30 31 def parse_path_ref(ref, default_path=None):
31 32 """
32 33 Parse out a path and reference combination and return both parts of it.
33 34
34 35 This is used to allow support of path based comparisons for Subversion
35 36 as an iterim solution in parameter handling.
36 37 """
37 38 if '@' in ref:
38 39 return ref.rsplit('@', 1)
39 40 else:
40 41 return default_path, ref
41 42
42 43
43 44 def get_format_ref_id(repo):
44 45 """Returns a `repo` specific reference formatter function"""
45 46 if h.is_svn(repo):
46 47 return _format_ref_id_svn
47 48 else:
48 49 return _format_ref_id
49 50
50 51
51 52 def _format_ref_id(name, raw_id):
52 53 """Default formatting of a given reference `name`"""
53 54 return name
54 55
55 56
56 57 def _format_ref_id_svn(name, raw_id):
57 58 """Special way of formatting a reference for Subversion including path"""
58 59 return '%s@%s' % (name, raw_id)
59 60
60 61
61 62 def get_commit_from_ref_name(repo, ref_name, ref_type=None):
62 63 """
63 64 Gets the commit for a `ref_name` taking into account `ref_type`.
64 65 Needed in case a bookmark / tag share the same name.
65 66
66 67 :param repo: the repo instance
67 68 :param ref_name: the name of the ref to get
68 69 :param ref_type: optional, used to disambiguate colliding refs
69 70 """
70 71 repo_scm = repo.scm_instance()
71 72 ref_type_mapping = {
72 73 'book': repo_scm.bookmarks,
73 74 'bookmark': repo_scm.bookmarks,
74 75 'tag': repo_scm.tags,
75 76 'branch': repo_scm.branches,
76 77 }
77 78
78 79 commit_id = ref_name
79 80 if repo_scm.alias != 'svn': # pass svn refs straight to backend until
80 81 # the branch issue with svn is fixed
81 82 if ref_type and ref_type in ref_type_mapping:
82 83 try:
83 84 commit_id = ref_type_mapping[ref_type][ref_name]
84 85 except KeyError:
85 86 raise RepositoryError(
86 87 '%s "%s" does not exist' % (ref_type, ref_name))
87 88
88 89 return repo_scm.get_commit(commit_id)
89 90
90 91
91 92 def reviewer_as_json(user, reasons):
92 93 """
93 94 Returns json struct of a reviewer for frontend
94 95
95 96 :param user: the reviewer
96 97 :param reasons: list of strings of why they are reviewers
97 98 """
98 99
99 100 return {
100 101 'user_id': user.user_id,
101 102 'reasons': reasons,
102 103 'username': user.username,
103 104 'firstname': user.firstname,
104 105 'lastname': user.lastname,
105 106 'gravatar_link': h.gravatar_url(user.email, 14),
106 107 }
@@ -1,1576 +1,1587 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2014-2016 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 """
22 22 Base module for all VCS systems
23 23 """
24 24
25 25 import collections
26 26 import datetime
27 27 import itertools
28 28 import logging
29 29 import os
30 30 import time
31 31 import warnings
32 32
33 33 from zope.cachedescriptors.property import Lazy as LazyProperty
34 34
35 35 from rhodecode.lib.utils2 import safe_str, safe_unicode
36 36 from rhodecode.lib.vcs import connection
37 37 from rhodecode.lib.vcs.utils import author_name, author_email
38 38 from rhodecode.lib.vcs.conf import settings
39 39 from rhodecode.lib.vcs.exceptions import (
40 40 CommitError, EmptyRepositoryError, NodeAlreadyAddedError,
41 41 NodeAlreadyChangedError, NodeAlreadyExistsError, NodeAlreadyRemovedError,
42 42 NodeDoesNotExistError, NodeNotChangedError, VCSError,
43 43 ImproperArchiveTypeError, BranchDoesNotExistError, CommitDoesNotExistError,
44 44 RepositoryError)
45 45
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49
50 50 FILEMODE_DEFAULT = 0100644
51 51 FILEMODE_EXECUTABLE = 0100755
52 52
53 53 Reference = collections.namedtuple('Reference', ('type', 'name', 'commit_id'))
54 54 MergeResponse = collections.namedtuple(
55 55 'MergeResponse',
56 56 ('possible', 'executed', 'merge_ref', 'failure_reason'))
57 57
58 58
59 59 class MergeFailureReason(object):
60 60 """
61 61 Enumeration with all the reasons why the server side merge could fail.
62 62
63 63 DO NOT change the number of the reasons, as they may be stored in the
64 64 database.
65 65
66 66 Changing the name of a reason is acceptable and encouraged to deprecate old
67 67 reasons.
68 68 """
69 69
70 70 # Everything went well.
71 71 NONE = 0
72 72
73 73 # An unexpected exception was raised. Check the logs for more details.
74 74 UNKNOWN = 1
75 75
76 76 # The merge was not successful, there are conflicts.
77 77 MERGE_FAILED = 2
78 78
79 79 # The merge succeeded but we could not push it to the target repository.
80 80 PUSH_FAILED = 3
81 81
82 82 # The specified target is not a head in the target repository.
83 83 TARGET_IS_NOT_HEAD = 4
84 84
85 85 # The source repository contains more branches than the target. Pushing
86 86 # the merge will create additional branches in the target.
87 87 HG_SOURCE_HAS_MORE_BRANCHES = 5
88 88
89 89 # The target reference has multiple heads. That does not allow to correctly
90 90 # identify the target location. This could only happen for mercurial
91 91 # branches.
92 92 HG_TARGET_HAS_MULTIPLE_HEADS = 6
93 93
94 94 # The target repository is locked
95 95 TARGET_IS_LOCKED = 7
96 96
97 97 # Deprecated, use MISSING_TARGET_REF or MISSING_SOURCE_REF instead.
98 98 # A involved commit could not be found.
99 99 _DEPRECATED_MISSING_COMMIT = 8
100 100
101 101 # The target repo reference is missing.
102 102 MISSING_TARGET_REF = 9
103 103
104 104 # The source repo reference is missing.
105 105 MISSING_SOURCE_REF = 10
106 106
107 107 # The merge was not successful, there are conflicts related to sub
108 108 # repositories.
109 109 SUBREPO_MERGE_FAILED = 11
110 110
111 111
112 112 class UpdateFailureReason(object):
113 113 """
114 114 Enumeration with all the reasons why the pull request update could fail.
115 115
116 116 DO NOT change the number of the reasons, as they may be stored in the
117 117 database.
118 118
119 119 Changing the name of a reason is acceptable and encouraged to deprecate old
120 120 reasons.
121 121 """
122 122
123 123 # Everything went well.
124 124 NONE = 0
125 125
126 126 # An unexpected exception was raised. Check the logs for more details.
127 127 UNKNOWN = 1
128 128
129 129 # The pull request is up to date.
130 130 NO_CHANGE = 2
131 131
132 132 # The pull request has a reference type that is not supported for update.
133 133 WRONG_REF_TPYE = 3
134 134
135 135 # Update failed because the target reference is missing.
136 136 MISSING_TARGET_REF = 4
137 137
138 138 # Update failed because the source reference is missing.
139 139 MISSING_SOURCE_REF = 5
140 140
141 141
142 142 class BaseRepository(object):
143 143 """
144 144 Base Repository for final backends
145 145
146 146 .. attribute:: DEFAULT_BRANCH_NAME
147 147
148 148 name of default branch (i.e. "trunk" for svn, "master" for git etc.
149 149
150 150 .. attribute:: commit_ids
151 151
152 152 list of all available commit ids, in ascending order
153 153
154 154 .. attribute:: path
155 155
156 156 absolute path to the repository
157 157
158 158 .. attribute:: bookmarks
159 159
160 160 Mapping from name to :term:`Commit ID` of the bookmark. Empty in case
161 161 there are no bookmarks or the backend implementation does not support
162 162 bookmarks.
163 163
164 164 .. attribute:: tags
165 165
166 166 Mapping from name to :term:`Commit ID` of the tag.
167 167
168 168 """
169 169
170 170 DEFAULT_BRANCH_NAME = None
171 171 DEFAULT_CONTACT = u"Unknown"
172 172 DEFAULT_DESCRIPTION = u"unknown"
173 173 EMPTY_COMMIT_ID = '0' * 40
174 174
175 175 path = None
176 176
177 177 def __init__(self, repo_path, config=None, create=False, **kwargs):
178 178 """
179 179 Initializes repository. Raises RepositoryError if repository could
180 180 not be find at the given ``repo_path`` or directory at ``repo_path``
181 181 exists and ``create`` is set to True.
182 182
183 183 :param repo_path: local path of the repository
184 184 :param config: repository configuration
185 185 :param create=False: if set to True, would try to create repository.
186 186 :param src_url=None: if set, should be proper url from which repository
187 187 would be cloned; requires ``create`` parameter to be set to True -
188 188 raises RepositoryError if src_url is set and create evaluates to
189 189 False
190 190 """
191 191 raise NotImplementedError
192 192
193 193 def __repr__(self):
194 194 return '<%s at %s>' % (self.__class__.__name__, self.path)
195 195
196 196 def __len__(self):
197 197 return self.count()
198 198
199 199 def __eq__(self, other):
200 200 same_instance = isinstance(other, self.__class__)
201 201 return same_instance and other.path == self.path
202 202
203 203 def __ne__(self, other):
204 204 return not self.__eq__(other)
205 205
206 206 @LazyProperty
207 207 def EMPTY_COMMIT(self):
208 208 return EmptyCommit(self.EMPTY_COMMIT_ID)
209 209
210 210 @LazyProperty
211 211 def alias(self):
212 212 for k, v in settings.BACKENDS.items():
213 213 if v.split('.')[-1] == str(self.__class__.__name__):
214 214 return k
215 215
216 216 @LazyProperty
217 217 def name(self):
218 218 return safe_unicode(os.path.basename(self.path))
219 219
220 220 @LazyProperty
221 221 def description(self):
222 222 raise NotImplementedError
223 223
224 224 def refs(self):
225 225 """
226 226 returns a `dict` with branches, bookmarks, tags, and closed_branches
227 227 for this repository
228 228 """
229 229 return dict(
230 230 branches=self.branches,
231 231 branches_closed=self.branches_closed,
232 232 tags=self.tags,
233 233 bookmarks=self.bookmarks
234 234 )
235 235
236 236 @LazyProperty
237 237 def branches(self):
238 238 """
239 239 A `dict` which maps branch names to commit ids.
240 240 """
241 241 raise NotImplementedError
242 242
243 243 @LazyProperty
244 244 def tags(self):
245 245 """
246 246 A `dict` which maps tags names to commit ids.
247 247 """
248 248 raise NotImplementedError
249 249
250 250 @LazyProperty
251 251 def size(self):
252 252 """
253 253 Returns combined size in bytes for all repository files
254 254 """
255 255 tip = self.get_commit()
256 256 return tip.size
257 257
258 258 def size_at_commit(self, commit_id):
259 259 commit = self.get_commit(commit_id)
260 260 return commit.size
261 261
262 262 def is_empty(self):
263 263 return not bool(self.commit_ids)
264 264
265 265 @staticmethod
266 266 def check_url(url, config):
267 267 """
268 268 Function will check given url and try to verify if it's a valid
269 269 link.
270 270 """
271 271 raise NotImplementedError
272 272
273 273 @staticmethod
274 274 def is_valid_repository(path):
275 275 """
276 276 Check if given `path` contains a valid repository of this backend
277 277 """
278 278 raise NotImplementedError
279 279
280 280 # ==========================================================================
281 281 # COMMITS
282 282 # ==========================================================================
283 283
284 284 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None):
285 285 """
286 286 Returns instance of `BaseCommit` class. If `commit_id` and `commit_idx`
287 287 are both None, most recent commit is returned.
288 288
289 289 :param pre_load: Optional. List of commit attributes to load.
290 290
291 291 :raises ``EmptyRepositoryError``: if there are no commits
292 292 """
293 293 raise NotImplementedError
294 294
295 295 def __iter__(self):
296 296 for commit_id in self.commit_ids:
297 297 yield self.get_commit(commit_id=commit_id)
298 298
299 299 def get_commits(
300 300 self, start_id=None, end_id=None, start_date=None, end_date=None,
301 301 branch_name=None, pre_load=None):
302 302 """
303 303 Returns iterator of `BaseCommit` objects from start to end
304 304 not inclusive. This should behave just like a list, ie. end is not
305 305 inclusive.
306 306
307 307 :param start_id: None or str, must be a valid commit id
308 308 :param end_id: None or str, must be a valid commit id
309 309 :param start_date:
310 310 :param end_date:
311 311 :param branch_name:
312 312 :param pre_load:
313 313 """
314 314 raise NotImplementedError
315 315
316 316 def __getitem__(self, key):
317 317 """
318 318 Allows index based access to the commit objects of this repository.
319 319 """
320 320 pre_load = ["author", "branch", "date", "message", "parents"]
321 321 if isinstance(key, slice):
322 322 return self._get_range(key, pre_load)
323 323 return self.get_commit(commit_idx=key, pre_load=pre_load)
324 324
325 325 def _get_range(self, slice_obj, pre_load):
326 326 for commit_id in self.commit_ids.__getitem__(slice_obj):
327 327 yield self.get_commit(commit_id=commit_id, pre_load=pre_load)
328 328
329 329 def count(self):
330 330 return len(self.commit_ids)
331 331
332 332 def tag(self, name, user, commit_id=None, message=None, date=None, **opts):
333 333 """
334 334 Creates and returns a tag for the given ``commit_id``.
335 335
336 336 :param name: name for new tag
337 337 :param user: full username, i.e.: "Joe Doe <joe.doe@example.com>"
338 338 :param commit_id: commit id for which new tag would be created
339 339 :param message: message of the tag's commit
340 340 :param date: date of tag's commit
341 341
342 342 :raises TagAlreadyExistError: if tag with same name already exists
343 343 """
344 344 raise NotImplementedError
345 345
346 346 def remove_tag(self, name, user, message=None, date=None):
347 347 """
348 348 Removes tag with the given ``name``.
349 349
350 350 :param name: name of the tag to be removed
351 351 :param user: full username, i.e.: "Joe Doe <joe.doe@example.com>"
352 352 :param message: message of the tag's removal commit
353 353 :param date: date of tag's removal commit
354 354
355 355 :raises TagDoesNotExistError: if tag with given name does not exists
356 356 """
357 357 raise NotImplementedError
358 358
359 359 def get_diff(
360 360 self, commit1, commit2, path=None, ignore_whitespace=False,
361 361 context=3, path1=None):
362 362 """
363 363 Returns (git like) *diff*, as plain text. Shows changes introduced by
364 364 `commit2` since `commit1`.
365 365
366 366 :param commit1: Entry point from which diff is shown. Can be
367 367 ``self.EMPTY_COMMIT`` - in this case, patch showing all
368 368 the changes since empty state of the repository until `commit2`
369 369 :param commit2: Until which commit changes should be shown.
370 370 :param path: Can be set to a path of a file to create a diff of that
371 371 file. If `path1` is also set, this value is only associated to
372 372 `commit2`.
373 373 :param ignore_whitespace: If set to ``True``, would not show whitespace
374 374 changes. Defaults to ``False``.
375 375 :param context: How many lines before/after changed lines should be
376 376 shown. Defaults to ``3``.
377 377 :param path1: Can be set to a path to associate with `commit1`. This
378 378 parameter works only for backends which support diff generation for
379 379 different paths. Other backends will raise a `ValueError` if `path1`
380 380 is set and has a different value than `path`.
381 :param file_path: filter this diff by given path pattern
381 382 """
382 383 raise NotImplementedError
383 384
384 385 def strip(self, commit_id, branch=None):
385 386 """
386 387 Strip given commit_id from the repository
387 388 """
388 389 raise NotImplementedError
389 390
390 391 def get_common_ancestor(self, commit_id1, commit_id2, repo2):
391 392 """
392 393 Return a latest common ancestor commit if one exists for this repo
393 394 `commit_id1` vs `commit_id2` from `repo2`.
394 395
395 396 :param commit_id1: Commit it from this repository to use as a
396 397 target for the comparison.
397 398 :param commit_id2: Source commit id to use for comparison.
398 399 :param repo2: Source repository to use for comparison.
399 400 """
400 401 raise NotImplementedError
401 402
402 403 def compare(self, commit_id1, commit_id2, repo2, merge, pre_load=None):
403 404 """
404 405 Compare this repository's revision `commit_id1` with `commit_id2`.
405 406
406 407 Returns a tuple(commits, ancestor) that would be merged from
407 408 `commit_id2`. Doing a normal compare (``merge=False``), ``None``
408 409 will be returned as ancestor.
409 410
410 411 :param commit_id1: Commit it from this repository to use as a
411 412 target for the comparison.
412 413 :param commit_id2: Source commit id to use for comparison.
413 414 :param repo2: Source repository to use for comparison.
414 415 :param merge: If set to ``True`` will do a merge compare which also
415 416 returns the common ancestor.
416 417 :param pre_load: Optional. List of commit attributes to load.
417 418 """
418 419 raise NotImplementedError
419 420
420 421 def merge(self, target_ref, source_repo, source_ref, workspace_id,
421 422 user_name='', user_email='', message='', dry_run=False,
422 423 use_rebase=False):
423 424 """
424 425 Merge the revisions specified in `source_ref` from `source_repo`
425 426 onto the `target_ref` of this repository.
426 427
427 428 `source_ref` and `target_ref` are named tupls with the following
428 429 fields `type`, `name` and `commit_id`.
429 430
430 431 Returns a MergeResponse named tuple with the following fields
431 432 'possible', 'executed', 'source_commit', 'target_commit',
432 433 'merge_commit'.
433 434
434 435 :param target_ref: `target_ref` points to the commit on top of which
435 436 the `source_ref` should be merged.
436 437 :param source_repo: The repository that contains the commits to be
437 438 merged.
438 439 :param source_ref: `source_ref` points to the topmost commit from
439 440 the `source_repo` which should be merged.
440 441 :param workspace_id: `workspace_id` unique identifier.
441 442 :param user_name: Merge commit `user_name`.
442 443 :param user_email: Merge commit `user_email`.
443 444 :param message: Merge commit `message`.
444 445 :param dry_run: If `True` the merge will not take place.
445 446 :param use_rebase: If `True` commits from the source will be rebased
446 447 on top of the target instead of being merged.
447 448 """
448 449 if dry_run:
449 450 message = message or 'dry_run_merge_message'
450 451 user_email = user_email or 'dry-run-merge@rhodecode.com'
451 452 user_name = user_name or 'Dry-Run User'
452 453 else:
453 454 if not user_name:
454 455 raise ValueError('user_name cannot be empty')
455 456 if not user_email:
456 457 raise ValueError('user_email cannot be empty')
457 458 if not message:
458 459 raise ValueError('message cannot be empty')
459 460
460 461 shadow_repository_path = self._maybe_prepare_merge_workspace(
461 462 workspace_id, target_ref)
462 463
463 464 try:
464 465 return self._merge_repo(
465 466 shadow_repository_path, target_ref, source_repo,
466 467 source_ref, message, user_name, user_email, dry_run=dry_run,
467 468 use_rebase=use_rebase)
468 469 except RepositoryError:
469 470 log.exception(
470 471 'Unexpected failure when running merge, dry-run=%s',
471 472 dry_run)
472 473 return MergeResponse(
473 474 False, False, None, MergeFailureReason.UNKNOWN)
474 475
475 476 def _merge_repo(self, shadow_repository_path, target_ref,
476 477 source_repo, source_ref, merge_message,
477 478 merger_name, merger_email, dry_run=False, use_rebase=False):
478 479 """Internal implementation of merge."""
479 480 raise NotImplementedError
480 481
481 482 def _maybe_prepare_merge_workspace(self, workspace_id, target_ref):
482 483 """
483 484 Create the merge workspace.
484 485
485 486 :param workspace_id: `workspace_id` unique identifier.
486 487 """
487 488 raise NotImplementedError
488 489
489 490 def cleanup_merge_workspace(self, workspace_id):
490 491 """
491 492 Remove merge workspace.
492 493
493 494 This function MUST not fail in case there is no workspace associated to
494 495 the given `workspace_id`.
495 496
496 497 :param workspace_id: `workspace_id` unique identifier.
497 498 """
498 499 raise NotImplementedError
499 500
500 501 # ========== #
501 502 # COMMIT API #
502 503 # ========== #
503 504
504 505 @LazyProperty
505 506 def in_memory_commit(self):
506 507 """
507 508 Returns :class:`InMemoryCommit` object for this repository.
508 509 """
509 510 raise NotImplementedError
510 511
511 512 # ======================== #
512 513 # UTILITIES FOR SUBCLASSES #
513 514 # ======================== #
514 515
515 516 def _validate_diff_commits(self, commit1, commit2):
516 517 """
517 518 Validates that the given commits are related to this repository.
518 519
519 520 Intended as a utility for sub classes to have a consistent validation
520 521 of input parameters in methods like :meth:`get_diff`.
521 522 """
522 523 self._validate_commit(commit1)
523 524 self._validate_commit(commit2)
524 525 if (isinstance(commit1, EmptyCommit) and
525 526 isinstance(commit2, EmptyCommit)):
526 527 raise ValueError("Cannot compare two empty commits")
527 528
528 529 def _validate_commit(self, commit):
529 530 if not isinstance(commit, BaseCommit):
530 531 raise TypeError(
531 532 "%s is not of type BaseCommit" % repr(commit))
532 533 if commit.repository != self and not isinstance(commit, EmptyCommit):
533 534 raise ValueError(
534 535 "Commit %s must be a valid commit from this repository %s, "
535 536 "related to this repository instead %s." %
536 537 (commit, self, commit.repository))
537 538
538 539 def _validate_commit_id(self, commit_id):
539 540 if not isinstance(commit_id, basestring):
540 541 raise TypeError("commit_id must be a string value")
541 542
542 543 def _validate_commit_idx(self, commit_idx):
543 544 if not isinstance(commit_idx, (int, long)):
544 545 raise TypeError("commit_idx must be a numeric value")
545 546
546 547 def _validate_branch_name(self, branch_name):
547 548 if branch_name and branch_name not in self.branches_all:
548 549 msg = ("Branch %s not found in %s" % (branch_name, self))
549 550 raise BranchDoesNotExistError(msg)
550 551
551 552 #
552 553 # Supporting deprecated API parts
553 554 # TODO: johbo: consider to move this into a mixin
554 555 #
555 556
556 557 @property
557 558 def EMPTY_CHANGESET(self):
558 559 warnings.warn(
559 560 "Use EMPTY_COMMIT or EMPTY_COMMIT_ID instead", DeprecationWarning)
560 561 return self.EMPTY_COMMIT_ID
561 562
562 563 @property
563 564 def revisions(self):
564 565 warnings.warn("Use commits attribute instead", DeprecationWarning)
565 566 return self.commit_ids
566 567
567 568 @revisions.setter
568 569 def revisions(self, value):
569 570 warnings.warn("Use commits attribute instead", DeprecationWarning)
570 571 self.commit_ids = value
571 572
572 573 def get_changeset(self, revision=None, pre_load=None):
573 574 warnings.warn("Use get_commit instead", DeprecationWarning)
574 575 commit_id = None
575 576 commit_idx = None
576 577 if isinstance(revision, basestring):
577 578 commit_id = revision
578 579 else:
579 580 commit_idx = revision
580 581 return self.get_commit(
581 582 commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load)
582 583
583 584 def get_changesets(
584 585 self, start=None, end=None, start_date=None, end_date=None,
585 586 branch_name=None, pre_load=None):
586 587 warnings.warn("Use get_commits instead", DeprecationWarning)
587 588 start_id = self._revision_to_commit(start)
588 589 end_id = self._revision_to_commit(end)
589 590 return self.get_commits(
590 591 start_id=start_id, end_id=end_id, start_date=start_date,
591 592 end_date=end_date, branch_name=branch_name, pre_load=pre_load)
592 593
593 594 def _revision_to_commit(self, revision):
594 595 """
595 596 Translates a revision to a commit_id
596 597
597 598 Helps to support the old changeset based API which allows to use
598 599 commit ids and commit indices interchangeable.
599 600 """
600 601 if revision is None:
601 602 return revision
602 603
603 604 if isinstance(revision, basestring):
604 605 commit_id = revision
605 606 else:
606 607 commit_id = self.commit_ids[revision]
607 608 return commit_id
608 609
609 610 @property
610 611 def in_memory_changeset(self):
611 612 warnings.warn("Use in_memory_commit instead", DeprecationWarning)
612 613 return self.in_memory_commit
613 614
614 615
615 616 class BaseCommit(object):
616 617 """
617 618 Each backend should implement it's commit representation.
618 619
619 620 **Attributes**
620 621
621 622 ``repository``
622 623 repository object within which commit exists
623 624
624 625 ``id``
625 626 The commit id, may be ``raw_id`` or i.e. for mercurial's tip
626 627 just ``tip``.
627 628
628 629 ``raw_id``
629 630 raw commit representation (i.e. full 40 length sha for git
630 631 backend)
631 632
632 633 ``short_id``
633 634 shortened (if apply) version of ``raw_id``; it would be simple
634 635 shortcut for ``raw_id[:12]`` for git/mercurial backends or same
635 636 as ``raw_id`` for subversion
636 637
637 638 ``idx``
638 639 commit index
639 640
640 641 ``files``
641 642 list of ``FileNode`` (``Node`` with NodeKind.FILE) objects
642 643
643 644 ``dirs``
644 645 list of ``DirNode`` (``Node`` with NodeKind.DIR) objects
645 646
646 647 ``nodes``
647 648 combined list of ``Node`` objects
648 649
649 650 ``author``
650 651 author of the commit, as unicode
651 652
652 653 ``message``
653 654 message of the commit, as unicode
654 655
655 656 ``parents``
656 657 list of parent commits
657 658
658 659 """
659 660
660 661 branch = None
661 662 """
662 663 Depending on the backend this should be set to the branch name of the
663 664 commit. Backends not supporting branches on commits should leave this
664 665 value as ``None``.
665 666 """
666 667
667 668 _ARCHIVE_PREFIX_TEMPLATE = b'{repo_name}-{short_id}'
668 669 """
669 670 This template is used to generate a default prefix for repository archives
670 671 if no prefix has been specified.
671 672 """
672 673
673 674 def __str__(self):
674 675 return '<%s at %s:%s>' % (
675 676 self.__class__.__name__, self.idx, self.short_id)
676 677
677 678 def __repr__(self):
678 679 return self.__str__()
679 680
680 681 def __unicode__(self):
681 682 return u'%s:%s' % (self.idx, self.short_id)
682 683
683 684 def __eq__(self, other):
684 685 same_instance = isinstance(other, self.__class__)
685 686 return same_instance and self.raw_id == other.raw_id
686 687
687 688 def __json__(self):
688 689 parents = []
689 690 try:
690 691 for parent in self.parents:
691 692 parents.append({'raw_id': parent.raw_id})
692 693 except NotImplementedError:
693 694 # empty commit doesn't have parents implemented
694 695 pass
695 696
696 697 return {
697 698 'short_id': self.short_id,
698 699 'raw_id': self.raw_id,
699 700 'revision': self.idx,
700 701 'message': self.message,
701 702 'date': self.date,
702 703 'author': self.author,
703 704 'parents': parents,
704 705 'branch': self.branch
705 706 }
706 707
707 708 @LazyProperty
708 709 def last(self):
709 710 """
710 711 ``True`` if this is last commit in repository, ``False``
711 712 otherwise; trying to access this attribute while there is no
712 713 commits would raise `EmptyRepositoryError`
713 714 """
714 715 if self.repository is None:
715 716 raise CommitError("Cannot check if it's most recent commit")
716 717 return self.raw_id == self.repository.commit_ids[-1]
717 718
718 719 @LazyProperty
719 720 def parents(self):
720 721 """
721 722 Returns list of parent commits.
722 723 """
723 724 raise NotImplementedError
724 725
725 726 @property
726 727 def merge(self):
727 728 """
728 729 Returns boolean if commit is a merge.
729 730 """
730 731 return len(self.parents) > 1
731 732
732 733 @LazyProperty
733 734 def children(self):
734 735 """
735 736 Returns list of child commits.
736 737 """
737 738 raise NotImplementedError
738 739
739 740 @LazyProperty
740 741 def id(self):
741 742 """
742 743 Returns string identifying this commit.
743 744 """
744 745 raise NotImplementedError
745 746
746 747 @LazyProperty
747 748 def raw_id(self):
748 749 """
749 750 Returns raw string identifying this commit.
750 751 """
751 752 raise NotImplementedError
752 753
753 754 @LazyProperty
754 755 def short_id(self):
755 756 """
756 757 Returns shortened version of ``raw_id`` attribute, as string,
757 758 identifying this commit, useful for presentation to users.
758 759 """
759 760 raise NotImplementedError
760 761
761 762 @LazyProperty
762 763 def idx(self):
763 764 """
764 765 Returns integer identifying this commit.
765 766 """
766 767 raise NotImplementedError
767 768
768 769 @LazyProperty
769 770 def committer(self):
770 771 """
771 772 Returns committer for this commit
772 773 """
773 774 raise NotImplementedError
774 775
775 776 @LazyProperty
776 777 def committer_name(self):
777 778 """
778 779 Returns committer name for this commit
779 780 """
780 781
781 782 return author_name(self.committer)
782 783
783 784 @LazyProperty
784 785 def committer_email(self):
785 786 """
786 787 Returns committer email address for this commit
787 788 """
788 789
789 790 return author_email(self.committer)
790 791
791 792 @LazyProperty
792 793 def author(self):
793 794 """
794 795 Returns author for this commit
795 796 """
796 797
797 798 raise NotImplementedError
798 799
799 800 @LazyProperty
800 801 def author_name(self):
801 802 """
802 803 Returns author name for this commit
803 804 """
804 805
805 806 return author_name(self.author)
806 807
807 808 @LazyProperty
808 809 def author_email(self):
809 810 """
810 811 Returns author email address for this commit
811 812 """
812 813
813 814 return author_email(self.author)
814 815
815 816 def get_file_mode(self, path):
816 817 """
817 818 Returns stat mode of the file at `path`.
818 819 """
819 820 raise NotImplementedError
820 821
821 822 def is_link(self, path):
822 823 """
823 824 Returns ``True`` if given `path` is a symlink
824 825 """
825 826 raise NotImplementedError
826 827
827 828 def get_file_content(self, path):
828 829 """
829 830 Returns content of the file at the given `path`.
830 831 """
831 832 raise NotImplementedError
832 833
833 834 def get_file_size(self, path):
834 835 """
835 836 Returns size of the file at the given `path`.
836 837 """
837 838 raise NotImplementedError
838 839
839 840 def get_file_commit(self, path, pre_load=None):
840 841 """
841 842 Returns last commit of the file at the given `path`.
842 843
843 844 :param pre_load: Optional. List of commit attributes to load.
844 845 """
845 846 commits = self.get_file_history(path, limit=1, pre_load=pre_load)
846 847 if not commits:
847 848 raise RepositoryError(
848 849 'Failed to fetch history for path {}. '
849 850 'Please check if such path exists in your repository'.format(
850 851 path))
851 852 return commits[0]
852 853
853 854 def get_file_history(self, path, limit=None, pre_load=None):
854 855 """
855 856 Returns history of file as reversed list of :class:`BaseCommit`
856 857 objects for which file at given `path` has been modified.
857 858
858 859 :param limit: Optional. Allows to limit the size of the returned
859 860 history. This is intended as a hint to the underlying backend, so
860 861 that it can apply optimizations depending on the limit.
861 862 :param pre_load: Optional. List of commit attributes to load.
862 863 """
863 864 raise NotImplementedError
864 865
865 866 def get_file_annotate(self, path, pre_load=None):
866 867 """
867 868 Returns a generator of four element tuples with
868 869 lineno, sha, commit lazy loader and line
869 870
870 871 :param pre_load: Optional. List of commit attributes to load.
871 872 """
872 873 raise NotImplementedError
873 874
874 875 def get_nodes(self, path):
875 876 """
876 877 Returns combined ``DirNode`` and ``FileNode`` objects list representing
877 878 state of commit at the given ``path``.
878 879
879 880 :raises ``CommitError``: if node at the given ``path`` is not
880 881 instance of ``DirNode``
881 882 """
882 883 raise NotImplementedError
883 884
884 885 def get_node(self, path):
885 886 """
886 887 Returns ``Node`` object from the given ``path``.
887 888
888 889 :raises ``NodeDoesNotExistError``: if there is no node at the given
889 890 ``path``
890 891 """
891 892 raise NotImplementedError
892 893
893 894 def get_largefile_node(self, path):
894 895 """
895 896 Returns the path to largefile from Mercurial storage.
896 897 """
897 898 raise NotImplementedError
898 899
899 900 def archive_repo(self, file_path, kind='tgz', subrepos=None,
900 901 prefix=None, write_metadata=False, mtime=None):
901 902 """
902 903 Creates an archive containing the contents of the repository.
903 904
904 905 :param file_path: path to the file which to create the archive.
905 906 :param kind: one of following: ``"tbz2"``, ``"tgz"``, ``"zip"``.
906 907 :param prefix: name of root directory in archive.
907 908 Default is repository name and commit's short_id joined with dash:
908 909 ``"{repo_name}-{short_id}"``.
909 910 :param write_metadata: write a metadata file into archive.
910 911 :param mtime: custom modification time for archive creation, defaults
911 912 to time.time() if not given.
912 913
913 914 :raise VCSError: If prefix has a problem.
914 915 """
915 916 allowed_kinds = settings.ARCHIVE_SPECS.keys()
916 917 if kind not in allowed_kinds:
917 918 raise ImproperArchiveTypeError(
918 919 'Archive kind (%s) not supported use one of %s' %
919 920 (kind, allowed_kinds))
920 921
921 922 prefix = self._validate_archive_prefix(prefix)
922 923
923 924 mtime = mtime or time.mktime(self.date.timetuple())
924 925
925 926 file_info = []
926 927 cur_rev = self.repository.get_commit(commit_id=self.raw_id)
927 928 for _r, _d, files in cur_rev.walk('/'):
928 929 for f in files:
929 930 f_path = os.path.join(prefix, f.path)
930 931 file_info.append(
931 932 (f_path, f.mode, f.is_link(), f.raw_bytes))
932 933
933 934 if write_metadata:
934 935 metadata = [
935 936 ('repo_name', self.repository.name),
936 937 ('rev', self.raw_id),
937 938 ('create_time', mtime),
938 939 ('branch', self.branch),
939 940 ('tags', ','.join(self.tags)),
940 941 ]
941 942 meta = ["%s:%s" % (f_name, value) for f_name, value in metadata]
942 943 file_info.append(('.archival.txt', 0644, False, '\n'.join(meta)))
943 944
944 945 connection.Hg.archive_repo(file_path, mtime, file_info, kind)
945 946
946 947 def _validate_archive_prefix(self, prefix):
947 948 if prefix is None:
948 949 prefix = self._ARCHIVE_PREFIX_TEMPLATE.format(
949 950 repo_name=safe_str(self.repository.name),
950 951 short_id=self.short_id)
951 952 elif not isinstance(prefix, str):
952 953 raise ValueError("prefix not a bytes object: %s" % repr(prefix))
953 954 elif prefix.startswith('/'):
954 955 raise VCSError("Prefix cannot start with leading slash")
955 956 elif prefix.strip() == '':
956 957 raise VCSError("Prefix cannot be empty")
957 958 return prefix
958 959
959 960 @LazyProperty
960 961 def root(self):
961 962 """
962 963 Returns ``RootNode`` object for this commit.
963 964 """
964 965 return self.get_node('')
965 966
966 967 def next(self, branch=None):
967 968 """
968 969 Returns next commit from current, if branch is gives it will return
969 970 next commit belonging to this branch
970 971
971 972 :param branch: show commits within the given named branch
972 973 """
973 974 indexes = xrange(self.idx + 1, self.repository.count())
974 975 return self._find_next(indexes, branch)
975 976
976 977 def prev(self, branch=None):
977 978 """
978 979 Returns previous commit from current, if branch is gives it will
979 980 return previous commit belonging to this branch
980 981
981 982 :param branch: show commit within the given named branch
982 983 """
983 984 indexes = xrange(self.idx - 1, -1, -1)
984 985 return self._find_next(indexes, branch)
985 986
986 987 def _find_next(self, indexes, branch=None):
987 988 if branch and self.branch != branch:
988 989 raise VCSError('Branch option used on commit not belonging '
989 990 'to that branch')
990 991
991 992 for next_idx in indexes:
992 993 commit = self.repository.get_commit(commit_idx=next_idx)
993 994 if branch and branch != commit.branch:
994 995 continue
995 996 return commit
996 997 raise CommitDoesNotExistError
997 998
998 999 def diff(self, ignore_whitespace=True, context=3):
999 1000 """
1000 1001 Returns a `Diff` object representing the change made by this commit.
1001 1002 """
1002 1003 parent = (
1003 1004 self.parents[0] if self.parents else self.repository.EMPTY_COMMIT)
1004 1005 diff = self.repository.get_diff(
1005 1006 parent, self,
1006 1007 ignore_whitespace=ignore_whitespace,
1007 1008 context=context)
1008 1009 return diff
1009 1010
1010 1011 @LazyProperty
1011 1012 def added(self):
1012 1013 """
1013 1014 Returns list of added ``FileNode`` objects.
1014 1015 """
1015 1016 raise NotImplementedError
1016 1017
1017 1018 @LazyProperty
1018 1019 def changed(self):
1019 1020 """
1020 1021 Returns list of modified ``FileNode`` objects.
1021 1022 """
1022 1023 raise NotImplementedError
1023 1024
1024 1025 @LazyProperty
1025 1026 def removed(self):
1026 1027 """
1027 1028 Returns list of removed ``FileNode`` objects.
1028 1029 """
1029 1030 raise NotImplementedError
1030 1031
1031 1032 @LazyProperty
1032 1033 def size(self):
1033 1034 """
1034 1035 Returns total number of bytes from contents of all filenodes.
1035 1036 """
1036 1037 return sum((node.size for node in self.get_filenodes_generator()))
1037 1038
1038 1039 def walk(self, topurl=''):
1039 1040 """
1040 1041 Similar to os.walk method. Insted of filesystem it walks through
1041 1042 commit starting at given ``topurl``. Returns generator of tuples
1042 1043 (topnode, dirnodes, filenodes).
1043 1044 """
1044 1045 topnode = self.get_node(topurl)
1045 1046 if not topnode.is_dir():
1046 1047 return
1047 1048 yield (topnode, topnode.dirs, topnode.files)
1048 1049 for dirnode in topnode.dirs:
1049 1050 for tup in self.walk(dirnode.path):
1050 1051 yield tup
1051 1052
1052 1053 def get_filenodes_generator(self):
1053 1054 """
1054 1055 Returns generator that yields *all* file nodes.
1055 1056 """
1056 1057 for topnode, dirs, files in self.walk():
1057 1058 for node in files:
1058 1059 yield node
1059 1060
1060 1061 #
1061 1062 # Utilities for sub classes to support consistent behavior
1062 1063 #
1063 1064
1064 1065 def no_node_at_path(self, path):
1065 1066 return NodeDoesNotExistError(
1066 1067 "There is no file nor directory at the given path: "
1067 1068 "'%s' at commit %s" % (path, self.short_id))
1068 1069
1069 1070 def _fix_path(self, path):
1070 1071 """
1071 1072 Paths are stored without trailing slash so we need to get rid off it if
1072 1073 needed.
1073 1074 """
1074 1075 return path.rstrip('/')
1075 1076
1076 1077 #
1077 1078 # Deprecated API based on changesets
1078 1079 #
1079 1080
1080 1081 @property
1081 1082 def revision(self):
1082 1083 warnings.warn("Use idx instead", DeprecationWarning)
1083 1084 return self.idx
1084 1085
1085 1086 @revision.setter
1086 1087 def revision(self, value):
1087 1088 warnings.warn("Use idx instead", DeprecationWarning)
1088 1089 self.idx = value
1089 1090
1090 1091 def get_file_changeset(self, path):
1091 1092 warnings.warn("Use get_file_commit instead", DeprecationWarning)
1092 1093 return self.get_file_commit(path)
1093 1094
1094 1095
1095 1096 class BaseChangesetClass(type):
1096 1097
1097 1098 def __instancecheck__(self, instance):
1098 1099 return isinstance(instance, BaseCommit)
1099 1100
1100 1101
1101 1102 class BaseChangeset(BaseCommit):
1102 1103
1103 1104 __metaclass__ = BaseChangesetClass
1104 1105
1105 1106 def __new__(cls, *args, **kwargs):
1106 1107 warnings.warn(
1107 1108 "Use BaseCommit instead of BaseChangeset", DeprecationWarning)
1108 1109 return super(BaseChangeset, cls).__new__(cls, *args, **kwargs)
1109 1110
1110 1111
1111 1112 class BaseInMemoryCommit(object):
1112 1113 """
1113 1114 Represents differences between repository's state (most recent head) and
1114 1115 changes made *in place*.
1115 1116
1116 1117 **Attributes**
1117 1118
1118 1119 ``repository``
1119 1120 repository object for this in-memory-commit
1120 1121
1121 1122 ``added``
1122 1123 list of ``FileNode`` objects marked as *added*
1123 1124
1124 1125 ``changed``
1125 1126 list of ``FileNode`` objects marked as *changed*
1126 1127
1127 1128 ``removed``
1128 1129 list of ``FileNode`` or ``RemovedFileNode`` objects marked to be
1129 1130 *removed*
1130 1131
1131 1132 ``parents``
1132 1133 list of :class:`BaseCommit` instances representing parents of
1133 1134 in-memory commit. Should always be 2-element sequence.
1134 1135
1135 1136 """
1136 1137
1137 1138 def __init__(self, repository):
1138 1139 self.repository = repository
1139 1140 self.added = []
1140 1141 self.changed = []
1141 1142 self.removed = []
1142 1143 self.parents = []
1143 1144
1144 1145 def add(self, *filenodes):
1145 1146 """
1146 1147 Marks given ``FileNode`` objects as *to be committed*.
1147 1148
1148 1149 :raises ``NodeAlreadyExistsError``: if node with same path exists at
1149 1150 latest commit
1150 1151 :raises ``NodeAlreadyAddedError``: if node with same path is already
1151 1152 marked as *added*
1152 1153 """
1153 1154 # Check if not already marked as *added* first
1154 1155 for node in filenodes:
1155 1156 if node.path in (n.path for n in self.added):
1156 1157 raise NodeAlreadyAddedError(
1157 1158 "Such FileNode %s is already marked for addition"
1158 1159 % node.path)
1159 1160 for node in filenodes:
1160 1161 self.added.append(node)
1161 1162
1162 1163 def change(self, *filenodes):
1163 1164 """
1164 1165 Marks given ``FileNode`` objects to be *changed* in next commit.
1165 1166
1166 1167 :raises ``EmptyRepositoryError``: if there are no commits yet
1167 1168 :raises ``NodeAlreadyExistsError``: if node with same path is already
1168 1169 marked to be *changed*
1169 1170 :raises ``NodeAlreadyRemovedError``: if node with same path is already
1170 1171 marked to be *removed*
1171 1172 :raises ``NodeDoesNotExistError``: if node doesn't exist in latest
1172 1173 commit
1173 1174 :raises ``NodeNotChangedError``: if node hasn't really be changed
1174 1175 """
1175 1176 for node in filenodes:
1176 1177 if node.path in (n.path for n in self.removed):
1177 1178 raise NodeAlreadyRemovedError(
1178 1179 "Node at %s is already marked as removed" % node.path)
1179 1180 try:
1180 1181 self.repository.get_commit()
1181 1182 except EmptyRepositoryError:
1182 1183 raise EmptyRepositoryError(
1183 1184 "Nothing to change - try to *add* new nodes rather than "
1184 1185 "changing them")
1185 1186 for node in filenodes:
1186 1187 if node.path in (n.path for n in self.changed):
1187 1188 raise NodeAlreadyChangedError(
1188 1189 "Node at '%s' is already marked as changed" % node.path)
1189 1190 self.changed.append(node)
1190 1191
1191 1192 def remove(self, *filenodes):
1192 1193 """
1193 1194 Marks given ``FileNode`` (or ``RemovedFileNode``) objects to be
1194 1195 *removed* in next commit.
1195 1196
1196 1197 :raises ``NodeAlreadyRemovedError``: if node has been already marked to
1197 1198 be *removed*
1198 1199 :raises ``NodeAlreadyChangedError``: if node has been already marked to
1199 1200 be *changed*
1200 1201 """
1201 1202 for node in filenodes:
1202 1203 if node.path in (n.path for n in self.removed):
1203 1204 raise NodeAlreadyRemovedError(
1204 1205 "Node is already marked to for removal at %s" % node.path)
1205 1206 if node.path in (n.path for n in self.changed):
1206 1207 raise NodeAlreadyChangedError(
1207 1208 "Node is already marked to be changed at %s" % node.path)
1208 1209 # We only mark node as *removed* - real removal is done by
1209 1210 # commit method
1210 1211 self.removed.append(node)
1211 1212
1212 1213 def reset(self):
1213 1214 """
1214 1215 Resets this instance to initial state (cleans ``added``, ``changed``
1215 1216 and ``removed`` lists).
1216 1217 """
1217 1218 self.added = []
1218 1219 self.changed = []
1219 1220 self.removed = []
1220 1221 self.parents = []
1221 1222
1222 1223 def get_ipaths(self):
1223 1224 """
1224 1225 Returns generator of paths from nodes marked as added, changed or
1225 1226 removed.
1226 1227 """
1227 1228 for node in itertools.chain(self.added, self.changed, self.removed):
1228 1229 yield node.path
1229 1230
1230 1231 def get_paths(self):
1231 1232 """
1232 1233 Returns list of paths from nodes marked as added, changed or removed.
1233 1234 """
1234 1235 return list(self.get_ipaths())
1235 1236
1236 1237 def check_integrity(self, parents=None):
1237 1238 """
1238 1239 Checks in-memory commit's integrity. Also, sets parents if not
1239 1240 already set.
1240 1241
1241 1242 :raises CommitError: if any error occurs (i.e.
1242 1243 ``NodeDoesNotExistError``).
1243 1244 """
1244 1245 if not self.parents:
1245 1246 parents = parents or []
1246 1247 if len(parents) == 0:
1247 1248 try:
1248 1249 parents = [self.repository.get_commit(), None]
1249 1250 except EmptyRepositoryError:
1250 1251 parents = [None, None]
1251 1252 elif len(parents) == 1:
1252 1253 parents += [None]
1253 1254 self.parents = parents
1254 1255
1255 1256 # Local parents, only if not None
1256 1257 parents = [p for p in self.parents if p]
1257 1258
1258 1259 # Check nodes marked as added
1259 1260 for p in parents:
1260 1261 for node in self.added:
1261 1262 try:
1262 1263 p.get_node(node.path)
1263 1264 except NodeDoesNotExistError:
1264 1265 pass
1265 1266 else:
1266 1267 raise NodeAlreadyExistsError(
1267 1268 "Node `%s` already exists at %s" % (node.path, p))
1268 1269
1269 1270 # Check nodes marked as changed
1270 1271 missing = set(self.changed)
1271 1272 not_changed = set(self.changed)
1272 1273 if self.changed and not parents:
1273 1274 raise NodeDoesNotExistError(str(self.changed[0].path))
1274 1275 for p in parents:
1275 1276 for node in self.changed:
1276 1277 try:
1277 1278 old = p.get_node(node.path)
1278 1279 missing.remove(node)
1279 1280 # if content actually changed, remove node from not_changed
1280 1281 if old.content != node.content:
1281 1282 not_changed.remove(node)
1282 1283 except NodeDoesNotExistError:
1283 1284 pass
1284 1285 if self.changed and missing:
1285 1286 raise NodeDoesNotExistError(
1286 1287 "Node `%s` marked as modified but missing in parents: %s"
1287 1288 % (node.path, parents))
1288 1289
1289 1290 if self.changed and not_changed:
1290 1291 raise NodeNotChangedError(
1291 1292 "Node `%s` wasn't actually changed (parents: %s)"
1292 1293 % (not_changed.pop().path, parents))
1293 1294
1294 1295 # Check nodes marked as removed
1295 1296 if self.removed and not parents:
1296 1297 raise NodeDoesNotExistError(
1297 1298 "Cannot remove node at %s as there "
1298 1299 "were no parents specified" % self.removed[0].path)
1299 1300 really_removed = set()
1300 1301 for p in parents:
1301 1302 for node in self.removed:
1302 1303 try:
1303 1304 p.get_node(node.path)
1304 1305 really_removed.add(node)
1305 1306 except CommitError:
1306 1307 pass
1307 1308 not_removed = set(self.removed) - really_removed
1308 1309 if not_removed:
1309 1310 # TODO: johbo: This code branch does not seem to be covered
1310 1311 raise NodeDoesNotExistError(
1311 1312 "Cannot remove node at %s from "
1312 1313 "following parents: %s" % (not_removed, parents))
1313 1314
1314 1315 def commit(
1315 1316 self, message, author, parents=None, branch=None, date=None,
1316 1317 **kwargs):
1317 1318 """
1318 1319 Performs in-memory commit (doesn't check workdir in any way) and
1319 1320 returns newly created :class:`BaseCommit`. Updates repository's
1320 1321 attribute `commits`.
1321 1322
1322 1323 .. note::
1323 1324
1324 1325 While overriding this method each backend's should call
1325 1326 ``self.check_integrity(parents)`` in the first place.
1326 1327
1327 1328 :param message: message of the commit
1328 1329 :param author: full username, i.e. "Joe Doe <joe.doe@example.com>"
1329 1330 :param parents: single parent or sequence of parents from which commit
1330 1331 would be derived
1331 1332 :param date: ``datetime.datetime`` instance. Defaults to
1332 1333 ``datetime.datetime.now()``.
1333 1334 :param branch: branch name, as string. If none given, default backend's
1334 1335 branch would be used.
1335 1336
1336 1337 :raises ``CommitError``: if any error occurs while committing
1337 1338 """
1338 1339 raise NotImplementedError
1339 1340
1340 1341
1341 1342 class BaseInMemoryChangesetClass(type):
1342 1343
1343 1344 def __instancecheck__(self, instance):
1344 1345 return isinstance(instance, BaseInMemoryCommit)
1345 1346
1346 1347
1347 1348 class BaseInMemoryChangeset(BaseInMemoryCommit):
1348 1349
1349 1350 __metaclass__ = BaseInMemoryChangesetClass
1350 1351
1351 1352 def __new__(cls, *args, **kwargs):
1352 1353 warnings.warn(
1353 1354 "Use BaseCommit instead of BaseInMemoryCommit", DeprecationWarning)
1354 1355 return super(BaseInMemoryChangeset, cls).__new__(cls, *args, **kwargs)
1355 1356
1356 1357
1357 1358 class EmptyCommit(BaseCommit):
1358 1359 """
1359 1360 An dummy empty commit. It's possible to pass hash when creating
1360 1361 an EmptyCommit
1361 1362 """
1362 1363
1363 1364 def __init__(
1364 1365 self, commit_id='0' * 40, repo=None, alias=None, idx=-1,
1365 1366 message='', author='', date=None):
1366 1367 self._empty_commit_id = commit_id
1367 1368 # TODO: johbo: Solve idx parameter, default value does not make
1368 1369 # too much sense
1369 1370 self.idx = idx
1370 1371 self.message = message
1371 1372 self.author = author
1372 1373 self.date = date or datetime.datetime.fromtimestamp(0)
1373 1374 self.repository = repo
1374 1375 self.alias = alias
1375 1376
1376 1377 @LazyProperty
1377 1378 def raw_id(self):
1378 1379 """
1379 1380 Returns raw string identifying this commit, useful for web
1380 1381 representation.
1381 1382 """
1382 1383
1383 1384 return self._empty_commit_id
1384 1385
1385 1386 @LazyProperty
1386 1387 def branch(self):
1387 1388 if self.alias:
1388 1389 from rhodecode.lib.vcs.backends import get_backend
1389 1390 return get_backend(self.alias).DEFAULT_BRANCH_NAME
1390 1391
1391 1392 @LazyProperty
1392 1393 def short_id(self):
1393 1394 return self.raw_id[:12]
1394 1395
1395 1396 @LazyProperty
1396 1397 def id(self):
1397 1398 return self.raw_id
1398 1399
1399 1400 def get_file_commit(self, path):
1400 1401 return self
1401 1402
1402 1403 def get_file_content(self, path):
1403 1404 return u''
1404 1405
1405 1406 def get_file_size(self, path):
1406 1407 return 0
1407 1408
1408 1409
1409 1410 class EmptyChangesetClass(type):
1410 1411
1411 1412 def __instancecheck__(self, instance):
1412 1413 return isinstance(instance, EmptyCommit)
1413 1414
1414 1415
1415 1416 class EmptyChangeset(EmptyCommit):
1416 1417
1417 1418 __metaclass__ = EmptyChangesetClass
1418 1419
1419 1420 def __new__(cls, *args, **kwargs):
1420 1421 warnings.warn(
1421 1422 "Use EmptyCommit instead of EmptyChangeset", DeprecationWarning)
1422 1423 return super(EmptyCommit, cls).__new__(cls, *args, **kwargs)
1423 1424
1424 1425 def __init__(self, cs='0' * 40, repo=None, requested_revision=None,
1425 1426 alias=None, revision=-1, message='', author='', date=None):
1426 1427 if requested_revision is not None:
1427 1428 warnings.warn(
1428 1429 "Parameter requested_revision not supported anymore",
1429 1430 DeprecationWarning)
1430 1431 super(EmptyChangeset, self).__init__(
1431 1432 commit_id=cs, repo=repo, alias=alias, idx=revision,
1432 1433 message=message, author=author, date=date)
1433 1434
1434 1435 @property
1435 1436 def revision(self):
1436 1437 warnings.warn("Use idx instead", DeprecationWarning)
1437 1438 return self.idx
1438 1439
1439 1440 @revision.setter
1440 1441 def revision(self, value):
1441 1442 warnings.warn("Use idx instead", DeprecationWarning)
1442 1443 self.idx = value
1443 1444
1444 1445
1445 1446 class EmptyRepository(BaseRepository):
1446 1447 def __init__(self, repo_path=None, config=None, create=False, **kwargs):
1447 1448 pass
1448 1449
1449 1450 def get_diff(self, *args, **kwargs):
1450 1451 from rhodecode.lib.vcs.backends.git.diff import GitDiff
1451 1452 return GitDiff('')
1452 1453
1453 1454
1454 1455 class CollectionGenerator(object):
1455 1456
1456 1457 def __init__(self, repo, commit_ids, collection_size=None, pre_load=None):
1457 1458 self.repo = repo
1458 1459 self.commit_ids = commit_ids
1459 1460 # TODO: (oliver) this isn't currently hooked up
1460 1461 self.collection_size = None
1461 1462 self.pre_load = pre_load
1462 1463
1463 1464 def __len__(self):
1464 1465 if self.collection_size is not None:
1465 1466 return self.collection_size
1466 1467 return self.commit_ids.__len__()
1467 1468
1468 1469 def __iter__(self):
1469 1470 for commit_id in self.commit_ids:
1470 1471 # TODO: johbo: Mercurial passes in commit indices or commit ids
1471 1472 yield self._commit_factory(commit_id)
1472 1473
1473 1474 def _commit_factory(self, commit_id):
1474 1475 """
1475 1476 Allows backends to override the way commits are generated.
1476 1477 """
1477 1478 return self.repo.get_commit(commit_id=commit_id,
1478 1479 pre_load=self.pre_load)
1479 1480
1480 1481 def __getslice__(self, i, j):
1481 1482 """
1482 1483 Returns an iterator of sliced repository
1483 1484 """
1484 1485 commit_ids = self.commit_ids[i:j]
1485 1486 return self.__class__(
1486 1487 self.repo, commit_ids, pre_load=self.pre_load)
1487 1488
1488 1489 def __repr__(self):
1489 1490 return '<CollectionGenerator[len:%s]>' % (self.__len__())
1490 1491
1491 1492
1492 1493 class Config(object):
1493 1494 """
1494 1495 Represents the configuration for a repository.
1495 1496
1496 1497 The API is inspired by :class:`ConfigParser.ConfigParser` from the
1497 1498 standard library. It implements only the needed subset.
1498 1499 """
1499 1500
1500 1501 def __init__(self):
1501 1502 self._values = {}
1502 1503
1503 1504 def copy(self):
1504 1505 clone = Config()
1505 1506 for section, values in self._values.items():
1506 1507 clone._values[section] = values.copy()
1507 1508 return clone
1508 1509
1509 1510 def __repr__(self):
1510 1511 return '<Config(%s sections) at %s>' % (
1511 1512 len(self._values), hex(id(self)))
1512 1513
1513 1514 def items(self, section):
1514 1515 return self._values.get(section, {}).iteritems()
1515 1516
1516 1517 def get(self, section, option):
1517 1518 return self._values.get(section, {}).get(option)
1518 1519
1519 1520 def set(self, section, option, value):
1520 1521 section_values = self._values.setdefault(section, {})
1521 1522 section_values[option] = value
1522 1523
1523 1524 def clear_section(self, section):
1524 1525 self._values[section] = {}
1525 1526
1526 1527 def serialize(self):
1527 1528 """
1528 1529 Creates a list of three tuples (section, key, value) representing
1529 1530 this config object.
1530 1531 """
1531 1532 items = []
1532 1533 for section in self._values:
1533 1534 for option, value in self._values[section].items():
1534 1535 items.append(
1535 1536 (safe_str(section), safe_str(option), safe_str(value)))
1536 1537 return items
1537 1538
1538 1539
1539 1540 class Diff(object):
1540 1541 """
1541 1542 Represents a diff result from a repository backend.
1542 1543
1543 Subclasses have to provide a backend specific value for :attr:`_header_re`.
1544 Subclasses have to provide a backend specific value for
1545 :attr:`_header_re` and :attr:`_meta_re`.
1544 1546 """
1545
1547 _meta_re = None
1546 1548 _header_re = None
1547 1549
1548 1550 def __init__(self, raw_diff):
1549 1551 self.raw = raw_diff
1550 1552
1551 1553 def chunks(self):
1552 1554 """
1553 1555 split the diff in chunks of separate --git a/file b/file chunks
1554 1556 to make diffs consistent we must prepend with \n, and make sure
1555 1557 we can detect last chunk as this was also has special rule
1556 1558 """
1557 chunks = ('\n' + self.raw).split('\ndiff --git')[1:]
1559
1560 diff_parts = ('\n' + self.raw).split('\ndiff --git')
1561 header = diff_parts[0]
1562
1563 if self._meta_re:
1564 match = self._meta_re.match(header)
1565
1566 chunks = diff_parts[1:]
1558 1567 total_chunks = len(chunks)
1559 return (DiffChunk(chunk, self, cur_chunk == total_chunks)
1568
1569 return (
1570 DiffChunk(chunk, self, cur_chunk == total_chunks)
1560 1571 for cur_chunk, chunk in enumerate(chunks, start=1))
1561 1572
1562 1573
1563 1574 class DiffChunk(object):
1564 1575
1565 1576 def __init__(self, chunk, diff, last_chunk):
1566 1577 self._diff = diff
1567 1578
1568 1579 # since we split by \ndiff --git that part is lost from original diff
1569 1580 # we need to re-apply it at the end, EXCEPT ! if it's last chunk
1570 1581 if not last_chunk:
1571 1582 chunk += '\n'
1572 1583
1573 1584 match = self._diff._header_re.match(chunk)
1574 1585 self.header = match.groupdict()
1575 1586 self.diff = chunk[match.end():]
1576 1587 self.raw = chunk
@@ -1,48 +1,52 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2014-2016 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
22 22 """
23 23 SVN diff module
24 24 """
25 25
26 26 import re
27 27
28 28 from rhodecode.lib.vcs.backends import base
29 29
30 30
31 31 class SubversionDiff(base.Diff):
32 32
33 _meta_re = re.compile(r"""
34 (?:^(?P<svn_bin_patch>Cannot[ ]display:[ ]file[ ]marked[ ]as[ ]a[ ]binary[ ]type.)(?:\n|$))?
35 """, re.VERBOSE | re.MULTILINE)
36
33 37 _header_re = re.compile(r"""
34 38 #^diff[ ]--git
35 39 [ ]"?a/(?P<a_path>.+?)"?[ ]"?b/(?P<b_path>.+?)"?\n
36 40 (?:^similarity[ ]index[ ](?P<similarity_index>\d+)%\n
37 41 ^rename[ ]from[ ](?P<rename_from>[^\r\n]+)\n
38 42 ^rename[ ]to[ ](?P<rename_to>[^\r\n]+)(?:\n|$))?
39 43 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
40 44 ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
41 45 (?:^new[ ]file[ ]mode[ ](?P<new_file_mode>.+)(?:\n|$))?
42 46 (?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
43 47 (?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+)
44 48 \.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
45 49 (?:^(?P<bin_patch>GIT[ ]binary[ ]patch)(?:\n|$))?
46 50 (?:^---[ ](a/(?P<a_file>.+)|/dev/null)\t\(revision[ ]\d+\)(?:\n|$))?
47 51 (?:^\+\+\+[ ](b/(?P<b_file>.+)|/dev/null)\t\(revision[ ]\d+\)(?:\n|$))?
48 52 """, re.VERBOSE | re.MULTILINE)
@@ -1,2216 +1,2217 b''
1 1 //Primary CSS
2 2
3 3 //--- IMPORTS ------------------//
4 4
5 5 @import 'helpers';
6 6 @import 'mixins';
7 7 @import 'rcicons';
8 8 @import 'fonts';
9 9 @import 'variables';
10 10 @import 'bootstrap-variables';
11 11 @import 'form-bootstrap';
12 12 @import 'codemirror';
13 13 @import 'legacy_code_styles';
14 14 @import 'progress-bar';
15 15
16 16 @import 'type';
17 17 @import 'alerts';
18 18 @import 'buttons';
19 19 @import 'tags';
20 20 @import 'code-block';
21 21 @import 'examples';
22 22 @import 'login';
23 23 @import 'main-content';
24 24 @import 'select2';
25 25 @import 'comments';
26 26 @import 'panels-bootstrap';
27 27 @import 'panels';
28 28 @import 'deform';
29 29
30 30 //--- BASE ------------------//
31 31 .noscript-error {
32 32 top: 0;
33 33 left: 0;
34 34 width: 100%;
35 35 z-index: 101;
36 36 text-align: center;
37 37 font-family: @text-semibold;
38 38 font-size: 120%;
39 39 color: white;
40 40 background-color: @alert2;
41 41 padding: 5px 0 5px 0;
42 42 }
43 43
44 44 html {
45 45 display: table;
46 46 height: 100%;
47 47 width: 100%;
48 48 }
49 49
50 50 body {
51 51 display: table-cell;
52 52 width: 100%;
53 53 }
54 54
55 55 //--- LAYOUT ------------------//
56 56
57 57 .hidden{
58 58 display: none !important;
59 59 }
60 60
61 61 .box{
62 62 float: left;
63 63 width: 100%;
64 64 }
65 65
66 66 .browser-header {
67 67 clear: both;
68 68 }
69 69 .main {
70 70 clear: both;
71 71 padding:0 0 @pagepadding;
72 72 height: auto;
73 73
74 74 &:after { //clearfix
75 75 content:"";
76 76 clear:both;
77 77 width:100%;
78 78 display:block;
79 79 }
80 80 }
81 81
82 82 .action-link{
83 83 margin-left: @padding;
84 84 padding-left: @padding;
85 85 border-left: @border-thickness solid @border-default-color;
86 86 }
87 87
88 88 input + .action-link, .action-link.first{
89 89 border-left: none;
90 90 }
91 91
92 92 .action-link.last{
93 93 margin-right: @padding;
94 94 padding-right: @padding;
95 95 }
96 96
97 97 .action-link.active,
98 98 .action-link.active a{
99 99 color: @grey4;
100 100 }
101 101
102 102 ul.simple-list{
103 103 list-style: none;
104 104 margin: 0;
105 105 padding: 0;
106 106 }
107 107
108 108 .main-content {
109 109 padding-bottom: @pagepadding;
110 110 }
111 111
112 112 .wide-mode-wrapper {
113 113 max-width:2400px !important;
114 114 }
115 115
116 116 .wrapper {
117 117 position: relative;
118 118 max-width: @wrapper-maxwidth;
119 119 margin: 0 auto;
120 120 }
121 121
122 122 #content {
123 123 clear: both;
124 124 padding: 0 @contentpadding;
125 125 }
126 126
127 127 .advanced-settings-fields{
128 128 input{
129 129 margin-left: @textmargin;
130 130 margin-right: @padding/2;
131 131 }
132 132 }
133 133
134 134 .cs_files_title {
135 135 margin: @pagepadding 0 0;
136 136 }
137 137
138 138 input.inline[type="file"] {
139 139 display: inline;
140 140 }
141 141
142 142 .error_page {
143 143 margin: 10% auto;
144 144
145 145 h1 {
146 146 color: @grey2;
147 147 }
148 148
149 149 .alert {
150 150 margin: @padding 0;
151 151 }
152 152
153 153 .error-branding {
154 154 font-family: @text-semibold;
155 155 color: @grey4;
156 156 }
157 157
158 158 .error_message {
159 159 font-family: @text-regular;
160 160 }
161 161
162 162 .sidebar {
163 163 min-height: 275px;
164 164 margin: 0;
165 165 padding: 0 0 @sidebarpadding @sidebarpadding;
166 166 border: none;
167 167 }
168 168
169 169 .main-content {
170 170 position: relative;
171 171 margin: 0 @sidebarpadding @sidebarpadding;
172 172 padding: 0 0 0 @sidebarpadding;
173 173 border-left: @border-thickness solid @grey5;
174 174
175 175 @media (max-width:767px) {
176 176 clear: both;
177 177 width: 100%;
178 178 margin: 0;
179 179 border: none;
180 180 }
181 181 }
182 182
183 183 .inner-column {
184 184 float: left;
185 185 width: 29.75%;
186 186 min-height: 150px;
187 187 margin: @sidebarpadding 2% 0 0;
188 188 padding: 0 2% 0 0;
189 189 border-right: @border-thickness solid @grey5;
190 190
191 191 @media (max-width:767px) {
192 192 clear: both;
193 193 width: 100%;
194 194 border: none;
195 195 }
196 196
197 197 ul {
198 198 padding-left: 1.25em;
199 199 }
200 200
201 201 &:last-child {
202 202 margin: @sidebarpadding 0 0;
203 203 border: none;
204 204 }
205 205
206 206 h4 {
207 207 margin: 0 0 @padding;
208 208 font-family: @text-semibold;
209 209 }
210 210 }
211 211 }
212 212 .error-page-logo {
213 213 width: 130px;
214 214 height: 160px;
215 215 }
216 216
217 217 // HEADER
218 218 .header {
219 219
220 220 // TODO: johbo: Fix login pages, so that they work without a min-height
221 221 // for the header and then remove the min-height. I chose a smaller value
222 222 // intentionally here to avoid rendering issues in the main navigation.
223 223 min-height: 49px;
224 224
225 225 position: relative;
226 226 vertical-align: bottom;
227 227 padding: 0 @header-padding;
228 228 background-color: @grey2;
229 229 color: @grey5;
230 230
231 231 .title {
232 232 overflow: visible;
233 233 }
234 234
235 235 &:before,
236 236 &:after {
237 237 content: "";
238 238 clear: both;
239 239 width: 100%;
240 240 }
241 241
242 242 // TODO: johbo: Avoids breaking "Repositories" chooser
243 243 .select2-container .select2-choice .select2-arrow {
244 244 display: none;
245 245 }
246 246 }
247 247
248 248 #header-inner {
249 249 &.title {
250 250 margin: 0;
251 251 }
252 252 &:before,
253 253 &:after {
254 254 content: "";
255 255 clear: both;
256 256 }
257 257 }
258 258
259 259 // Gists
260 260 #files_data {
261 261 clear: both; //for firefox
262 262 }
263 263 #gistid {
264 264 margin-right: @padding;
265 265 }
266 266
267 267 // Global Settings Editor
268 268 .textarea.editor {
269 269 float: left;
270 270 position: relative;
271 271 max-width: @texteditor-width;
272 272
273 273 select {
274 274 position: absolute;
275 275 top:10px;
276 276 right:0;
277 277 }
278 278
279 279 .CodeMirror {
280 280 margin: 0;
281 281 }
282 282
283 283 .help-block {
284 284 margin: 0 0 @padding;
285 285 padding:.5em;
286 286 background-color: @grey6;
287 287 }
288 288 }
289 289
290 290 ul.auth_plugins {
291 291 margin: @padding 0 @padding @legend-width;
292 292 padding: 0;
293 293
294 294 li {
295 295 margin-bottom: @padding;
296 296 line-height: 1em;
297 297 list-style-type: none;
298 298
299 299 .auth_buttons .btn {
300 300 margin-right: @padding;
301 301 }
302 302
303 303 &:before { content: none; }
304 304 }
305 305 }
306 306
307 307
308 308 // My Account PR list
309 309
310 310 #show_closed {
311 311 margin: 0 1em 0 0;
312 312 }
313 313
314 314 .pullrequestlist {
315 315 .closed {
316 316 background-color: @grey6;
317 317 }
318 318 .td-status {
319 319 padding-left: .5em;
320 320 }
321 321 .log-container .truncate {
322 322 height: 2.75em;
323 323 white-space: pre-line;
324 324 }
325 325 table.rctable .user {
326 326 padding-left: 0;
327 327 }
328 328 table.rctable {
329 329 td.td-description,
330 330 .rc-user {
331 331 min-width: auto;
332 332 }
333 333 }
334 334 }
335 335
336 336 // Pull Requests
337 337
338 338 .pullrequests_section_head {
339 339 display: block;
340 340 clear: both;
341 341 margin: @padding 0;
342 342 font-family: @text-bold;
343 343 }
344 344
345 345 .pr-origininfo, .pr-targetinfo {
346 346 position: relative;
347 347
348 348 .tag {
349 349 display: inline-block;
350 350 margin: 0 1em .5em 0;
351 351 }
352 352
353 353 .clone-url {
354 354 display: inline-block;
355 355 margin: 0 0 .5em 0;
356 356 padding: 0;
357 357 line-height: 1.2em;
358 358 }
359 359 }
360 360
361 361 .pr-pullinfo {
362 362 clear: both;
363 363 margin: .5em 0;
364 364 }
365 365
366 366 #pr-title-input {
367 367 width: 72%;
368 368 font-size: 1em;
369 369 font-family: @text-bold;
370 370 margin: 0;
371 371 padding: 0 0 0 @padding/4;
372 372 line-height: 1.7em;
373 373 color: @text-color;
374 374 letter-spacing: .02em;
375 375 }
376 376
377 377 #pullrequest_title {
378 378 width: 100%;
379 379 box-sizing: border-box;
380 380 }
381 381
382 382 #pr_open_message {
383 383 border: @border-thickness solid #fff;
384 384 border-radius: @border-radius;
385 385 padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0;
386 386 text-align: right;
387 387 overflow: hidden;
388 388 }
389 389
390 390 .pr-submit-button {
391 391 float: right;
392 392 margin: 0 0 0 5px;
393 393 }
394 394
395 395 .pr-spacing-container {
396 396 padding: 20px;
397 397 clear: both
398 398 }
399 399
400 400 #pr-description-input {
401 401 margin-bottom: 0;
402 402 }
403 403
404 404 .pr-description-label {
405 405 vertical-align: top;
406 406 }
407 407
408 408 .perms_section_head {
409 409 min-width: 625px;
410 410
411 411 h2 {
412 412 margin-bottom: 0;
413 413 }
414 414
415 415 .label-checkbox {
416 416 float: left;
417 417 }
418 418
419 419 &.field {
420 420 margin: @space 0 @padding;
421 421 }
422 422
423 423 &:first-child.field {
424 424 margin-top: 0;
425 425
426 426 .label {
427 427 margin-top: 0;
428 428 padding-top: 0;
429 429 }
430 430
431 431 .radios {
432 432 padding-top: 0;
433 433 }
434 434 }
435 435
436 436 .radios {
437 437 float: right;
438 438 position: relative;
439 439 width: 405px;
440 440 }
441 441 }
442 442
443 443 //--- MODULES ------------------//
444 444
445 445
446 446 // Server Announcement
447 447 #server-announcement {
448 448 width: 95%;
449 449 margin: @padding auto;
450 450 padding: @padding;
451 451 border-width: 2px;
452 452 border-style: solid;
453 453 .border-radius(2px);
454 454 font-family: @text-bold;
455 455
456 456 &.info { border-color: @alert4; background-color: @alert4-inner; }
457 457 &.warning { border-color: @alert3; background-color: @alert3-inner; }
458 458 &.error { border-color: @alert2; background-color: @alert2-inner; }
459 459 &.success { border-color: @alert1; background-color: @alert1-inner; }
460 460 &.neutral { border-color: @grey3; background-color: @grey6; }
461 461 }
462 462
463 463 // Fixed Sidebar Column
464 464 .sidebar-col-wrapper {
465 465 padding-left: @sidebar-all-width;
466 466
467 467 .sidebar {
468 468 width: @sidebar-width;
469 469 margin-left: -@sidebar-all-width;
470 470 }
471 471 }
472 472
473 473 .sidebar-col-wrapper.scw-small {
474 474 padding-left: @sidebar-small-all-width;
475 475
476 476 .sidebar {
477 477 width: @sidebar-small-width;
478 478 margin-left: -@sidebar-small-all-width;
479 479 }
480 480 }
481 481
482 482
483 483 // FOOTER
484 484 #footer {
485 485 padding: 0;
486 486 text-align: center;
487 487 vertical-align: middle;
488 488 color: @grey2;
489 489 background-color: @grey6;
490 490
491 491 p {
492 492 margin: 0;
493 493 padding: 1em;
494 494 line-height: 1em;
495 495 }
496 496
497 497 .server-instance { //server instance
498 498 display: none;
499 499 }
500 500
501 501 .title {
502 502 float: none;
503 503 margin: 0 auto;
504 504 }
505 505 }
506 506
507 507 button.close {
508 508 padding: 0;
509 509 cursor: pointer;
510 510 background: transparent;
511 511 border: 0;
512 512 .box-shadow(none);
513 513 -webkit-appearance: none;
514 514 }
515 515
516 516 .close {
517 517 float: right;
518 518 font-size: 21px;
519 519 font-family: @text-bootstrap;
520 520 line-height: 1em;
521 521 font-weight: bold;
522 522 color: @grey2;
523 523
524 524 &:hover,
525 525 &:focus {
526 526 color: @grey1;
527 527 text-decoration: none;
528 528 cursor: pointer;
529 529 }
530 530 }
531 531
532 532 // GRID
533 533 .sorting,
534 534 .sorting_desc,
535 535 .sorting_asc {
536 536 cursor: pointer;
537 537 }
538 538 .sorting_desc:after {
539 539 content: "\00A0\25B2";
540 540 font-size: .75em;
541 541 }
542 542 .sorting_asc:after {
543 543 content: "\00A0\25BC";
544 544 font-size: .68em;
545 545 }
546 546
547 547
548 548 .user_auth_tokens {
549 549
550 550 &.truncate {
551 551 white-space: nowrap;
552 552 overflow: hidden;
553 553 text-overflow: ellipsis;
554 554 }
555 555
556 556 .fields .field .input {
557 557 margin: 0;
558 558 }
559 559
560 560 input#description {
561 561 width: 100px;
562 562 margin: 0;
563 563 }
564 564
565 565 .drop-menu {
566 566 // TODO: johbo: Remove this, should work out of the box when
567 567 // having multiple inputs inline
568 568 margin: 0 0 0 5px;
569 569 }
570 570 }
571 571 #user_list_table {
572 572 .closed {
573 573 background-color: @grey6;
574 574 }
575 575 }
576 576
577 577
578 578 input {
579 579 &.disabled {
580 580 opacity: .5;
581 581 }
582 582 }
583 583
584 584 // remove extra padding in firefox
585 585 input::-moz-focus-inner { border:0; padding:0 }
586 586
587 587 .adjacent input {
588 588 margin-bottom: @padding;
589 589 }
590 590
591 591 .permissions_boxes {
592 592 display: block;
593 593 }
594 594
595 595 //TODO: lisa: this should be in tables
596 596 .show_more_col {
597 597 width: 20px;
598 598 }
599 599
600 600 //FORMS
601 601
602 602 .medium-inline,
603 603 input#description.medium-inline {
604 604 display: inline;
605 605 width: @medium-inline-input-width;
606 606 min-width: 100px;
607 607 }
608 608
609 609 select {
610 610 //reset
611 611 -webkit-appearance: none;
612 612 -moz-appearance: none;
613 613
614 614 display: inline-block;
615 615 height: 28px;
616 616 width: auto;
617 617 margin: 0 @padding @padding 0;
618 618 padding: 0 18px 0 8px;
619 619 line-height:1em;
620 620 font-size: @basefontsize;
621 621 border: @border-thickness solid @rcblue;
622 622 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
623 623 color: @rcblue;
624 624
625 625 &:after {
626 626 content: "\00A0\25BE";
627 627 }
628 628
629 629 &:focus {
630 630 outline: none;
631 631 }
632 632 }
633 633
634 634 option {
635 635 &:focus {
636 636 outline: none;
637 637 }
638 638 }
639 639
640 640 input,
641 641 textarea {
642 642 padding: @input-padding;
643 643 border: @input-border-thickness solid @border-highlight-color;
644 644 .border-radius (@border-radius);
645 645 font-family: @text-light;
646 646 font-size: @basefontsize;
647 647
648 648 &.input-sm {
649 649 padding: 5px;
650 650 }
651 651
652 652 &#description {
653 653 min-width: @input-description-minwidth;
654 654 min-height: 1em;
655 655 padding: 10px;
656 656 }
657 657 }
658 658
659 659 .field-sm {
660 660 input,
661 661 textarea {
662 662 padding: 5px;
663 663 }
664 664 }
665 665
666 666 textarea {
667 667 display: block;
668 668 clear: both;
669 669 width: 100%;
670 670 min-height: 100px;
671 671 margin-bottom: @padding;
672 672 .box-sizing(border-box);
673 673 overflow: auto;
674 674 }
675 675
676 676 label {
677 677 font-family: @text-light;
678 678 }
679 679
680 680 // GRAVATARS
681 681 // centers gravatar on username to the right
682 682
683 683 .gravatar {
684 684 display: inline;
685 685 min-width: 16px;
686 686 min-height: 16px;
687 687 margin: -5px 0;
688 688 padding: 0;
689 689 line-height: 1em;
690 690 border: 1px solid @grey4;
691 691
692 692 &.gravatar-large {
693 693 margin: -0.5em .25em -0.5em 0;
694 694 }
695 695
696 696 & + .user {
697 697 display: inline;
698 698 margin: 0;
699 699 padding: 0 0 0 .17em;
700 700 line-height: 1em;
701 701 }
702 702 }
703 703
704 704 .user-inline-data {
705 705 display: inline-block;
706 706 float: left;
707 707 padding-left: .5em;
708 708 line-height: 1.3em;
709 709 }
710 710
711 711 .rc-user { // gravatar + user wrapper
712 712 float: left;
713 713 position: relative;
714 714 min-width: 100px;
715 715 max-width: 200px;
716 716 min-height: (@gravatar-size + @border-thickness * 2); // account for border
717 717 display: block;
718 718 padding: 0 0 0 (@gravatar-size + @basefontsize/2 + @border-thickness * 2);
719 719
720 720
721 721 .gravatar {
722 722 display: block;
723 723 position: absolute;
724 724 top: 0;
725 725 left: 0;
726 726 min-width: @gravatar-size;
727 727 min-height: @gravatar-size;
728 728 margin: 0;
729 729 }
730 730
731 731 .user {
732 732 display: block;
733 733 max-width: 175px;
734 734 padding-top: 2px;
735 735 overflow: hidden;
736 736 text-overflow: ellipsis;
737 737 }
738 738 }
739 739
740 740 .gist-gravatar,
741 741 .journal_container {
742 742 .gravatar-large {
743 743 margin: 0 .5em -10px 0;
744 744 }
745 745 }
746 746
747 747
748 748 // ADMIN SETTINGS
749 749
750 750 // Tag Patterns
751 751 .tag_patterns {
752 752 .tag_input {
753 753 margin-bottom: @padding;
754 754 }
755 755 }
756 756
757 757 .locked_input {
758 758 position: relative;
759 759
760 760 input {
761 761 display: inline;
762 762 margin-top: 3px;
763 763 }
764 764
765 765 br {
766 766 display: none;
767 767 }
768 768
769 769 .error-message {
770 770 float: left;
771 771 width: 100%;
772 772 }
773 773
774 774 .lock_input_button {
775 775 display: inline;
776 776 }
777 777
778 778 .help-block {
779 779 clear: both;
780 780 }
781 781 }
782 782
783 783 // Notifications
784 784
785 785 .notifications_buttons {
786 786 margin: 0 0 @space 0;
787 787 padding: 0;
788 788
789 789 .btn {
790 790 display: inline-block;
791 791 }
792 792 }
793 793
794 794 .notification-list {
795 795
796 796 div {
797 797 display: inline-block;
798 798 vertical-align: middle;
799 799 }
800 800
801 801 .container {
802 802 display: block;
803 803 margin: 0 0 @padding 0;
804 804 }
805 805
806 806 .delete-notifications {
807 807 margin-left: @padding;
808 808 text-align: right;
809 809 cursor: pointer;
810 810 }
811 811
812 812 .read-notifications {
813 813 margin-left: @padding/2;
814 814 text-align: right;
815 815 width: 35px;
816 816 cursor: pointer;
817 817 }
818 818
819 819 .icon-minus-sign {
820 820 color: @alert2;
821 821 }
822 822
823 823 .icon-ok-sign {
824 824 color: @alert1;
825 825 }
826 826 }
827 827
828 828 .user_settings {
829 829 float: left;
830 830 clear: both;
831 831 display: block;
832 832 width: 100%;
833 833
834 834 .gravatar_box {
835 835 margin-bottom: @padding;
836 836
837 837 &:after {
838 838 content: " ";
839 839 clear: both;
840 840 width: 100%;
841 841 }
842 842 }
843 843
844 844 .fields .field {
845 845 clear: both;
846 846 }
847 847 }
848 848
849 849 .advanced_settings {
850 850 margin-bottom: @space;
851 851
852 852 .help-block {
853 853 margin-left: 0;
854 854 }
855 855
856 856 button + .help-block {
857 857 margin-top: @padding;
858 858 }
859 859 }
860 860
861 861 // admin settings radio buttons and labels
862 862 .label-2 {
863 863 float: left;
864 864 width: @label2-width;
865 865
866 866 label {
867 867 color: @grey1;
868 868 }
869 869 }
870 870 .checkboxes {
871 871 float: left;
872 872 width: @checkboxes-width;
873 873 margin-bottom: @padding;
874 874
875 875 .checkbox {
876 876 width: 100%;
877 877
878 878 label {
879 879 margin: 0;
880 880 padding: 0;
881 881 }
882 882 }
883 883
884 884 .checkbox + .checkbox {
885 885 display: inline-block;
886 886 }
887 887
888 888 label {
889 889 margin-right: 1em;
890 890 }
891 891 }
892 892
893 893 // CHANGELOG
894 894 .container_header {
895 895 float: left;
896 896 display: block;
897 897 width: 100%;
898 898 margin: @padding 0 @padding;
899 899
900 900 #filter_changelog {
901 901 float: left;
902 902 margin-right: @padding;
903 903 }
904 904
905 905 .breadcrumbs_light {
906 906 display: inline-block;
907 907 }
908 908 }
909 909
910 910 .info_box {
911 911 float: right;
912 912 }
913 913
914 914
915 915 #graph_nodes {
916 916 padding-top: 43px;
917 917 }
918 918
919 919 #graph_content{
920 920
921 921 // adjust for table headers so that graph renders properly
922 922 // #graph_nodes padding - table cell padding
923 923 padding-top: (@space - (@basefontsize * 2.4));
924 924
925 925 &.graph_full_width {
926 926 width: 100%;
927 927 max-width: 100%;
928 928 }
929 929 }
930 930
931 931 #graph {
932 932 .flag_status {
933 933 margin: 0;
934 934 }
935 935
936 936 .pagination-left {
937 937 float: left;
938 938 clear: both;
939 939 }
940 940
941 941 .log-container {
942 942 max-width: 345px;
943 943
944 944 .message{
945 945 max-width: 340px;
946 946 }
947 947 }
948 948
949 949 .graph-col-wrapper {
950 950 padding-left: 110px;
951 951
952 952 #graph_nodes {
953 953 width: 100px;
954 954 margin-left: -110px;
955 955 float: left;
956 956 clear: left;
957 957 }
958 958 }
959 959 }
960 960
961 961 #filter_changelog {
962 962 float: left;
963 963 }
964 964
965 965
966 966 //--- THEME ------------------//
967 967
968 968 #logo {
969 969 float: left;
970 970 margin: 9px 0 0 0;
971 971
972 972 .header {
973 973 background-color: transparent;
974 974 }
975 975
976 976 a {
977 977 display: inline-block;
978 978 }
979 979
980 980 img {
981 981 height:30px;
982 982 }
983 983 }
984 984
985 985 .logo-wrapper {
986 986 float:left;
987 987 }
988 988
989 989 .branding{
990 990 float: left;
991 991 padding: 9px 2px;
992 992 line-height: 1em;
993 993 font-size: @navigation-fontsize;
994 994 }
995 995
996 996 img {
997 997 border: none;
998 998 outline: none;
999 999 }
1000 1000 user-profile-header
1001 1001 label {
1002 1002
1003 1003 input[type="checkbox"] {
1004 1004 margin-right: 1em;
1005 1005 }
1006 1006 input[type="radio"] {
1007 1007 margin-right: 1em;
1008 1008 }
1009 1009 }
1010 1010
1011 1011 .flag_status {
1012 1012 margin: 2px 8px 6px 2px;
1013 1013 &.under_review {
1014 1014 .circle(5px, @alert3);
1015 1015 }
1016 1016 &.approved {
1017 1017 .circle(5px, @alert1);
1018 1018 }
1019 1019 &.rejected,
1020 1020 &.forced_closed{
1021 1021 .circle(5px, @alert2);
1022 1022 }
1023 1023 &.not_reviewed {
1024 1024 .circle(5px, @grey5);
1025 1025 }
1026 1026 }
1027 1027
1028 1028 .flag_status_comment_box {
1029 1029 margin: 5px 6px 0px 2px;
1030 1030 }
1031 1031 .test_pattern_preview {
1032 1032 margin: @space 0;
1033 1033
1034 1034 p {
1035 1035 margin-bottom: 0;
1036 1036 border-bottom: @border-thickness solid @border-default-color;
1037 1037 color: @grey3;
1038 1038 }
1039 1039
1040 1040 .btn {
1041 1041 margin-bottom: @padding;
1042 1042 }
1043 1043 }
1044 1044 #test_pattern_result {
1045 1045 display: none;
1046 1046 &:extend(pre);
1047 1047 padding: .9em;
1048 1048 color: @grey3;
1049 1049 background-color: @grey7;
1050 1050 border-right: @border-thickness solid @border-default-color;
1051 1051 border-bottom: @border-thickness solid @border-default-color;
1052 1052 border-left: @border-thickness solid @border-default-color;
1053 1053 }
1054 1054
1055 1055 #repo_vcs_settings {
1056 1056 #inherit_overlay_vcs_default {
1057 1057 display: none;
1058 1058 }
1059 1059 #inherit_overlay_vcs_custom {
1060 1060 display: custom;
1061 1061 }
1062 1062 &.inherited {
1063 1063 #inherit_overlay_vcs_default {
1064 1064 display: block;
1065 1065 }
1066 1066 #inherit_overlay_vcs_custom {
1067 1067 display: none;
1068 1068 }
1069 1069 }
1070 1070 }
1071 1071
1072 1072 .issue-tracker-link {
1073 1073 color: @rcblue;
1074 1074 }
1075 1075
1076 1076 // Issue Tracker Table Show/Hide
1077 1077 #repo_issue_tracker {
1078 1078 #inherit_overlay {
1079 1079 display: none;
1080 1080 }
1081 1081 #custom_overlay {
1082 1082 display: custom;
1083 1083 }
1084 1084 &.inherited {
1085 1085 #inherit_overlay {
1086 1086 display: block;
1087 1087 }
1088 1088 #custom_overlay {
1089 1089 display: none;
1090 1090 }
1091 1091 }
1092 1092 }
1093 1093 table.issuetracker {
1094 1094 &.readonly {
1095 1095 tr, td {
1096 1096 color: @grey3;
1097 1097 }
1098 1098 }
1099 1099 .edit {
1100 1100 display: none;
1101 1101 }
1102 1102 .editopen {
1103 1103 .edit {
1104 1104 display: inline;
1105 1105 }
1106 1106 .entry {
1107 1107 display: none;
1108 1108 }
1109 1109 }
1110 1110 tr td.td-action {
1111 1111 min-width: 117px;
1112 1112 }
1113 1113 td input {
1114 1114 max-width: none;
1115 1115 min-width: 30px;
1116 1116 width: 80%;
1117 1117 }
1118 1118 .issuetracker_pref input {
1119 1119 width: 40%;
1120 1120 }
1121 1121 input.edit_issuetracker_update {
1122 1122 margin-right: 0;
1123 1123 width: auto;
1124 1124 }
1125 1125 }
1126 1126
1127 1127 table.integrations {
1128 1128 .td-icon {
1129 1129 width: 20px;
1130 1130 .integration-icon {
1131 1131 height: 20px;
1132 1132 width: 20px;
1133 1133 }
1134 1134 }
1135 1135 }
1136 1136
1137 1137 .integrations {
1138 1138 a.integration-box {
1139 1139 color: @text-color;
1140 1140 &:hover {
1141 1141 .panel {
1142 1142 background: #fbfbfb;
1143 1143 }
1144 1144 }
1145 1145 .integration-icon {
1146 1146 width: 30px;
1147 1147 height: 30px;
1148 1148 margin-right: 20px;
1149 1149 float: left;
1150 1150 }
1151 1151
1152 1152 .panel-body {
1153 1153 padding: 10px;
1154 1154 }
1155 1155 .panel {
1156 1156 margin-bottom: 10px;
1157 1157 }
1158 1158 h2 {
1159 1159 display: inline-block;
1160 1160 margin: 0;
1161 1161 min-width: 140px;
1162 1162 }
1163 1163 }
1164 1164 }
1165 1165
1166 1166 //Permissions Settings
1167 1167 #add_perm {
1168 1168 margin: 0 0 @padding;
1169 1169 cursor: pointer;
1170 1170 }
1171 1171
1172 1172 .perm_ac {
1173 1173 input {
1174 1174 width: 95%;
1175 1175 }
1176 1176 }
1177 1177
1178 1178 .autocomplete-suggestions {
1179 1179 width: auto !important; // overrides autocomplete.js
1180 1180 margin: 0;
1181 1181 border: @border-thickness solid @rcblue;
1182 1182 border-radius: @border-radius;
1183 1183 color: @rcblue;
1184 1184 background-color: white;
1185 1185 }
1186 1186 .autocomplete-selected {
1187 1187 background: #F0F0F0;
1188 1188 }
1189 1189 .ac-container-wrap {
1190 1190 margin: 0;
1191 1191 padding: 8px;
1192 1192 border-bottom: @border-thickness solid @rclightblue;
1193 1193 list-style-type: none;
1194 1194 cursor: pointer;
1195 1195
1196 1196 &:hover {
1197 1197 background-color: @rclightblue;
1198 1198 }
1199 1199
1200 1200 img {
1201 1201 height: @gravatar-size;
1202 1202 width: @gravatar-size;
1203 1203 margin-right: 1em;
1204 1204 }
1205 1205
1206 1206 strong {
1207 1207 font-weight: normal;
1208 1208 }
1209 1209 }
1210 1210
1211 1211 // Settings Dropdown
1212 1212 .user-menu .container {
1213 1213 padding: 0 4px;
1214 1214 margin: 0;
1215 1215 }
1216 1216
1217 1217 .user-menu .gravatar {
1218 1218 cursor: pointer;
1219 1219 }
1220 1220
1221 1221 .codeblock {
1222 1222 margin-bottom: @padding;
1223 1223 clear: both;
1224 1224
1225 1225 .stats{
1226 1226 overflow: hidden;
1227 1227 }
1228 1228
1229 1229 .message{
1230 1230 textarea{
1231 1231 margin: 0;
1232 1232 }
1233 1233 }
1234 1234
1235 1235 .code-header {
1236 1236 .stats {
1237 1237 line-height: 2em;
1238 1238
1239 1239 .revision_id {
1240 1240 margin-left: 0;
1241 1241 }
1242 1242 .buttons {
1243 1243 padding-right: 0;
1244 1244 }
1245 1245 }
1246 1246
1247 1247 .item{
1248 1248 margin-right: 0.5em;
1249 1249 }
1250 1250 }
1251 1251
1252 1252 #editor_container{
1253 1253 position: relative;
1254 1254 margin: @padding;
1255 1255 }
1256 1256 }
1257 1257
1258 1258 #file_history_container {
1259 1259 display: none;
1260 1260 }
1261 1261
1262 1262 .file-history-inner {
1263 1263 margin-bottom: 10px;
1264 1264 }
1265 1265
1266 1266 // Pull Requests
1267 1267 .summary-details {
1268 1268 width: 72%;
1269 1269 }
1270 1270 .pr-summary {
1271 1271 border-bottom: @border-thickness solid @grey5;
1272 1272 margin-bottom: @space;
1273 1273 }
1274 1274 .reviewers-title {
1275 1275 width: 25%;
1276 1276 min-width: 200px;
1277 1277 }
1278 1278 .reviewers {
1279 1279 width: 25%;
1280 1280 min-width: 200px;
1281 1281 }
1282 1282 .reviewers ul li {
1283 1283 position: relative;
1284 1284 width: 100%;
1285 1285 margin-bottom: 8px;
1286 1286 }
1287 1287 .reviewers_member {
1288 1288 width: 100%;
1289 1289 overflow: auto;
1290 1290 }
1291 1291 .reviewer_reason {
1292 1292 padding-left: 20px;
1293 1293 }
1294 1294 .reviewer_status {
1295 1295 display: inline-block;
1296 1296 vertical-align: top;
1297 1297 width: 7%;
1298 1298 min-width: 20px;
1299 1299 height: 1.2em;
1300 1300 margin-top: 3px;
1301 1301 line-height: 1em;
1302 1302 }
1303 1303
1304 1304 .reviewer_name {
1305 1305 display: inline-block;
1306 1306 max-width: 83%;
1307 1307 padding-right: 20px;
1308 1308 vertical-align: middle;
1309 1309 line-height: 1;
1310 1310
1311 1311 .rc-user {
1312 1312 min-width: 0;
1313 1313 margin: -2px 1em 0 0;
1314 1314 }
1315 1315
1316 1316 .reviewer {
1317 1317 float: left;
1318 1318 }
1319 1319
1320 1320 &.to-delete {
1321 1321 .user,
1322 1322 .reviewer {
1323 1323 text-decoration: line-through;
1324 1324 }
1325 1325 }
1326 1326 }
1327 1327
1328 1328 .reviewer_member_remove {
1329 1329 position: absolute;
1330 1330 right: 0;
1331 1331 top: 0;
1332 1332 width: 16px;
1333 1333 margin-bottom: 10px;
1334 1334 padding: 0;
1335 1335 color: black;
1336 1336 }
1337 1337 .reviewer_member_status {
1338 1338 margin-top: 5px;
1339 1339 }
1340 1340 .pr-summary #summary{
1341 1341 width: 100%;
1342 1342 }
1343 1343 .pr-summary .action_button:hover {
1344 1344 border: 0;
1345 1345 cursor: pointer;
1346 1346 }
1347 1347 .pr-details-title {
1348 1348 padding-bottom: 8px;
1349 1349 border-bottom: @border-thickness solid @grey5;
1350 1350
1351 1351 .action_button.disabled {
1352 1352 color: @grey4;
1353 1353 cursor: inherit;
1354 1354 }
1355 1355 .action_button {
1356 1356 color: @rcblue;
1357 1357 }
1358 1358 }
1359 1359 .pr-details-content {
1360 1360 margin-top: @textmargin;
1361 1361 margin-bottom: @textmargin;
1362 1362 }
1363 1363 .pr-description {
1364 1364 white-space:pre-wrap;
1365 1365 }
1366 1366 .group_members {
1367 1367 margin-top: 0;
1368 1368 padding: 0;
1369 1369 list-style: outside none none;
1370 1370
1371 1371 img {
1372 1372 height: @gravatar-size;
1373 1373 width: @gravatar-size;
1374 1374 margin-right: .5em;
1375 1375 margin-left: 3px;
1376 1376 }
1377 1377
1378 1378 .to-delete {
1379 1379 .user {
1380 1380 text-decoration: line-through;
1381 1381 }
1382 1382 }
1383 1383 }
1384 1384
1385 1385 .compare_view_commits_title {
1386 1386 .disabled {
1387 1387 cursor: inherit;
1388 1388 &:hover{
1389 1389 background-color: inherit;
1390 1390 color: inherit;
1391 1391 }
1392 1392 }
1393 1393 }
1394 1394
1395 1395 // new entry in group_members
1396 1396 .td-author-new-entry {
1397 1397 background-color: rgba(red(@alert1), green(@alert1), blue(@alert1), 0.3);
1398 1398 }
1399 1399
1400 1400 .usergroup_member_remove {
1401 1401 width: 16px;
1402 1402 margin-bottom: 10px;
1403 1403 padding: 0;
1404 1404 color: black !important;
1405 1405 cursor: pointer;
1406 1406 }
1407 1407
1408 1408 .reviewer_ac .ac-input {
1409 1409 width: 92%;
1410 1410 margin-bottom: 1em;
1411 1411 }
1412 1412
1413 1413 .compare_view_commits tr{
1414 1414 height: 20px;
1415 1415 }
1416 1416 .compare_view_commits td {
1417 1417 vertical-align: top;
1418 1418 padding-top: 10px;
1419 1419 }
1420 1420 .compare_view_commits .author {
1421 1421 margin-left: 5px;
1422 1422 }
1423 1423
1424 1424 .compare_view_files {
1425 1425 width: 100%;
1426 1426
1427 1427 td {
1428 1428 vertical-align: middle;
1429 1429 }
1430 1430 }
1431 1431
1432 1432 .compare_view_filepath {
1433 1433 color: @grey1;
1434 1434 }
1435 1435
1436 1436 .show_more {
1437 1437 display: inline-block;
1438 1438 position: relative;
1439 1439 vertical-align: middle;
1440 1440 width: 4px;
1441 1441 height: @basefontsize;
1442 1442
1443 1443 &:after {
1444 1444 content: "\00A0\25BE";
1445 1445 display: inline-block;
1446 1446 width:10px;
1447 1447 line-height: 5px;
1448 1448 font-size: 12px;
1449 1449 cursor: pointer;
1450 1450 }
1451 1451 }
1452 1452
1453 1453 .journal_more .show_more {
1454 1454 display: inline;
1455 1455
1456 1456 &:after {
1457 1457 content: none;
1458 1458 }
1459 1459 }
1460 1460
1461 1461 .open .show_more:after,
1462 1462 .select2-dropdown-open .show_more:after {
1463 1463 .rotate(180deg);
1464 1464 margin-left: 4px;
1465 1465 }
1466 1466
1467 1467
1468 1468 .compare_view_commits .collapse_commit:after {
1469 1469 cursor: pointer;
1470 1470 content: "\00A0\25B4";
1471 1471 margin-left: -3px;
1472 1472 font-size: 17px;
1473 1473 color: @grey4;
1474 1474 }
1475 1475
1476 1476 .diff_links {
1477 1477 margin-left: 8px;
1478 1478 }
1479 1479
1480 p.ancestor {
1480 div.ancestor {
1481 1481 margin: @padding 0;
1482 line-height: 3.0em;
1482 1483 }
1483 1484
1484 1485 .cs_icon_td input[type="checkbox"] {
1485 1486 display: none;
1486 1487 }
1487 1488
1488 1489 .cs_icon_td .expand_file_icon:after {
1489 1490 cursor: pointer;
1490 1491 content: "\00A0\25B6";
1491 1492 font-size: 12px;
1492 1493 color: @grey4;
1493 1494 }
1494 1495
1495 1496 .cs_icon_td .collapse_file_icon:after {
1496 1497 cursor: pointer;
1497 1498 content: "\00A0\25BC";
1498 1499 font-size: 12px;
1499 1500 color: @grey4;
1500 1501 }
1501 1502
1502 1503 /*new binary
1503 1504 NEW_FILENODE = 1
1504 1505 DEL_FILENODE = 2
1505 1506 MOD_FILENODE = 3
1506 1507 RENAMED_FILENODE = 4
1507 1508 COPIED_FILENODE = 5
1508 1509 CHMOD_FILENODE = 6
1509 1510 BIN_FILENODE = 7
1510 1511 */
1511 1512 .cs_files_expand {
1512 1513 font-size: @basefontsize + 5px;
1513 1514 line-height: 1.8em;
1514 1515 float: right;
1515 1516 }
1516 1517
1517 1518 .cs_files_expand span{
1518 1519 color: @rcblue;
1519 1520 cursor: pointer;
1520 1521 }
1521 1522 .cs_files {
1522 1523 clear: both;
1523 1524 padding-bottom: @padding;
1524 1525
1525 1526 .cur_cs {
1526 1527 margin: 10px 2px;
1527 1528 font-weight: bold;
1528 1529 }
1529 1530
1530 1531 .node {
1531 1532 float: left;
1532 1533 }
1533 1534
1534 1535 .changes {
1535 1536 float: right;
1536 1537 color: white;
1537 1538 font-size: @basefontsize - 4px;
1538 1539 margin-top: 4px;
1539 1540 opacity: 0.6;
1540 1541 filter: Alpha(opacity=60); /* IE8 and earlier */
1541 1542
1542 1543 .added {
1543 1544 background-color: @alert1;
1544 1545 float: left;
1545 1546 text-align: center;
1546 1547 }
1547 1548
1548 1549 .deleted {
1549 1550 background-color: @alert2;
1550 1551 float: left;
1551 1552 text-align: center;
1552 1553 }
1553 1554
1554 1555 .bin {
1555 1556 background-color: @alert1;
1556 1557 text-align: center;
1557 1558 }
1558 1559
1559 1560 /*new binary*/
1560 1561 .bin.bin1 {
1561 1562 background-color: @alert1;
1562 1563 text-align: center;
1563 1564 }
1564 1565
1565 1566 /*deleted binary*/
1566 1567 .bin.bin2 {
1567 1568 background-color: @alert2;
1568 1569 text-align: center;
1569 1570 }
1570 1571
1571 1572 /*mod binary*/
1572 1573 .bin.bin3 {
1573 1574 background-color: @grey2;
1574 1575 text-align: center;
1575 1576 }
1576 1577
1577 1578 /*rename file*/
1578 1579 .bin.bin4 {
1579 1580 background-color: @alert4;
1580 1581 text-align: center;
1581 1582 }
1582 1583
1583 1584 /*copied file*/
1584 1585 .bin.bin5 {
1585 1586 background-color: @alert4;
1586 1587 text-align: center;
1587 1588 }
1588 1589
1589 1590 /*chmod file*/
1590 1591 .bin.bin6 {
1591 1592 background-color: @grey2;
1592 1593 text-align: center;
1593 1594 }
1594 1595 }
1595 1596 }
1596 1597
1597 1598 .cs_files .cs_added, .cs_files .cs_A,
1598 1599 .cs_files .cs_added, .cs_files .cs_M,
1599 1600 .cs_files .cs_added, .cs_files .cs_D {
1600 1601 height: 16px;
1601 1602 padding-right: 10px;
1602 1603 margin-top: 7px;
1603 1604 text-align: left;
1604 1605 }
1605 1606
1606 1607 .cs_icon_td {
1607 1608 min-width: 16px;
1608 1609 width: 16px;
1609 1610 }
1610 1611
1611 1612 .pull-request-merge {
1612 1613 padding: 10px 0;
1613 1614 margin-top: 10px;
1614 1615 margin-bottom: 20px;
1615 1616 }
1616 1617
1617 1618 .pull-request-merge .pull-request-wrap {
1618 1619 height: 25px;
1619 1620 padding: 5px 0;
1620 1621 }
1621 1622
1622 1623 .pull-request-merge span {
1623 1624 margin-right: 10px;
1624 1625 }
1625 1626 #close_pull_request {
1626 1627 margin-right: 0px;
1627 1628 }
1628 1629
1629 1630 .empty_data {
1630 1631 color: @grey4;
1631 1632 }
1632 1633
1633 1634 #changeset_compare_view_content {
1634 1635 margin-bottom: @space;
1635 1636 clear: both;
1636 1637 width: 100%;
1637 1638 box-sizing: border-box;
1638 1639 .border-radius(@border-radius);
1639 1640
1640 1641 .help-block {
1641 1642 margin: @padding 0;
1642 1643 color: @text-color;
1643 1644 }
1644 1645
1645 1646 .empty_data {
1646 1647 margin: @padding 0;
1647 1648 }
1648 1649
1649 1650 .alert {
1650 1651 margin-bottom: @space;
1651 1652 }
1652 1653 }
1653 1654
1654 1655 .table_disp {
1655 1656 .status {
1656 1657 width: auto;
1657 1658
1658 1659 .flag_status {
1659 1660 float: left;
1660 1661 }
1661 1662 }
1662 1663 }
1663 1664
1664 1665 .status_box_menu {
1665 1666 margin: 0;
1666 1667 }
1667 1668
1668 1669 .notification-table{
1669 1670 margin-bottom: @space;
1670 1671 display: table;
1671 1672 width: 100%;
1672 1673
1673 1674 .container{
1674 1675 display: table-row;
1675 1676
1676 1677 .notification-header{
1677 1678 border-bottom: @border-thickness solid @border-default-color;
1678 1679 }
1679 1680
1680 1681 .notification-subject{
1681 1682 display: table-cell;
1682 1683 }
1683 1684 }
1684 1685 }
1685 1686
1686 1687 // Notifications
1687 1688 .notification-header{
1688 1689 display: table;
1689 1690 width: 100%;
1690 1691 padding: floor(@basefontsize/2) 0;
1691 1692 line-height: 1em;
1692 1693
1693 1694 .desc, .delete-notifications, .read-notifications{
1694 1695 display: table-cell;
1695 1696 text-align: left;
1696 1697 }
1697 1698
1698 1699 .desc{
1699 1700 width: 1163px;
1700 1701 }
1701 1702
1702 1703 .delete-notifications, .read-notifications{
1703 1704 width: 35px;
1704 1705 min-width: 35px; //fixes when only one button is displayed
1705 1706 }
1706 1707 }
1707 1708
1708 1709 .notification-body {
1709 1710 .markdown-block,
1710 1711 .rst-block {
1711 1712 padding: @padding 0;
1712 1713 }
1713 1714
1714 1715 .notification-subject {
1715 1716 padding: @textmargin 0;
1716 1717 border-bottom: @border-thickness solid @border-default-color;
1717 1718 }
1718 1719 }
1719 1720
1720 1721
1721 1722 .notifications_buttons{
1722 1723 float: right;
1723 1724 }
1724 1725
1725 1726 #notification-status{
1726 1727 display: inline;
1727 1728 }
1728 1729
1729 1730 // Repositories
1730 1731
1731 1732 #summary.fields{
1732 1733 display: table;
1733 1734
1734 1735 .field{
1735 1736 display: table-row;
1736 1737
1737 1738 .label-summary{
1738 1739 display: table-cell;
1739 1740 min-width: @label-summary-minwidth;
1740 1741 padding-top: @padding/2;
1741 1742 padding-bottom: @padding/2;
1742 1743 padding-right: @padding/2;
1743 1744 }
1744 1745
1745 1746 .input{
1746 1747 display: table-cell;
1747 1748 padding: @padding/2;
1748 1749
1749 1750 input{
1750 1751 min-width: 29em;
1751 1752 padding: @padding/4;
1752 1753 }
1753 1754 }
1754 1755 .statistics, .downloads{
1755 1756 .disabled{
1756 1757 color: @grey4;
1757 1758 }
1758 1759 }
1759 1760 }
1760 1761 }
1761 1762
1762 1763 #summary{
1763 1764 width: 70%;
1764 1765 }
1765 1766
1766 1767
1767 1768 // Journal
1768 1769 .journal.title {
1769 1770 h5 {
1770 1771 float: left;
1771 1772 margin: 0;
1772 1773 width: 70%;
1773 1774 }
1774 1775
1775 1776 ul {
1776 1777 float: right;
1777 1778 display: inline-block;
1778 1779 margin: 0;
1779 1780 width: 30%;
1780 1781 text-align: right;
1781 1782
1782 1783 li {
1783 1784 display: inline;
1784 1785 font-size: @journal-fontsize;
1785 1786 line-height: 1em;
1786 1787
1787 1788 &:before { content: none; }
1788 1789 }
1789 1790 }
1790 1791 }
1791 1792
1792 1793 .filterexample {
1793 1794 position: absolute;
1794 1795 top: 95px;
1795 1796 left: @contentpadding;
1796 1797 color: @rcblue;
1797 1798 font-size: 11px;
1798 1799 font-family: @text-regular;
1799 1800 cursor: help;
1800 1801
1801 1802 &:hover {
1802 1803 color: @rcdarkblue;
1803 1804 }
1804 1805
1805 1806 @media (max-width:768px) {
1806 1807 position: relative;
1807 1808 top: auto;
1808 1809 left: auto;
1809 1810 display: block;
1810 1811 }
1811 1812 }
1812 1813
1813 1814
1814 1815 #journal{
1815 1816 margin-bottom: @space;
1816 1817
1817 1818 .journal_day{
1818 1819 margin-bottom: @textmargin/2;
1819 1820 padding-bottom: @textmargin/2;
1820 1821 font-size: @journal-fontsize;
1821 1822 border-bottom: @border-thickness solid @border-default-color;
1822 1823 }
1823 1824
1824 1825 .journal_container{
1825 1826 margin-bottom: @space;
1826 1827
1827 1828 .journal_user{
1828 1829 display: inline-block;
1829 1830 }
1830 1831 .journal_action_container{
1831 1832 display: block;
1832 1833 margin-top: @textmargin;
1833 1834
1834 1835 div{
1835 1836 display: inline;
1836 1837 }
1837 1838
1838 1839 div.journal_action_params{
1839 1840 display: block;
1840 1841 }
1841 1842
1842 1843 div.journal_repo:after{
1843 1844 content: "\A";
1844 1845 white-space: pre;
1845 1846 }
1846 1847
1847 1848 div.date{
1848 1849 display: block;
1849 1850 margin-bottom: @textmargin;
1850 1851 }
1851 1852 }
1852 1853 }
1853 1854 }
1854 1855
1855 1856 // Files
1856 1857 .edit-file-title {
1857 1858 border-bottom: @border-thickness solid @border-default-color;
1858 1859
1859 1860 .breadcrumbs {
1860 1861 margin-bottom: 0;
1861 1862 }
1862 1863 }
1863 1864
1864 1865 .edit-file-fieldset {
1865 1866 margin-top: @sidebarpadding;
1866 1867
1867 1868 .fieldset {
1868 1869 .left-label {
1869 1870 width: 13%;
1870 1871 }
1871 1872 .right-content {
1872 1873 width: 87%;
1873 1874 max-width: 100%;
1874 1875 }
1875 1876 .filename-label {
1876 1877 margin-top: 13px;
1877 1878 }
1878 1879 .commit-message-label {
1879 1880 margin-top: 4px;
1880 1881 }
1881 1882 .file-upload-input {
1882 1883 input {
1883 1884 display: none;
1884 1885 }
1885 1886 }
1886 1887 p {
1887 1888 margin-top: 5px;
1888 1889 }
1889 1890
1890 1891 }
1891 1892 .custom-path-link {
1892 1893 margin-left: 5px;
1893 1894 }
1894 1895 #commit {
1895 1896 resize: vertical;
1896 1897 }
1897 1898 }
1898 1899
1899 1900 .delete-file-preview {
1900 1901 max-height: 250px;
1901 1902 }
1902 1903
1903 1904 .new-file,
1904 1905 #filter_activate,
1905 1906 #filter_deactivate {
1906 1907 float: left;
1907 1908 margin: 0 0 0 15px;
1908 1909 }
1909 1910
1910 1911 h3.files_location{
1911 1912 line-height: 2.4em;
1912 1913 }
1913 1914
1914 1915 .browser-nav {
1915 1916 display: table;
1916 1917 margin-bottom: @space;
1917 1918
1918 1919
1919 1920 .info_box {
1920 1921 display: inline-table;
1921 1922 height: 2.5em;
1922 1923
1923 1924 .browser-cur-rev, .info_box_elem {
1924 1925 display: table-cell;
1925 1926 vertical-align: middle;
1926 1927 }
1927 1928
1928 1929 .info_box_elem {
1929 1930 border-top: @border-thickness solid @rcblue;
1930 1931 border-bottom: @border-thickness solid @rcblue;
1931 1932
1932 1933 #at_rev, a {
1933 1934 padding: 0.6em 0.9em;
1934 1935 margin: 0;
1935 1936 .box-shadow(none);
1936 1937 border: 0;
1937 1938 height: 12px;
1938 1939 }
1939 1940
1940 1941 input#at_rev {
1941 1942 max-width: 50px;
1942 1943 text-align: right;
1943 1944 }
1944 1945
1945 1946 &.previous {
1946 1947 border: @border-thickness solid @rcblue;
1947 1948 .disabled {
1948 1949 color: @grey4;
1949 1950 cursor: not-allowed;
1950 1951 }
1951 1952 }
1952 1953
1953 1954 &.next {
1954 1955 border: @border-thickness solid @rcblue;
1955 1956 .disabled {
1956 1957 color: @grey4;
1957 1958 cursor: not-allowed;
1958 1959 }
1959 1960 }
1960 1961 }
1961 1962
1962 1963 .browser-cur-rev {
1963 1964
1964 1965 span{
1965 1966 margin: 0;
1966 1967 color: @rcblue;
1967 1968 height: 12px;
1968 1969 display: inline-block;
1969 1970 padding: 0.7em 1em ;
1970 1971 border: @border-thickness solid @rcblue;
1971 1972 margin-right: @padding;
1972 1973 }
1973 1974 }
1974 1975 }
1975 1976
1976 1977 .search_activate {
1977 1978 display: table-cell;
1978 1979 vertical-align: middle;
1979 1980
1980 1981 input, label{
1981 1982 margin: 0;
1982 1983 padding: 0;
1983 1984 }
1984 1985
1985 1986 input{
1986 1987 margin-left: @textmargin;
1987 1988 }
1988 1989
1989 1990 }
1990 1991 }
1991 1992
1992 1993 .browser-cur-rev{
1993 1994 margin-bottom: @textmargin;
1994 1995 }
1995 1996
1996 1997 #node_filter_box_loading{
1997 1998 .info_text;
1998 1999 }
1999 2000
2000 2001 .browser-search {
2001 2002 margin: -25px 0px 5px 0px;
2002 2003 }
2003 2004
2004 2005 .node-filter {
2005 2006 font-size: @repo-title-fontsize;
2006 2007 padding: 4px 0px 0px 0px;
2007 2008
2008 2009 .node-filter-path {
2009 2010 float: left;
2010 2011 color: @grey4;
2011 2012 }
2012 2013 .node-filter-input {
2013 2014 float: left;
2014 2015 margin: -2px 0px 0px 2px;
2015 2016 input {
2016 2017 padding: 2px;
2017 2018 border: none;
2018 2019 font-size: @repo-title-fontsize;
2019 2020 }
2020 2021 }
2021 2022 }
2022 2023
2023 2024
2024 2025 .browser-result{
2025 2026 td a{
2026 2027 margin-left: 0.5em;
2027 2028 display: inline-block;
2028 2029
2029 2030 em{
2030 2031 font-family: @text-bold;
2031 2032 }
2032 2033 }
2033 2034 }
2034 2035
2035 2036 .browser-highlight{
2036 2037 background-color: @grey5-alpha;
2037 2038 }
2038 2039
2039 2040
2040 2041 // Search
2041 2042
2042 2043 .search-form{
2043 2044 #q {
2044 2045 width: @search-form-width;
2045 2046 }
2046 2047 .fields{
2047 2048 margin: 0 0 @space;
2048 2049 }
2049 2050
2050 2051 label{
2051 2052 display: inline-block;
2052 2053 margin-right: @textmargin;
2053 2054 padding-top: 0.25em;
2054 2055 }
2055 2056
2056 2057
2057 2058 .results{
2058 2059 clear: both;
2059 2060 margin: 0 0 @padding;
2060 2061 }
2061 2062 }
2062 2063
2063 2064 div.search-feedback-items {
2064 2065 display: inline-block;
2065 2066 padding:0px 0px 0px 96px;
2066 2067 }
2067 2068
2068 2069 div.search-code-body {
2069 2070 background-color: #ffffff; padding: 5px 0 5px 10px;
2070 2071 pre {
2071 2072 .match { background-color: #faffa6;}
2072 2073 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
2073 2074 }
2074 2075 }
2075 2076
2076 2077 .expand_commit.search {
2077 2078 .show_more.open {
2078 2079 height: auto;
2079 2080 max-height: none;
2080 2081 }
2081 2082 }
2082 2083
2083 2084 .search-results {
2084 2085
2085 2086 h2 {
2086 2087 margin-bottom: 0;
2087 2088 }
2088 2089 .codeblock {
2089 2090 border: none;
2090 2091 background: transparent;
2091 2092 }
2092 2093
2093 2094 .codeblock-header {
2094 2095 border: none;
2095 2096 background: transparent;
2096 2097 }
2097 2098
2098 2099 .code-body {
2099 2100 border: @border-thickness solid @border-default-color;
2100 2101 .border-radius(@border-radius);
2101 2102 }
2102 2103
2103 2104 .td-commit {
2104 2105 &:extend(pre);
2105 2106 border-bottom: @border-thickness solid @border-default-color;
2106 2107 }
2107 2108
2108 2109 .message {
2109 2110 height: auto;
2110 2111 max-width: 350px;
2111 2112 white-space: normal;
2112 2113 text-overflow: initial;
2113 2114 overflow: visible;
2114 2115
2115 2116 .match { background-color: #faffa6;}
2116 2117 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
2117 2118 }
2118 2119
2119 2120 }
2120 2121
2121 2122 table.rctable td.td-search-results div {
2122 2123 max-width: 100%;
2123 2124 }
2124 2125
2125 2126 #tip-box, .tip-box{
2126 2127 padding: @menupadding/2;
2127 2128 display: block;
2128 2129 border: @border-thickness solid @border-highlight-color;
2129 2130 .border-radius(@border-radius);
2130 2131 background-color: white;
2131 2132 z-index: 99;
2132 2133 white-space: pre-wrap;
2133 2134 }
2134 2135
2135 2136 #linktt {
2136 2137 width: 79px;
2137 2138 }
2138 2139
2139 2140 #help_kb .modal-content{
2140 2141 max-width: 750px;
2141 2142 margin: 10% auto;
2142 2143
2143 2144 table{
2144 2145 td,th{
2145 2146 border-bottom: none;
2146 2147 line-height: 2.5em;
2147 2148 }
2148 2149 th{
2149 2150 padding-bottom: @textmargin/2;
2150 2151 }
2151 2152 td.keys{
2152 2153 text-align: center;
2153 2154 }
2154 2155 }
2155 2156
2156 2157 .block-left{
2157 2158 width: 45%;
2158 2159 margin-right: 5%;
2159 2160 }
2160 2161 .modal-footer{
2161 2162 clear: both;
2162 2163 }
2163 2164 .key.tag{
2164 2165 padding: 0.5em;
2165 2166 background-color: @rcblue;
2166 2167 color: white;
2167 2168 border-color: @rcblue;
2168 2169 .box-shadow(none);
2169 2170 }
2170 2171 }
2171 2172
2172 2173
2173 2174
2174 2175 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2175 2176
2176 2177 @import 'statistics-graph';
2177 2178 @import 'tables';
2178 2179 @import 'forms';
2179 2180 @import 'diff';
2180 2181 @import 'summary';
2181 2182 @import 'navigation';
2182 2183
2183 2184 //--- SHOW/HIDE SECTIONS --//
2184 2185
2185 2186 .btn-collapse {
2186 2187 float: right;
2187 2188 text-align: right;
2188 2189 font-family: @text-light;
2189 2190 font-size: @basefontsize;
2190 2191 cursor: pointer;
2191 2192 border: none;
2192 2193 color: @rcblue;
2193 2194 }
2194 2195
2195 2196 table.rctable,
2196 2197 table.dataTable {
2197 2198 .btn-collapse {
2198 2199 float: right;
2199 2200 text-align: right;
2200 2201 }
2201 2202 }
2202 2203
2203 2204
2204 2205 // TODO: johbo: Fix for IE10, this avoids that we see a border
2205 2206 // and padding around checkboxes and radio boxes. Move to the right place,
2206 2207 // or better: Remove this once we did the form refactoring.
2207 2208 input[type=checkbox],
2208 2209 input[type=radio] {
2209 2210 padding: 0;
2210 2211 border: none;
2211 2212 }
2212 2213
2213 2214 .toggle-ajax-spinner{
2214 2215 height: 16px;
2215 2216 width: 16px;
2216 2217 }
@@ -1,264 +1,265 b''
1 1 // summary.less
2 2 // For use in RhodeCode applications;
3 3 // Used for headers and file detail summary screens.
4 4
5 5 .summary {
6 6 float: left;
7 7 position: relative;
8 8 width: 100%;
9 9 margin: 0;
10 10 padding: 0;
11 11
12 12 .summary-detail-header {
13 13 float: left;
14 14 display: block;
15 15 width: 100%;
16 16 margin-bottom: @textmargin;
17 17 padding: 0 0 .5em 0;
18 18 border-bottom: @border-thickness solid @border-default-color;
19 19
20 20 .breadcrumbs {
21 21 float: left;
22 22 display: inline;
23 23 margin: 0;
24 24 padding: 0;
25 25 }
26 26 h4 {
27 27 float: left;
28 28 margin: 0 1em 0 0;
29 29 padding: 0;
30 30 line-height: 1.2em;
31 31 font-size: @basefontsize;
32 32 }
33 33
34 34 .action_link {
35 35 float: right;
36 36 }
37 37
38 38 .new-file {
39 39 float: right;
40 40 margin-top: -1.5em;
41 41 }
42 42 }
43 43
44 44 .summary-detail {
45 45 float: left;
46 46 position: relative;
47 47 width: 73%;
48 48 margin: 0 3% @space 0;
49 49 padding: 0;
50 50
51 51 .file_diff_buttons {
52 52 margin-top: @space;
53 53 }
54 54
55 55 // commit message
56 56 .commit {
57 57 white-space: pre-wrap;
58 58 }
59 59
60 60 #clone_url,
61 61 #clone_url_id {
62 62 min-width: 29em;
63 63 padding: @padding/4;
64 64 }
65 65
66 66 &.directory {
67 67 margin-bottom: 0;
68 68 }
69 69
70 70 .desc {
71 71 white-space: pre-wrap;
72 72 }
73 73 .disabled {
74 74 opacity: .5;
75 cursor: inherit;
75 76 }
76 77 .help-block {
77 78 color: inherit;
78 79 margin: 0;
79 80 }
80 81 }
81 82
82 83 .sidebar-right {
83 84 float: left;
84 85 width: 24%;
85 86 margin: 0;
86 87 padding: 0;
87 88
88 89 ul {
89 90 margin-left: 0;
90 91 padding-left: 0;
91 92
92 93 li {
93 94
94 95 &:before {
95 96 content: none;
96 97 width: 0;
97 98 }
98 99 }
99 100 }
100 101 }
101 102
102 103 #clone_by_name, #clone_by_id{
103 104 display: inline-block;
104 105 margin-left: @padding;
105 106 }
106 107
107 108 .codeblock {
108 109 border: none;
109 110 background-color: transparent;
110 111 }
111 112
112 113 .code-body {
113 114 border: @border-thickness solid @border-default-color;
114 115 .border-radius(@border-radius);
115 116 }
116 117 }
117 118
118 119 // this is used outside of just the summary
119 120 .fieldset, // similar to form fieldset
120 121 .summary .sidebar-right-content { // these have to match
121 122 clear: both;
122 123 float: left;
123 124 position: relative;
124 125 display:block;
125 126 width: 100%;
126 127 min-height: 1em;
127 128 margin-bottom: @textmargin;
128 129 padding: 0;
129 130 line-height: 1.2em;
130 131
131 132 &:after { // clearfix
132 133 content: "";
133 134 clear: both;
134 135 width: 100%;
135 136 height: 1em;
136 137 }
137 138 }
138 139
139 140 .summary .sidebar-right-content {
140 141 margin-bottom: @space;
141 142
142 143 .rc-user {
143 144 min-width: 0;
144 145 }
145 146 }
146 147
147 148 .fieldset {
148 149
149 150 .left-label { // similar to form legend
150 151 float: left;
151 152 display: block;
152 153 width: 25%;
153 154 margin: 0;
154 155 padding: 0;
155 156 font-family: @text-semibold;
156 157 }
157 158
158 159 .right-content { // similar to form fields
159 160 float: left;
160 161 display: block;
161 162 width: 75%;
162 163 margin: 0 0 0 -15%;
163 164 padding: 0 0 0 15%;
164 165
165 166 .truncate-wrap,
166 167 .truncate {
167 168 max-width: 100%;
168 169 width: 100%;
169 170 }
170 171
171 172 .commit-long {
172 173 overflow-x: auto;
173 174 }
174 175 }
175 176 .commit.truncate-wrap {
176 177 overflow:hidden;
177 178 text-overflow: ellipsis;
178 179 }
179 180 }
180 181
181 182 // expand commit message
182 183 #message_expand {
183 184 clear: both;
184 185 display: block;
185 186 color: @rcblue;
186 187 cursor: pointer;
187 188 }
188 189
189 190 #trimmed_message_box {
190 191 max-height: floor(2 * @basefontsize * 1.2); // 2 lines * line-height
191 192 overflow: hidden;
192 193 }
193 194
194 195 // show/hide comments button
195 196 .show-inline-comments {
196 197 display: inline;
197 198 cursor: pointer;
198 199
199 200 .comments-show { display: inline; }
200 201 .comments-hide { display: none; }
201 202
202 203 &.comments-visible {
203 204 .comments-show { display: none; }
204 205 .comments-hide { display: inline; }
205 206 }
206 207 }
207 208
208 209 // Quick Start section
209 210 .quick_start {
210 211 float: left;
211 212 display: block;
212 213 position: relative;
213 214
214 215 // adds some space to make copy and paste easier
215 216 .left-label,
216 217 .right-content {
217 218 line-height: 1.6em;
218 219 }
219 220 }
220 221
221 222 .submodule {
222 223 .summary-detail {
223 224 width: 100%;
224 225
225 226 .btn-collapse {
226 227 display: none;
227 228 }
228 229 }
229 230 }
230 231
231 232 .codeblock-header {
232 233 float: left;
233 234 display: block;
234 235 width: 100%;
235 236 margin: 0;
236 237 padding: @space 0 @padding 0;
237 238 border-top: @border-thickness solid @border-default-color;
238 239
239 240 .stats {
240 241 float: left;
241 242 width: 50%;
242 243 }
243 244
244 245 .buttons {
245 246 float: right;
246 247 width: 50%;
247 248 text-align: right;
248 249 color: @grey4;
249 250 }
250 251 }
251 252
252 253 #summary-menu-stats {
253 254
254 255 .stats-bullet {
255 256 color: @grey3;
256 257 min-width: 3em;
257 258 }
258 259
259 260 .repo-size {
260 261 margin-bottom: .5em;
261 262 }
262 263
263 264 }
264 265
@@ -1,174 +1,184 b''
1 1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 2 // #
3 3 // # This program is free software: you can redistribute it and/or modify
4 4 // # it under the terms of the GNU Affero General Public License, version 3
5 5 // # (only), as published by the Free Software Foundation.
6 6 // #
7 7 // # This program is distributed in the hope that it will be useful,
8 8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 // # GNU General Public License for more details.
11 11 // #
12 12 // # You should have received a copy of the GNU Affero General Public License
13 13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 // #
15 15 // # This program is dual-licensed. If you wish to learn more about the
16 16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 /**
20 20 * INJECT .format function into String
21 21 * Usage: "My name is {0} {1}".format("Johny","Bravo")
22 22 * Return "My name is Johny Bravo"
23 23 * Inspired by https://gist.github.com/1049426
24 24 */
25 25 String.prototype.format = function() {
26 26
27 27 function format() {
28 28 var str = this;
29 29 var len = arguments.length+1;
30 30 var safe = undefined;
31 31 var arg = undefined;
32 32
33 33 // For each {0} {1} {n...} replace with the argument in that position. If
34 34 // the argument is an object or an array it will be stringified to JSON.
35 35 for (var i=0; i < len; arg = arguments[i++]) {
36 36 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
37 37 str = str.replace(new RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
38 38 }
39 39 return str;
40 40 }
41 41
42 42 // Save a reference of what may already exist under the property native.
43 43 // Allows for doing something like: if("".format.native) { /* use native */ }
44 44 format.native = String.prototype.format;
45 45
46 46 // Replace the prototype property
47 47 return format;
48 48 }();
49 49
50 50 String.prototype.strip = function(char) {
51 51 if(char === undefined){
52 52 char = '\\s';
53 53 }
54 54 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
55 55 };
56 56
57 57 String.prototype.lstrip = function(char) {
58 58 if(char === undefined){
59 59 char = '\\s';
60 60 }
61 61 return this.replace(new RegExp('^'+char+'+'),'');
62 62 };
63 63
64 64 String.prototype.rstrip = function(char) {
65 65 if(char === undefined){
66 66 char = '\\s';
67 67 }
68 68 return this.replace(new RegExp(''+char+'+$'),'');
69 69 };
70 70
71 71 String.prototype.capitalizeFirstLetter = function() {
72 72 return this.charAt(0).toUpperCase() + this.slice(1);
73 73 };
74 74
75 75
76 String.prototype.truncateAfter = function(chars, suffix) {
77 var suffix = suffix || '';
78 if (this.length > chars) {
79 return this.substr(0, chars) + suffix;
80 } else {
81 return this;
82 }
83 };
84
85
76 86 /**
77 87 * Splits remainder
78 88 *
79 89 * @param input
80 90 */
81 91 function splitDelimitedHash(input){
82 92 var splitIx = input.indexOf('/?/');
83 93 if (splitIx !== -1){
84 94 var loc = input.slice(0, splitIx);
85 95 var remainder = input.slice(splitIx + 2);
86 96 }
87 97 else{
88 98 var loc = input;
89 99 var remainder = null;
90 100 }
91 101 //fixes for some urls generated incorrectly
92 102 var result = loc.match('#+(.*)');
93 103 if (result !== null){
94 104 loc = '#' + result[1];
95 105 }
96 106 return {loc:loc, remainder: remainder}
97 107 }
98 108
99 109 /**
100 110 * Escape html characters in string
101 111 */
102 112 var entityMap = {
103 113 "&": "&amp;",
104 114 "<": "&lt;",
105 115 ">": "&gt;",
106 116 '"': '&quot;',
107 117 "'": '&#39;',
108 118 "/": '&#x2F;'
109 119 };
110 120
111 121 function escapeHtml(string) {
112 122 return String(string).replace(/[&<>"'\/]/g, function (s) {
113 123 return entityMap[s];
114 124 });
115 125 }
116 126
117 127 /** encode/decode html special chars**/
118 128 var htmlEnDeCode = (function() {
119 129 var charToEntityRegex,
120 130 entityToCharRegex,
121 131 charToEntity,
122 132 entityToChar;
123 133
124 134 function resetCharacterEntities() {
125 135 charToEntity = {};
126 136 entityToChar = {};
127 137 // add the default set
128 138 addCharacterEntities({
129 139 '&amp;' : '&',
130 140 '&gt;' : '>',
131 141 '&lt;' : '<',
132 142 '&quot;' : '"',
133 143 '&#39;' : "'"
134 144 });
135 145 }
136 146
137 147 function addCharacterEntities(newEntities) {
138 148 var charKeys = [],
139 149 entityKeys = [],
140 150 key, echar;
141 151 for (key in newEntities) {
142 152 echar = newEntities[key];
143 153 entityToChar[key] = echar;
144 154 charToEntity[echar] = key;
145 155 charKeys.push(echar);
146 156 entityKeys.push(key);
147 157 }
148 158 charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')', 'g');
149 159 entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
150 160 }
151 161
152 162 function htmlEncode(value){
153 163 var htmlEncodeReplaceFn = function(match, capture) {
154 164 return charToEntity[capture];
155 165 };
156 166
157 167 return (!value) ? value : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn);
158 168 }
159 169
160 170 function htmlDecode(value) {
161 171 var htmlDecodeReplaceFn = function(match, capture) {
162 172 return (capture in entityToChar) ? entityToChar[capture] : String.fromCharCode(parseInt(capture.substr(2), 10));
163 173 };
164 174
165 175 return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn);
166 176 }
167 177
168 178 resetCharacterEntities();
169 179
170 180 return {
171 181 htmlEncode: htmlEncode,
172 182 htmlDecode: htmlDecode
173 183 };
174 184 })();
@@ -1,314 +1,314 b''
1 1 ## -*- coding: utf-8 -*-
2 2
3 3 <%inherit file="/base/base.html"/>
4 4 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
5 5
6 6 <%def name="title()">
7 7 ${_('%s Commit') % c.repo_name} - ${h.show_id(c.commit)}
8 8 %if c.rhodecode_name:
9 9 &middot; ${h.branding(c.rhodecode_name)}
10 10 %endif
11 11 </%def>
12 12
13 13 <%def name="menu_bar_nav()">
14 14 ${self.menu_items(active='repositories')}
15 15 </%def>
16 16
17 17 <%def name="menu_bar_subnav()">
18 18 ${self.repo_menu(active='changelog')}
19 19 </%def>
20 20
21 21 <%def name="main()">
22 22 <script>
23 23 // TODO: marcink switch this to pyroutes
24 24 AJAX_COMMENT_DELETE_URL = "${url('changeset_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
25 25 templateContext.commit_data.commit_id = "${c.commit.raw_id}";
26 26 </script>
27 27 <div class="box">
28 28 <div class="title">
29 29 ${self.repo_page_title(c.rhodecode_db_repo)}
30 30 </div>
31 31
32 32 <div id="changeset_compare_view_content" class="summary changeset">
33 33 <div class="summary-detail">
34 34 <div class="summary-detail-header">
35 35 <span class="breadcrumbs files_location">
36 36 <h4>${_('Commit')}
37 37 <code>
38 38 ${h.show_id(c.commit)}
39 39 </code>
40 40 </h4>
41 41 </span>
42 42 <span id="parent_link">
43 43 <a href="#" title="${_('Parent Commit')}">${_('Parent')}</a>
44 44 </span>
45 45 |
46 46 <span id="child_link">
47 47 <a href="#" title="${_('Child Commit')}">${_('Child')}</a>
48 48 </span>
49 49 </div>
50 50
51 51 <div class="fieldset">
52 52 <div class="left-label">
53 53 ${_('Description')}:
54 54 </div>
55 55 <div class="right-content">
56 56 <div id="trimmed_message_box" class="commit">${h.urlify_commit_message(c.commit.message,c.repo_name)}</div>
57 57 <div id="message_expand" style="display:none;">
58 58 ${_('Expand')}
59 59 </div>
60 60 </div>
61 61 </div>
62 62
63 63 %if c.statuses:
64 64 <div class="fieldset">
65 65 <div class="left-label">
66 66 ${_('Commit status')}:
67 67 </div>
68 68 <div class="right-content">
69 69 <div class="changeset-status-ico">
70 70 <div class="${'flag_status %s' % c.statuses[0]} pull-left"></div>
71 71 </div>
72 72 <div title="${_('Commit status')}" class="changeset-status-lbl">[${h.commit_status_lbl(c.statuses[0])}]</div>
73 73 </div>
74 74 </div>
75 75 %endif
76 76
77 77 <div class="fieldset">
78 78 <div class="left-label">
79 79 ${_('References')}:
80 80 </div>
81 81 <div class="right-content">
82 82 <div class="tags">
83 83
84 84 %if c.commit.merge:
85 85 <span class="mergetag tag">
86 86 <i class="icon-merge"></i>${_('merge')}
87 87 </span>
88 88 %endif
89 89
90 90 %if h.is_hg(c.rhodecode_repo):
91 91 %for book in c.commit.bookmarks:
92 92 <span class="booktag tag" title="${_('Bookmark %s') % book}">
93 93 <a href="${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
94 94 </span>
95 95 %endfor
96 96 %endif
97 97
98 98 %for tag in c.commit.tags:
99 99 <span class="tagtag tag" title="${_('Tag %s') % tag}">
100 100 <a href="${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id)}"><i class="icon-tag"></i>${tag}</a>
101 101 </span>
102 102 %endfor
103 103
104 104 %if c.commit.branch:
105 105 <span class="branchtag tag" title="${_('Branch %s') % c.commit.branch}">
106 106 <a href="${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id)}"><i class="icon-code-fork"></i>${h.shorter(c.commit.branch)}</a>
107 107 </span>
108 108 %endif
109 109 </div>
110 110 </div>
111 111 </div>
112 112
113 113 <div class="fieldset">
114 114 <div class="left-label">
115 ${_('Diffs')}:
115 ${_('Diff options')}:
116 116 </div>
117 117 <div class="right-content">
118 118 <div class="diff-actions">
119 119 <a href="${h.url('changeset_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id)}" class="tooltip" title="${h.tooltip(_('Raw diff'))}">
120 120 ${_('Raw Diff')}
121 121 </a>
122 122 |
123 123 <a href="${h.url('changeset_patch_home',repo_name=c.repo_name,revision=c.commit.raw_id)}" class="tooltip" title="${h.tooltip(_('Patch diff'))}">
124 124 ${_('Patch Diff')}
125 125 </a>
126 126 |
127 127 <a href="${h.url('changeset_download_home',repo_name=c.repo_name,revision=c.commit.raw_id,diff='download')}" class="tooltip" title="${h.tooltip(_('Download diff'))}">
128 128 ${_('Download Diff')}
129 129 </a>
130 130 |
131 131 ${c.ignorews_url(request.GET)}
132 132 |
133 133 ${c.context_url(request.GET)}
134 134 </div>
135 135 </div>
136 136 </div>
137 137
138 138 <div class="fieldset">
139 139 <div class="left-label">
140 140 ${_('Comments')}:
141 141 </div>
142 142 <div class="right-content">
143 143 <div class="comments-number">
144 144 %if c.comments:
145 145 <a href="#comments">${ungettext("%d Commit comment", "%d Commit comments", len(c.comments)) % len(c.comments)}</a>,
146 146 %else:
147 147 ${ungettext("%d Commit comment", "%d Commit comments", len(c.comments)) % len(c.comments)}
148 148 %endif
149 149 %if c.inline_cnt:
150 150 <a href="#" onclick="return Rhodecode.comments.nextComment();" id="inline-comments-counter">${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}</a>
151 151 %else:
152 152 ${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}
153 153 %endif
154 154 </div>
155 155 </div>
156 156 </div>
157 157
158 158 </div> <!-- end summary-detail -->
159 159
160 160 <div id="commit-stats" class="sidebar-right">
161 161 <div class="summary-detail-header">
162 162 <h4 class="item">
163 163 ${_('Author')}
164 164 </h4>
165 165 </div>
166 166 <div class="sidebar-right-content">
167 167 ${self.gravatar_with_user(c.commit.author)}
168 168 <div class="user-inline-data">- ${h.age_component(c.commit.date)}</div>
169 169 </div>
170 170 </div><!-- end sidebar -->
171 171 </div> <!-- end summary -->
172 172 <div class="cs_files">
173 173 <%namespace name="cbdiffs" file="/codeblocks/diffs.html"/>
174 174 ${cbdiffs.render_diffset_menu()}
175 175 ${cbdiffs.render_diffset(
176 176 c.changes[c.commit.raw_id], commit=c.commit, use_comments=True)}
177 177 </div>
178 178
179 179 ## template for inline comment form
180 180 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
181 181
182 182 ## render comments
183 183 ${comment.generate_comments()}
184 184
185 185 ## main comment form and it status
186 186 ${comment.comments(h.url('changeset_comment', repo_name=c.repo_name, revision=c.commit.raw_id),
187 187 h.commit_status(c.rhodecode_db_repo, c.commit.raw_id))}
188 188 </div>
189 189
190 190 ## FORM FOR MAKING JS ACTION AS CHANGESET COMMENTS
191 191 <script type="text/javascript">
192 192
193 193 $(document).ready(function() {
194 194
195 195 var boxmax = parseInt($('#trimmed_message_box').css('max-height'), 10);
196 196 if($('#trimmed_message_box').height() === boxmax){
197 197 $('#message_expand').show();
198 198 }
199 199
200 200 $('#message_expand').on('click', function(e){
201 201 $('#trimmed_message_box').css('max-height', 'none');
202 202 $(this).hide();
203 203 });
204 204
205 205 $('.show-inline-comments').on('click', function(e){
206 206 var boxid = $(this).attr('data-comment-id');
207 207 var button = $(this);
208 208
209 209 if(button.hasClass("comments-visible")) {
210 210 $('#{0} .inline-comments'.format(boxid)).each(function(index){
211 211 $(this).hide();
212 212 });
213 213 button.removeClass("comments-visible");
214 214 } else {
215 215 $('#{0} .inline-comments'.format(boxid)).each(function(index){
216 216 $(this).show();
217 217 });
218 218 button.addClass("comments-visible");
219 219 }
220 220 });
221 221
222 222
223 223 // next links
224 224 $('#child_link').on('click', function(e){
225 225 // fetch via ajax what is going to be the next link, if we have
226 226 // >1 links show them to user to choose
227 227 if(!$('#child_link').hasClass('disabled')){
228 228 $.ajax({
229 229 url: '${h.url('changeset_children',repo_name=c.repo_name, revision=c.commit.raw_id)}',
230 230 success: function(data) {
231 231 if(data.results.length === 0){
232 232 $('#child_link').html("${_('No Child Commits')}").addClass('disabled');
233 233 }
234 234 if(data.results.length === 1){
235 235 var commit = data.results[0];
236 236 window.location = pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': commit.raw_id});
237 237 }
238 238 else if(data.results.length === 2){
239 239 $('#child_link').addClass('disabled');
240 240 $('#child_link').addClass('double');
241 241 var _html = '';
242 242 _html +='<a title="__title__" href="__url__">__rev__</a> '
243 243 .replace('__rev__','r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0,6)))
244 244 .replace('__title__', data.results[0].message)
245 245 .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[0].raw_id}));
246 246 _html +=' | '
247 247 _html +='<a title="__title__" href="__url__">__rev__</a> '
248 248 .replace('__rev__','r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0,6)))
249 249 .replace('__title__', data.results[1].message)
250 250 .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[1].raw_id}));
251 251 $('#child_link').html(_html);
252 252 }
253 253 }
254 254 });
255 255 e.preventDefault();
256 256 }
257 257 });
258 258
259 259 // prev links
260 260 $('#parent_link').on('click', function(e){
261 261 // fetch via ajax what is going to be the next link, if we have
262 262 // >1 links show them to user to choose
263 263 if(!$('#parent_link').hasClass('disabled')){
264 264 $.ajax({
265 265 url: '${h.url("changeset_parents",repo_name=c.repo_name, revision=c.commit.raw_id)}',
266 266 success: function(data) {
267 267 if(data.results.length === 0){
268 268 $('#parent_link').html('${_('No Parent Commits')}').addClass('disabled');
269 269 }
270 270 if(data.results.length === 1){
271 271 var commit = data.results[0];
272 272 window.location = pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': commit.raw_id});
273 273 }
274 274 else if(data.results.length === 2){
275 275 $('#parent_link').addClass('disabled');
276 276 $('#parent_link').addClass('double');
277 277 var _html = '';
278 278 _html +='<a title="__title__" href="__url__">Parent __rev__</a>'
279 279 .replace('__rev__','r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0,6)))
280 280 .replace('__title__', data.results[0].message)
281 281 .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[0].raw_id}));
282 282 _html +=' | '
283 283 _html +='<a title="__title__" href="__url__">Parent __rev__</a>'
284 284 .replace('__rev__','r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0,6)))
285 285 .replace('__title__', data.results[1].message)
286 286 .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[1].raw_id}));
287 287 $('#parent_link').html(_html);
288 288 }
289 289 }
290 290 });
291 291 e.preventDefault();
292 292 }
293 293 });
294 294
295 295 if (location.hash) {
296 296 var result = splitDelimitedHash(location.hash);
297 297 var line = $('html').find(result.loc);
298 298 if (line.length > 0){
299 299 offsetScroll(line, 70);
300 300 }
301 301 }
302 302
303 303 // browse tree @ revision
304 304 $('#files_link').on('click', function(e){
305 305 window.location = '${h.url('files_home',repo_name=c.repo_name, revision=c.commit.raw_id, f_path='')}';
306 306 e.preventDefault();
307 307 });
308 308
309 309 // inject comments into their proper positions
310 310 var file_comments = $('.inline-comment-placeholder');
311 311 })
312 312 </script>
313 313
314 314 </%def>
@@ -1,71 +1,125 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('%s Commits') % c.repo_name} -
6 6 r${c.commit_ranges[0].revision}:${h.short_id(c.commit_ranges[0].raw_id)}
7 7 ...
8 8 r${c.commit_ranges[-1].revision}:${h.short_id(c.commit_ranges[-1].raw_id)}
9 9 ${ungettext('(%s commit)','(%s commits)', len(c.commit_ranges)) % len(c.commit_ranges)}
10 10 %if c.rhodecode_name:
11 11 &middot; ${h.branding(c.rhodecode_name)}
12 12 %endif
13 13 </%def>
14 14
15 15 <%def name="breadcrumbs_links()">
16 16 ${_('Commits')} -
17 17 r${c.commit_ranges[0].revision}:${h.short_id(c.commit_ranges[0].raw_id)}
18 18 ...
19 19 r${c.commit_ranges[-1].revision}:${h.short_id(c.commit_ranges[-1].raw_id)}
20 20 ${ungettext('(%s commit)','(%s commits)', len(c.commit_ranges)) % len(c.commit_ranges)}
21 21 </%def>
22 22
23 23 <%def name="menu_bar_nav()">
24 24 ${self.menu_items(active='repositories')}
25 25 </%def>
26 26
27 27 <%def name="menu_bar_subnav()">
28 28 ${self.repo_menu(active='changelog')}
29 29 </%def>
30 30
31 31 <%def name="main()">
32 32 <div class="summary-header">
33 33 <div class="title">
34 <div class="title-content">
35 34 ${self.repo_page_title(c.rhodecode_db_repo)}
36 35 </div>
37 36 </div>
37
38
39 <div class="summary changeset">
40 <div class="summary-detail">
41 <div class="summary-detail-header">
42 <span class="breadcrumbs files_location">
43 <h4>
44 ${_('Commit Range')}
45 <code>
46 r${c.commit_ranges[0].revision}:${h.short_id(c.commit_ranges[0].raw_id)}...r${c.commit_ranges[-1].revision}:${h.short_id(c.commit_ranges[-1].raw_id)}
47 </code>
48 </h4>
49 </span>
50 </div>
51
52 <div class="fieldset">
53 <div class="left-label">
54 ${_('Diff option')}:
55 </div>
56 <div class="right-content">
38 57 <div class="header-buttons">
39 <a href="${h.url('compare_url', repo_name=c.repo_name, source_ref_type='rev', source_ref=getattr(c.commit_ranges[0].parents[0] if c.commit_ranges[0].parents else h.EmptyCommit(), 'raw_id'), target_ref_type='rev', target_ref=c.commit_ranges[-1].raw_id)}"
40 class="btn btn-default">
58 <a href="${h.url('compare_url', repo_name=c.repo_name, source_ref_type='rev', source_ref=getattr(c.commit_ranges[0].parents[0] if c.commit_ranges[0].parents else h.EmptyCommit(), 'raw_id'), target_ref_type='rev', target_ref=c.commit_ranges[-1].raw_id)}">
41 59 ${_('Show combined compare')}
42 60 </a>
43 61 </div>
44 62 </div>
63 </div>
45 64
46 <div class="summary-detail">
47 <div class="title">
48 <h2>
49 ${self.breadcrumbs_links()}
50 </h2>
65 <%doc>
66 ##TODO(marcink): implement this and diff menus
67 <div class="fieldset">
68 <div class="left-label">
69 ${_('Diff options')}:
70 </div>
71 <div class="right-content">
72 <div class="diff-actions">
73 <a href="${h.url('changeset_raw_home',repo_name=c.repo_name,revision='?')}" class="tooltip" title="${h.tooltip(_('Raw diff'))}">
74 ${_('Raw Diff')}
75 </a>
76 |
77 <a href="${h.url('changeset_patch_home',repo_name=c.repo_name,revision='?')}" class="tooltip" title="${h.tooltip(_('Patch diff'))}">
78 ${_('Patch Diff')}
79 </a>
80 |
81 <a href="${h.url('changeset_download_home',repo_name=c.repo_name,revision='?',diff='download')}" class="tooltip" title="${h.tooltip(_('Download diff'))}">
82 ${_('Download Diff')}
83 </a>
84 </div>
51 85 </div>
52 86 </div>
87 </%doc>
88 </div> <!-- end summary-detail -->
89
90 </div> <!-- end summary -->
91
53 92 <div id="changeset_compare_view_content">
54 ##CS
93 <div class="pull-left">
94 <div class="btn-group">
95 <a
96 class="btn"
97 href="#"
98 onclick="$('.compare_select').show();$('.compare_select_hidden').hide(); return false">
99 ${ungettext('Expand %s commit','Expand %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
100 </a>
101 <a
102 class="btn"
103 href="#"
104 onclick="$('.compare_select').hide();$('.compare_select_hidden').show(); return false">
105 ${ungettext('Collapse %s commit','Collapse %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
106 </a>
107 </div>
108 </div>
109 ## Commit range generated below
55 110 <%include file="../compare/compare_commits.html"/>
56 111 <div class="cs_files">
57 112 <%namespace name="cbdiffs" file="/codeblocks/diffs.html"/>
58 113 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
59 114 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
60 115 ${cbdiffs.render_diffset_menu()}
61 116 %for commit in c.commit_ranges:
62 117 ${cbdiffs.render_diffset(
63 118 diffset=c.changes[commit.raw_id],
64 119 collapse_when_files_over=5,
65 120 commit=commit,
66 121 )}
67 122 %endfor
68 </table>
69 123 </div>
70 124 </div>
71 125 </%def>
@@ -1,103 +1,64 b''
1 1 ## -*- coding: utf-8 -*-
2 2 ##usage:
3 3 ## <%namespace name="diff_block" file="/changeset/diff_block.html"/>
4 4 ## ${diff_block.diff_block_changeset_table(change)}
5 5 ##
6 6 <%def name="changeset_message()">
7 7 <h5>${_('The requested commit is too big and content was truncated.')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a></h5>
8 8 </%def>
9 9 <%def name="file_message()">
10 10 <h5>${_('The requested file is too big and its content is not shown.')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a></h5>
11 11 </%def>
12 12
13 13 <%def name="diff_block_changeset_table(change)">
14 14 <div class="diff-container" id="${'diff-container-%s' % (id(change))}">
15 15 %for FID,(cs1, cs2, change, filenode_path, diff, stats, file) in change.iteritems():
16 16 <div id="${h.FID('',filenode_path)}_target" ></div>
17 17 <div id="${h.FID('',filenode_path)}" class="diffblock margined comm">
18 18 <div class="code-body">
19 19 <div class="full_f_path" path="${h.safe_unicode(filenode_path)}" style="display: none"></div>
20 20 ${diff|n}
21 21 % if file["is_limited_diff"]:
22 22 % if file["exceeds_limit"]:
23 23 ${self.file_message()}
24 24 % else:
25 25 <h5>${_('Diff was truncated. File content available only in full diff.')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a></h5>
26 26 % endif
27 27 % endif
28 28 </div>
29 29 </div>
30 30 %endfor
31 31 </div>
32 32 </%def>
33 33
34 34 <%def name="diff_block_simple(change)">
35 35 <div class="diff-container" id="${'diff-container-%s' % (id(change))}">
36 36 %for op,filenode_path,diff,file in change:
37 37 <div id="${h.FID('',filenode_path)}_target" ></div>
38 38 <div id="${h.FID('',filenode_path)}" class="diffblock margined comm" >
39 39 <div class="code-body">
40 40 <div class="full_f_path" path="${h.safe_unicode(filenode_path)}" style="display: none;"></div>
41 41 ${diff|n}
42 42 % if file["is_limited_diff"]:
43 43 % if file["exceeds_limit"]:
44 44 ${self.file_message()}
45 45 % else:
46 46 <h5>${_('Diff was truncated. File content available only in full diff.')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a></h5>
47 47 % endif
48 48 % endif
49 49 </div>
50 50 </div>
51 51 %endfor
52 52 </div>
53 53 </%def>
54 54
55 <%def name="diff_menu(repo_name, f_path, cs1, cs2, change, file=None)">
56 <%
57 onclick_diff2way = ''
58 if (file and file["exceeds_limit"]):
59 onclick_diff2way = '''return confirm('%s');''' % _("Showing a big diff might take some time and resources, continue?")
60 %>
61
62 % if change in ['A', 'M']:
63 <a href="${h.url('files_home',repo_name=repo_name,f_path=f_path,revision=cs2)}"
64 class="tooltip" title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': cs2[:12]})}">
65 ${_('Show File')}
66 </a>
67 % else:
68 <span
69 class="tooltip" title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': cs2[:12]})}">
70 ${_('Show File')}
71 </span>
72 % endif
73 |
74 <a href="${h.url('files_diff_home',repo_name=repo_name,f_path=f_path,diff2=cs2,diff1=cs1,diff='diff',fulldiff=1)}"
75 class="tooltip" title="${h.tooltip(_('Show full diff for this file'))}">
76 ${_('Unified Diff')}
77 </a>
78 |
79 <a href="${h.url('files_diff_2way_home',repo_name=repo_name,f_path=f_path,diff2=cs2,diff1=cs1,diff='diff',fulldiff=1)}"
80 class="tooltip" title="${h.tooltip(_('Show full side-by-side diff for this file'))}"} onclick="${onclick_diff2way}">
81 ${_('Side-by-side Diff')}
82 </a>
83 |
84 <a href="${h.url('files_diff_home',repo_name=repo_name,f_path=f_path,diff2=cs2,diff1=cs1,diff='raw')}"
85 class="tooltip" title="${h.tooltip(_('Raw diff'))}">
86 ${_('Raw Diff')}
87 </a>
88 |
89 <a href="${h.url('files_diff_home',repo_name=repo_name,f_path=f_path,diff2=cs2,diff1=cs1,diff='download')}"
90 class="tooltip" title="${h.tooltip(_('Download diff'))}">
91 ${_('Download Diff')}
92 </a>
93 </%def>
94 55
95 56 <%def name="diff_summary_text(changed_files, lines_added, lines_deleted, limited_diff=False)">
96 57 % if limited_diff:
97 58 ${ungettext('%(num)s file changed', '%(num)s files changed', changed_files) % {'num': changed_files}}
98 59 % else:
99 60 ${ungettext('%(num)s file changed: %(linesadd)s inserted, ''%(linesdel)s deleted',
100 61 '%(num)s files changed: %(linesadd)s inserted, %(linesdel)s deleted', changed_files) % {'num': changed_files, 'linesadd': lines_added, 'linesdel': lines_deleted}}
101 62 %endif
102 63 </%def>
103 64
@@ -1,577 +1,581 b''
1 1 <%def name="diff_line_anchor(filename, line, type)"><%
2 2 return '%s_%s_%i' % (h.safeid(filename), type, line)
3 3 %></%def>
4 4
5 5 <%def name="action_class(action)"><%
6 6 return {
7 7 '-': 'cb-deletion',
8 8 '+': 'cb-addition',
9 9 ' ': 'cb-context',
10 10 }.get(action, 'cb-empty')
11 11 %></%def>
12 12
13 13 <%def name="op_class(op_id)"><%
14 14 return {
15 15 DEL_FILENODE: 'deletion', # file deleted
16 16 BIN_FILENODE: 'warning' # binary diff hidden
17 17 }.get(op_id, 'addition')
18 18 %></%def>
19 19
20 20 <%def name="link_for(**kw)"><%
21 21 new_args = request.GET.mixed()
22 22 new_args.update(kw)
23 23 return h.url('', **new_args)
24 24 %></%def>
25 25
26 26 <%def name="render_diffset(diffset, commit=None,
27 27
28 28 # collapse all file diff entries when there are more than this amount of files in the diff
29 29 collapse_when_files_over=20,
30 30
31 31 # collapse lines in the diff when more than this amount of lines changed in the file diff
32 32 lines_changed_limit=500,
33 33
34 34 # add a ruler at to the output
35 35 ruler_at_chars=0,
36 36
37 37 # show inline comments
38 38 use_comments=False,
39 39
40 40 # disable new comments
41 41 disable_new_comments=False,
42 42
43 43 )">
44 44
45 45 %if use_comments:
46 46 <div id="cb-comments-inline-container-template" class="js-template">
47 47 ${inline_comments_container([])}
48 48 </div>
49 49 <div class="js-template" id="cb-comment-inline-form-template">
50 50 <div class="comment-inline-form ac">
51 51 %if c.rhodecode_user.username != h.DEFAULT_USER:
52 52 ${h.form('#', method='get')}
53 53 <div id="edit-container_{1}" class="clearfix">
54 54 <div class="comment-title pull-left">
55 55 ${_('Create a comment on line {1}.')}
56 56 </div>
57 57 <div class="comment-help pull-right">
58 58 ${(_('Comments parsed using %s syntax with %s support.') % (
59 59 ('<a href="%s">%s</a>' % (h.url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
60 60 ('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user'))
61 61 )
62 62 )|n
63 63 }
64 64 </div>
65 65 <div style="clear: both"></div>
66 66 <textarea id="text_{1}" name="text" class="comment-block-ta ac-input"></textarea>
67 67 </div>
68 68 <div id="preview-container_{1}" class="clearfix" style="display: none;">
69 69 <div class="comment-help">
70 70 ${_('Comment preview')}
71 71 </div>
72 72 <div id="preview-box_{1}" class="preview-box"></div>
73 73 </div>
74 74 <div class="comment-footer">
75 75 <div class="action-buttons">
76 76 <input type="hidden" name="f_path" value="{0}">
77 77 <input type="hidden" name="line" value="{1}">
78 78 <button id="preview-btn_{1}" class="btn btn-secondary">${_('Preview')}</button>
79 79 <button id="edit-btn_{1}" class="btn btn-secondary" style="display: none;">${_('Edit')}</button>
80 80 ${h.submit('save', _('Comment'), class_='btn btn-success save-inline-form')}
81 81 </div>
82 82 <div class="comment-button">
83 83 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
84 84 ${_('Cancel')}
85 85 </button>
86 86 </div>
87 87 ${h.end_form()}
88 88 </div>
89 89 %else:
90 90 ${h.form('', class_='inline-form comment-form-login', method='get')}
91 91 <div class="pull-left">
92 92 <div class="comment-help pull-right">
93 93 ${_('You need to be logged in to comment.')} <a href="${h.route_path('login', _query={'came_from': h.url.current()})}">${_('Login now')}</a>
94 94 </div>
95 95 </div>
96 96 <div class="comment-button pull-right">
97 97 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
98 98 ${_('Cancel')}
99 99 </button>
100 100 </div>
101 101 <div class="clearfix"></div>
102 102 ${h.end_form()}
103 103 %endif
104 104 </div>
105 105 </div>
106 106
107 107 %endif
108 108 <%
109 109 collapse_all = len(diffset.files) > collapse_when_files_over
110 110 %>
111 111
112 112 %if c.diffmode == 'sideside':
113 113 <style>
114 114 .wrapper {
115 115 max-width: 1600px !important;
116 116 }
117 117 </style>
118 118 %endif
119 119 %if ruler_at_chars:
120 120 <style>
121 121 .diff table.cb .cb-content:after {
122 122 content: "";
123 123 border-left: 1px solid blue;
124 124 position: absolute;
125 125 top: 0;
126 126 height: 18px;
127 127 opacity: .2;
128 128 z-index: 10;
129 129 ## +5 to account for diff action (+/-)
130 130 left: ${ruler_at_chars + 5}ch;
131 131 </style>
132 132 %endif
133 133 <div class="diffset ${disable_new_comments and 'diffset-comments-disabled'}">
134 134 <div class="diffset-heading ${diffset.limited_diff and 'diffset-heading-warning' or ''}">
135 135 %if commit:
136 136 <div class="pull-right">
137 137 <a class="btn tooltip" title="${_('Browse Files at revision {}').format(commit.raw_id)}" href="${h.url('files_home',repo_name=diffset.repo_name, revision=commit.raw_id, f_path='')}">
138 138 ${_('Browse Files')}
139 139 </a>
140 140 </div>
141 141 %endif
142 142 <h2 class="clearinner">
143 143 %if commit:
144 144 <a class="tooltip revision" title="${h.tooltip(commit.message)}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id)}">${'r%s:%s' % (commit.revision,h.short_id(commit.raw_id))}</a> -
145 145 ${h.age_component(commit.date)} -
146 146 %endif
147 147 %if diffset.limited_diff:
148 148 ${_('The requested commit is too big and content was truncated.')}
149 149
150 150 ${ungettext('%(num)s file changed.', '%(num)s files changed.', diffset.changed_files) % {'num': diffset.changed_files}}
151 151 <a href="${link_for(fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
152 152 %else:
153 153 ${ungettext('%(num)s file changed: %(linesadd)s inserted, ''%(linesdel)s deleted',
154 154 '%(num)s files changed: %(linesadd)s inserted, %(linesdel)s deleted', diffset.changed_files) % {'num': diffset.changed_files, 'linesadd': diffset.lines_added, 'linesdel': diffset.lines_deleted}}
155 155 %endif
156 156 </h2>
157 157 </div>
158 158
159 159 %if not diffset.files:
160 160 <p class="empty_data">${_('No files')}</p>
161 161 %endif
162 162
163 163 <div class="filediffs">
164 164 %for i, filediff in enumerate(diffset.files):
165
165 166 <%
166 167 lines_changed = filediff['patch']['stats']['added'] + filediff['patch']['stats']['deleted']
167 168 over_lines_changed_limit = lines_changed > lines_changed_limit
168 169 %>
169 170 <input ${collapse_all and 'checked' or ''} class="filediff-collapse-state" id="filediff-collapse-${id(filediff)}" type="checkbox">
170 171 <div
171 172 class="filediff"
172 173 data-f-path="${filediff['patch']['filename']}"
173 174 id="a_${h.FID('', filediff['patch']['filename'])}">
174 175 <label for="filediff-collapse-${id(filediff)}" class="filediff-heading">
175 176 <div class="filediff-collapse-indicator"></div>
176 177 ${diff_ops(filediff)}
177 178 </label>
178 179 ${diff_menu(filediff, use_comments=use_comments)}
179 180 <table class="cb cb-diff-${c.diffmode} code-highlight ${over_lines_changed_limit and 'cb-collapsed' or ''}">
180 181 %if not filediff.hunks:
181 182 %for op_id, op_text in filediff['patch']['stats']['ops'].items():
182 183 <tr>
183 184 <td class="cb-text cb-${op_class(op_id)}" ${c.diffmode == 'unified' and 'colspan=3' or 'colspan=4'}>
184 185 %if op_id == DEL_FILENODE:
185 186 ${_('File was deleted')}
186 187 %elif op_id == BIN_FILENODE:
187 188 ${_('Binary file hidden')}
188 189 %else:
189 190 ${op_text}
190 191 %endif
191 192 </td>
192 193 </tr>
193 194 %endfor
194 195 %endif
195 196 %if over_lines_changed_limit:
196 197 <tr class="cb-warning cb-collapser">
197 198 <td class="cb-text" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=6'}>
198 199 ${_('This diff has been collapsed as it changes many lines, (%i lines changed)' % lines_changed)}
199 200 <a href="#" class="cb-expand"
200 201 onclick="$(this).closest('table').removeClass('cb-collapsed'); return false;">${_('Show them')}
201 202 </a>
202 203 <a href="#" class="cb-collapse"
203 204 onclick="$(this).closest('table').addClass('cb-collapsed'); return false;">${_('Hide them')}
204 205 </a>
205 206 </td>
206 207 </tr>
207 208 %endif
208 209 %if filediff.patch['is_limited_diff']:
209 210 <tr class="cb-warning cb-collapser">
210 211 <td class="cb-text" ${c.diffmode == 'unified' and 'colspan=4' or 'colspan=6'}>
211 212 ${_('The requested commit is too big and content was truncated.')} <a href="${link_for(fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
212 213 </td>
213 214 </tr>
214 215 %endif
215 216 %for hunk in filediff.hunks:
216 217 <tr class="cb-hunk">
217 218 <td ${c.diffmode == 'unified' and 'colspan=3' or ''}>
218 219 ## TODO: dan: add ajax loading of more context here
219 220 ## <a href="#">
220 221 <i class="icon-more"></i>
221 222 ## </a>
222 223 </td>
223 224 <td ${c.diffmode == 'sideside' and 'colspan=5' or ''}>
224 225 @@
225 226 -${hunk.source_start},${hunk.source_length}
226 227 +${hunk.target_start},${hunk.target_length}
227 228 ${hunk.section_header}
228 229 </td>
229 230 </tr>
230 231 %if c.diffmode == 'unified':
231 232 ${render_hunk_lines_unified(hunk, use_comments=use_comments)}
232 233 %elif c.diffmode == 'sideside':
233 234 ${render_hunk_lines_sideside(hunk, use_comments=use_comments)}
234 235 %else:
235 236 <tr class="cb-line">
236 237 <td>unknown diff mode</td>
237 238 </tr>
238 239 %endif
239 240 %endfor
240 241 </table>
241 242 </div>
242 243 %endfor
243 244 </div>
244 245 </div>
245 246 </%def>
246 247
247 248 <%def name="diff_ops(filediff)">
248 249 <%
249 250 stats = filediff['patch']['stats']
250 251 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
251 252 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE
252 253 %>
253 254 <span class="pill">
254 255 %if filediff.source_file_path and filediff.target_file_path:
255 256 %if filediff.source_file_path != filediff.target_file_path: # file was renamed
256 257 <strong>${filediff.target_file_path}</strong><del>${filediff.source_file_path}</del>
257 258 %else:
258 259 ## file was modified
259 260 <strong>${filediff.source_file_path}</strong>
260 261 %endif
261 262 %else:
262 263 %if filediff.source_file_path:
263 264 ## file was deleted
264 265 <strong>${filediff.source_file_path}</strong>
265 266 %else:
266 267 ## file was added
267 268 <strong>${filediff.target_file_path}</strong>
268 269 %endif
269 270 %endif
270 271 </span>
271 272 <span class="pill-group" style="float: left">
272 273 %if filediff.patch['is_limited_diff']:
273 274 <span class="pill tooltip" op="limited" title="The stats for this diff are not complete">limited diff</span>
274 275 %endif
275 276 %if RENAMED_FILENODE in stats['ops']:
276 277 <span class="pill" op="renamed">renamed</span>
277 278 %endif
278 279
279 280 %if NEW_FILENODE in stats['ops']:
280 281 <span class="pill" op="created">created</span>
281 282 %if filediff['target_mode'].startswith('120'):
282 283 <span class="pill" op="symlink">symlink</span>
283 284 %else:
284 285 <span class="pill" op="mode">${nice_mode(filediff['target_mode'])}</span>
285 286 %endif
286 287 %endif
287 288
288 289 %if DEL_FILENODE in stats['ops']:
289 290 <span class="pill" op="removed">removed</span>
290 291 %endif
291 292
292 293 %if CHMOD_FILENODE in stats['ops']:
293 294 <span class="pill" op="mode">
294 295 ${nice_mode(filediff['source_mode'])} ➡ ${nice_mode(filediff['target_mode'])}
295 296 </span>
296 297 %endif
297 298 </span>
298 299
299 300 <a class="pill filediff-anchor" href="#a_${h.FID('', filediff.patch['filename'])}"></a>
300 301
301 302 <span class="pill-group" style="float: right">
302 303 %if BIN_FILENODE in stats['ops']:
303 304 <span class="pill" op="binary">binary</span>
304 305 %if MOD_FILENODE in stats['ops']:
305 306 <span class="pill" op="modified">modified</span>
306 307 %endif
307 308 %endif
308 309 %if stats['added']:
309 310 <span class="pill" op="added">+${stats['added']}</span>
310 311 %endif
311 312 %if stats['deleted']:
312 313 <span class="pill" op="deleted">-${stats['deleted']}</span>
313 314 %endif
314 315 </span>
315 316
316 317 </%def>
317 318
318 319 <%def name="nice_mode(filemode)">
319 320 ${filemode.startswith('100') and filemode[3:] or filemode}
320 321 </%def>
321 322
322 323 <%def name="diff_menu(filediff, use_comments=False)">
323 324 <div class="filediff-menu">
324 325 %if filediff.diffset.source_ref:
325 326 %if filediff.patch['operation'] in ['D', 'M']:
326 327 <a
327 328 class="tooltip"
328 329 href="${h.url('files_home',repo_name=filediff.diffset.repo_name,f_path=filediff.source_file_path,revision=filediff.diffset.source_ref)}"
329 330 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
330 331 >
331 332 ${_('Show file before')}
332 333 </a>
333 334 %else:
334 335 <span
335 336 class="tooltip"
336 337 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.source_ref[:12]})}"
337 338 >
338 339 ${_('Show file before')}
339 340 </span>
340 341 %endif
341 342 %if filediff.patch['operation'] in ['A', 'M']:
342 343 <a
343 344 class="tooltip"
344 345 href="${h.url('files_home',repo_name=filediff.diffset.source_repo_name,f_path=filediff.target_file_path,revision=filediff.diffset.target_ref)}"
345 346 title="${h.tooltip(_('Show file at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
346 347 >
347 348 ${_('Show file after')}
348 349 </a>
349 350 %else:
350 351 <span
351 352 class="tooltip"
352 353 title="${h.tooltip(_('File no longer present at commit: %(commit_id)s') % {'commit_id': filediff.diffset.target_ref[:12]})}"
353 354 >
354 355 ${_('Show file after')}
355 356 </span>
356 357 %endif
357 358 <a
358 359 class="tooltip"
359 360 title="${h.tooltip(_('Raw diff'))}"
360 361 href="${h.url('files_diff_home',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path,diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='raw')}"
361 362 >
362 363 ${_('Raw diff')}
363 364 </a>
364 365 <a
365 366 class="tooltip"
366 367 title="${h.tooltip(_('Download diff'))}"
367 368 href="${h.url('files_diff_home',repo_name=filediff.diffset.repo_name,f_path=filediff.target_file_path,diff2=filediff.diffset.target_ref,diff1=filediff.diffset.source_ref,diff='download')}"
368 369 >
369 370 ${_('Download diff')}
370 371 </a>
371 372
372 373 ## TODO: dan: refactor ignorews_url and context_url into the diff renderer same as diffmode=unified/sideside. Also use ajax to load more context (by clicking hunks)
373 374 %if hasattr(c, 'ignorews_url'):
374 375 ${c.ignorews_url(request.GET, h.FID('', filediff['patch']['filename']))}
375 376 %endif
376 377 %if hasattr(c, 'context_url'):
377 378 ${c.context_url(request.GET, h.FID('', filediff['patch']['filename']))}
378 379 %endif
379 380
380 381
381 382 %if use_comments:
382 383 <a href="#" onclick="return Rhodecode.comments.toggleComments(this);">
383 384 <span class="show-comment-button">${_('Show comments')}</span><span class="hide-comment-button">${_('Hide comments')}</span>
384 385 </a>
385 386 %endif
386 387 %endif
387 388 </div>
388 389 </%def>
389 390
390 391
391 392 <%namespace name="commentblock" file="/changeset/changeset_file_comment.html"/>
392 393 <%def name="inline_comments_container(comments)">
393 394 <div class="inline-comments">
394 395 %for comment in comments:
395 396 ${commentblock.comment_block(comment, inline=True)}
396 397 %endfor
397 398
398 399 <span onclick="return Rhodecode.comments.createComment(this)"
399 400 class="btn btn-secondary cb-comment-add-button ${'comment-outdated' if comments and comments[-1].outdated else ''}"
400 401 style="${'display: none;' if comments and comments[-1].outdated else ''}">
401 402 ${_('Add another comment')}
402 403 </span>
403 404
404 405 </div>
405 406 </%def>
406 407
407 408
408 409 <%def name="render_hunk_lines_sideside(hunk, use_comments=False)">
409 410 %for i, line in enumerate(hunk.sideside):
410 411 <%
411 412 old_line_anchor, new_line_anchor = None, None
412 413 if line.original.lineno:
413 414 old_line_anchor = diff_line_anchor(hunk.filediff.source_file_path, line.original.lineno, 'o')
414 415 if line.modified.lineno:
415 416 new_line_anchor = diff_line_anchor(hunk.filediff.target_file_path, line.modified.lineno, 'n')
416 417 %>
418
417 419 <tr class="cb-line">
418 420 <td class="cb-data ${action_class(line.original.action)}"
419 421 data-line-number="${line.original.lineno}"
420 422 >
421 423 <div>
422 424 %if line.original.comments:
423 425 <i class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
424 426 %endif
425 427 </div>
426 428 </td>
427 429 <td class="cb-lineno ${action_class(line.original.action)}"
428 430 data-line-number="${line.original.lineno}"
429 431 %if old_line_anchor:
430 432 id="${old_line_anchor}"
431 433 %endif
432 434 >
433 435 %if line.original.lineno:
434 436 <a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a>
435 437 %endif
436 438 </td>
437 439 <td class="cb-content ${action_class(line.original.action)}"
438 440 data-line-number="o${line.original.lineno}"
439 441 >
440 442 %if use_comments and line.original.lineno:
441 443 ${render_add_comment_button()}
442 444 %endif
443 445 <span class="cb-code">${line.original.action} ${line.original.content or '' | n}</span>
444 446 %if use_comments and line.original.lineno and line.original.comments:
445 447 ${inline_comments_container(line.original.comments)}
446 448 %endif
447 449 </td>
448 450 <td class="cb-data ${action_class(line.modified.action)}"
449 451 data-line-number="${line.modified.lineno}"
450 452 >
451 453 <div>
452 454 %if line.modified.comments:
453 455 <i class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
454 456 %endif
455 457 </div>
456 458 </td>
457 459 <td class="cb-lineno ${action_class(line.modified.action)}"
458 460 data-line-number="${line.modified.lineno}"
459 461 %if new_line_anchor:
460 462 id="${new_line_anchor}"
461 463 %endif
462 464 >
463 465 %if line.modified.lineno:
464 466 <a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a>
465 467 %endif
466 468 </td>
467 469 <td class="cb-content ${action_class(line.modified.action)}"
468 470 data-line-number="n${line.modified.lineno}"
469 471 >
470 472 %if use_comments and line.modified.lineno:
471 473 ${render_add_comment_button()}
472 474 %endif
473 475 <span class="cb-code">${line.modified.action} ${line.modified.content or '' | n}</span>
474 476 %if use_comments and line.modified.lineno and line.modified.comments:
475 477 ${inline_comments_container(line.modified.comments)}
476 478 %endif
477 479 </td>
478 480 </tr>
479 481 %endfor
480 482 </%def>
481 483
482 484
483 485 <%def name="render_hunk_lines_unified(hunk, use_comments=False)">
484 486 %for old_line_no, new_line_no, action, content, comments in hunk.unified:
485 487 <%
486 488 old_line_anchor, new_line_anchor = None, None
487 489 if old_line_no:
488 490 old_line_anchor = diff_line_anchor(hunk.filediff.source_file_path, old_line_no, 'o')
489 491 if new_line_no:
490 492 new_line_anchor = diff_line_anchor(hunk.filediff.target_file_path, new_line_no, 'n')
491 493 %>
492 494 <tr class="cb-line">
493 495 <td class="cb-data ${action_class(action)}">
494 496 <div>
495 497 %if comments:
496 498 <i class="icon-comment" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
497 499 %endif
498 500 </div>
499 501 </td>
500 502 <td class="cb-lineno ${action_class(action)}"
501 503 data-line-number="${old_line_no}"
502 504 %if old_line_anchor:
503 505 id="${old_line_anchor}"
504 506 %endif
505 507 >
506 508 %if old_line_anchor:
507 509 <a name="${old_line_anchor}" href="#${old_line_anchor}">${old_line_no}</a>
508 510 %endif
509 511 </td>
510 512 <td class="cb-lineno ${action_class(action)}"
511 513 data-line-number="${new_line_no}"
512 514 %if new_line_anchor:
513 515 id="${new_line_anchor}"
514 516 %endif
515 517 >
516 518 %if new_line_anchor:
517 519 <a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a>
518 520 %endif
519 521 </td>
520 522 <td class="cb-content ${action_class(action)}"
521 523 data-line-number="${new_line_no and 'n' or 'o'}${new_line_no or old_line_no}"
522 524 >
523 525 %if use_comments:
524 526 ${render_add_comment_button()}
525 527 %endif
526 528 <span class="cb-code">${action} ${content or '' | n}</span>
527 529 %if use_comments and comments:
528 530 ${inline_comments_container(comments)}
529 531 %endif
530 532 </td>
531 533 </tr>
532 534 %endfor
533 535 </%def>
534 536
535 537 <%def name="render_add_comment_button()">
536 538 <button
537 539 class="btn btn-small btn-primary cb-comment-box-opener"
538 540 onclick="return Rhodecode.comments.createComment(this)"
539 541 ><span>+</span></button>
540 542 </%def>
541 543
542 544 <%def name="render_diffset_menu()">
543 545
544 546 <div class="diffset-menu clearinner">
545 547 <div class="pull-right">
546 548 <div class="btn-group">
549
547 550 <a
548 551 class="btn ${c.diffmode == 'sideside' and 'btn-primary'} tooltip"
549 552 title="${_('View side by side')}"
550 553 href="${h.url_replace(diffmode='sideside')}">
551 554 <span>${_('Side by Side')}</span>
552 555 </a>
553 556 <a
554 557 class="btn ${c.diffmode == 'unified' and 'btn-primary'} tooltip"
555 558 title="${_('View unified')}" href="${h.url_replace(diffmode='unified')}">
556 559 <span>${_('Unified')}</span>
557 560 </a>
558 561 </div>
559 562 </div>
563
560 564 <div class="pull-left">
561 565 <div class="btn-group">
562 566 <a
563 567 class="btn"
564 568 href="#"
565 onclick="$('input[class=filediff-collapse-state]').prop('checked', false); return false">${_('Expand All')}</a>
569 onclick="$('input[class=filediff-collapse-state]').prop('checked', false); return false">${_('Expand All Files')}</a>
566 570 <a
567 571 class="btn"
568 572 href="#"
569 onclick="$('input[class=filediff-collapse-state]').prop('checked', true); return false">${_('Collapse All')}</a>
573 onclick="$('input[class=filediff-collapse-state]').prop('checked', true); return false">${_('Collapse All Files')}</a>
570 574 <a
571 575 class="btn"
572 576 href="#"
573 onclick="return Rhodecode.comments.toggleWideMode(this)">${_('Wide Mode')}</a>
577 onclick="return Rhodecode.comments.toggleWideMode(this)">${_('Wide Mode Diff')}</a>
574 578 </div>
575 579 </div>
576 580 </div>
577 581 </%def>
@@ -1,104 +1,113 b''
1 1 ## Changesets table !
2 2 <%namespace name="base" file="/base/base.html"/>
3 <div class="container">
4 %if not c.commit_ranges:
5 <p class="empty_data">${_('No Commits')}</p>
6 %else:
7 3
8 4 %if c.ancestor:
9 <p class="ancestor">${_('Common Ancestor Commit')}:
5 <div class="ancestor">${_('Common Ancestor Commit')}:
10 6 <a href="${h.url('changeset_home',
11 7 repo_name=c.repo_name,
12 8 revision=c.ancestor)}">
13 9 ${h.short_id(c.ancestor)}
14 10 </a>
15 </p>
11 </div>
16 12 %endif
17 13
14 <div class="container">
18 15 <input type="hidden" name="__start__" value="revisions:sequence">
19 16 <table class="rctable compare_view_commits">
20 17 <tr>
21 18 <th>${_('Time')}</th>
22 19 <th>${_('Author')}</th>
23 20 <th>${_('Commit')}</th>
24 21 <th></th>
25 22 <th>${_('Description')}</th>
26 23 </tr>
27 24 %for commit in c.commit_ranges:
28 25 <tr id="row-${commit.raw_id}"
29 26 commit_id="${commit.raw_id}"
30 27 class="compare_select"
31 28 >
32 29 <td class="td-time">
33 30 ${h.age_component(commit.date)}
34 31 </td>
35 32 <td class="td-user">
36 33 ${base.gravatar_with_user(commit.author, 16)}
37 34 </td>
38 35 <td class="td-hash">
39 36 <code>
40 37 <a href="${h.url('changeset_home',
41 38 repo_name=c.target_repo.repo_name,
42 39 revision=commit.raw_id)}">
43 40 r${commit.revision}:${h.short_id(commit.raw_id)}
44 41 </a>
45 42 ${h.hidden('revisions',commit.raw_id)}
46 43 </code>
47 44 </td>
48 45 <td class="expand_commit"
49 46 data-commit-id="${commit.raw_id}"
50 47 title="${_( 'Expand commit message')}"
51 48 >
52 49 <div class="show_more_col">
53 50 <i class="show_more"></i>
54 51 </div>
55 52 </td>
56 53 <td class="mid td-description">
57 54 <div class="log-container truncate-wrap">
58 55 <div
59 56 id="c-${commit.raw_id}"
60 57 class="message truncate"
61 58 data-message-raw="${commit.message}"
62 59 >
63 60 ${h.urlify_commit_message(commit.message, c.repo_name)}
64 61 </div>
65 62 </div>
66 63 </td>
67 64 </tr>
68 65 %endfor
66 <tr class="compare_select_hidden" style="display: none">
67 <td colspan="5">
68 ${ungettext('%s commit hidden','%s commits hidden', len(c.commit_ranges)) % len(c.commit_ranges)}
69 </td>
70 </tr>
71 % if not c.commit_ranges:
72 <tr class="compare_select">
73 <td colspan="5">
74 ${_('No commits in this compare')}
75 </td>
76 </tr>
77 % endif
69 78 </table>
70 79 <input type="hidden" name="__end__" value="revisions:sequence">
71 %endif
80
72 81 </div>
73 82
74 83 <script>
75 84 $('.expand_commit').on('click',function(e){
76 85 var target_expand = $(this);
77 86 var cid = target_expand.data('commitId');
78 87
79 ## TODO: dan: extract styles into css, and just toggleClass('open') here
88 // ## TODO: dan: extract styles into css, and just toggleClass('open') here
80 89 if (target_expand.hasClass('open')){
81 90 $('#c-'+cid).css({
82 91 'height': '1.5em',
83 92 'white-space': 'nowrap',
84 93 'text-overflow': 'ellipsis',
85 94 'overflow':'hidden'
86 95 });
87 96 target_expand.removeClass('open');
88 97 }
89 98 else {
90 99 $('#c-'+cid).css({
91 100 'height': 'auto',
92 101 'white-space': 'pre-line',
93 102 'text-overflow': 'initial',
94 103 'overflow':'visible'
95 104 });
96 105 target_expand.addClass('open');
97 106 }
98 107 });
99 108
100 109 $('.compare_select').on('click',function(e){
101 110 var cid = $(this).attr('commit_id');
102 111 $('#row-'+cid).toggleClass('hl', !$('#row-'+cid).hasClass('hl'));
103 112 });
104 113 </script>
@@ -1,259 +1,370 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3 <%namespace name="cbdiffs" file="/codeblocks/diffs.html"/>
4 4
5 5 <%def name="title()">
6 6 %if c.compare_home:
7 7 ${_('%s Compare') % c.repo_name}
8 8 %else:
9 9 ${_('%s Compare') % c.repo_name} - ${'%s@%s' % (c.source_repo.repo_name, c.source_ref)} &gt; ${'%s@%s' % (c.target_repo.repo_name, c.target_ref)}
10 10 %endif
11 11 %if c.rhodecode_name:
12 12 &middot; ${h.branding(c.rhodecode_name)}
13 13 %endif
14 14 </%def>
15 15
16 16 <%def name="breadcrumbs_links()">
17 17 ${ungettext('%s commit','%s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
18 18 </%def>
19 19
20 20 <%def name="menu_bar_nav()">
21 21 ${self.menu_items(active='repositories')}
22 22 </%def>
23 23
24 24 <%def name="menu_bar_subnav()">
25 25 ${self.repo_menu(active='compare')}
26 26 </%def>
27 27
28 28 <%def name="main()">
29 29 <script type="text/javascript">
30 30 // set fake commitId on this commit-range page
31 31 templateContext.commit_data.commit_id = "${h.EmptyCommit().raw_id}";
32 32 </script>
33 33
34 34 <div class="box">
35 35 <div class="title">
36 36 ${self.repo_page_title(c.rhodecode_db_repo)}
37 <div class="breadcrumbs">
38 ${_('Compare Commits')}
39 </div>
40 37 </div>
41 38
42 <div class="table">
43 <div id="codeblock" class="diffblock">
39 <div class="summary changeset">
40 <div class="summary-detail">
41 <div class="summary-detail-header">
42 <span class="breadcrumbs files_location">
43 <h4>
44 ${_('Compare Commits')}
45 % if c.file_path:
46 ${_('for file')} <a href="#${'a_' + h.FID('',c.file_path)}">${c.file_path}</a>
47 % endif
48
49 % if c.commit_ranges:
50 <code>
51 r${c.source_commit.revision}:${h.short_id(c.source_commit.raw_id)}...r${c.target_commit.revision}:${h.short_id(c.target_commit.raw_id)}
52 </code>
53 % endif
54 </h4>
55 </span>
56 </div>
57
58 <div class="fieldset">
59 <div class="left-label">
60 ${_('Target')}:
61 </div>
62 <div class="right-content">
63 <div>
44 64 <div class="code-header" >
45 65 <div class="compare_header">
46 66 ## The hidden elements are replaced with a select2 widget
47 <div class="compare-label">${_('Target')}</div>${h.hidden('compare_source')}
48 <div class="compare-label">${_('Source')}</div>${h.hidden('compare_target')}
67 ${h.hidden('compare_source')}
68 </div>
69 </div>
70 </div>
71 </div>
72 </div>
49 73
50 %if not c.preview_mode:
51 <div class="compare-label"></div>
74 <div class="fieldset">
75 <div class="left-label">
76 ${_('Source')}:
77 </div>
78 <div class="right-content">
79 <div>
80 <div class="code-header" >
81 <div class="compare_header">
82 ## The hidden elements are replaced with a select2 widget
83 ${h.hidden('compare_target')}
84 </div>
85 </div>
86 </div>
87 </div>
88 </div>
89
90 <div class="fieldset">
91 <div class="left-label">
92 ${_('Actions')}:
93 </div>
94 <div class="right-content">
95 <div>
96 <div class="code-header" >
97 <div class="compare_header">
98
52 99 <div class="compare-buttons">
53 %if not c.compare_home:
54 <a id="btn-swap" class="btn btn-primary" href="${c.swap_url}"><i class="icon-refresh"></i> ${_('Swap')}</a>
100 % if c.compare_home:
101 <a id="compare_revs" class="btn btn-primary"> ${_('Compare Commits')}</a>
102
103 <a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Swap')}</a>
104 <a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Comment')}</a>
105 <div id="changeset_compare_view_content">
106 <div class="help-block">${_('Compare commits, branches, bookmarks or tags.')}</div>
107 </div>
108
109 % elif c.preview_mode:
110 <a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Compare Commits')}</a>
111 <a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Swap')}</a>
112 <a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Comment')}</a>
113
114 % else:
115 <a id="compare_revs" class="btn btn-primary"> ${_('Compare Commits')}</a>
116 <a id="btn-swap" class="btn btn-primary" href="${c.swap_url}">${_('Swap')}</a>
117
118 ## allow comment only if there are commits to comment on
119 % if c.diffset and c.diffset.files and c.commit_ranges:
120 <a id="compare_changeset_status_toggle" class="btn btn-primary">${_('Comment')}</a>
121 % else:
122 <a class="btn disabled tooltip" disabled="disabled" title="${_('Action unavailable in current view')}">${_('Comment')}</a>
55 123 %endif
56 <div id="compare_revs" class="btn btn-primary"><i class ="icon-loop"></i> ${_('Compare Commits')}</div>
57 %if c.diffset and c.diffset.files:
58 <div id="compare_changeset_status_toggle" class="btn btn-primary">${_('Comment')}</div>
59 %endif
60 </div>
61 124 %endif
62 125 </div>
63 126 </div>
64 127 </div>
128 </div>
129 </div>
130 </div>
131
132 <%doc>
133 ##TODO(marcink): implement this and diff menus
134 <div class="fieldset">
135 <div class="left-label">
136 ${_('Diff options')}:
137 </div>
138 <div class="right-content">
139 <div class="diff-actions">
140 <a href="${h.url('changeset_raw_home',repo_name=c.repo_name,revision='?')}" class="tooltip" title="${h.tooltip(_('Raw diff'))}">
141 ${_('Raw Diff')}
142 </a>
143 |
144 <a href="${h.url('changeset_patch_home',repo_name=c.repo_name,revision='?')}" class="tooltip" title="${h.tooltip(_('Patch diff'))}">
145 ${_('Patch Diff')}
146 </a>
147 |
148 <a href="${h.url('changeset_download_home',repo_name=c.repo_name,revision='?',diff='download')}" class="tooltip" title="${h.tooltip(_('Download diff'))}">
149 ${_('Download Diff')}
150 </a>
151 </div>
152 </div>
153 </div>
154 </%doc>
155
156 </div> <!-- end summary-detail -->
157
158 </div> <!-- end summary -->
159
160
161 <div class="table">
162
65 163 ## use JS script to load it quickly before potentially large diffs render long time
66 164 ## this prevents from situation when large diffs block rendering of select2 fields
67 165 <script type="text/javascript">
68 166
69 167 var cache = {};
70 168
71 169 var formatSelection = function(repoName){
72 170 return function(data, container, escapeMarkup) {
73 171 var selection = data ? this.text(data) : "";
74 172 return escapeMarkup('{0}@{1}'.format(repoName, selection));
75 173 }
76 174 };
77 175
78 176 var feedCompareData = function(query, cachedValue){
79 177 var data = {results: []};
80 178 //filter results
81 179 $.each(cachedValue.results, function() {
82 180 var section = this.text;
83 181 var children = [];
84 182 $.each(this.children, function() {
85 183 if (query.term.length === 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
86 184 children.push({
87 185 'id': this.id,
88 186 'text': this.text,
89 187 'type': this.type
90 188 })
91 189 }
92 190 });
93 191 data.results.push({
94 192 'text': section,
95 193 'children': children
96 194 })
97 195 });
98 196 //push the typed in changeset
99 197 data.results.push({
100 198 'text': _gettext('specify commit'),
101 199 'children': [{
102 200 'id': query.term,
103 201 'text': query.term,
104 202 'type': 'rev'
105 203 }]
106 204 });
107 205 query.callback(data);
108 206 };
109 207
110 208 var loadCompareData = function(repoName, query, cache){
111 209 $.ajax({
112 210 url: pyroutes.url('repo_refs_data', {'repo_name': repoName}),
113 211 data: {},
114 212 dataType: 'json',
115 213 type: 'GET',
116 214 success: function(data) {
117 215 cache[repoName] = data;
118 216 query.callback({results: data.results});
119 217 }
120 218 })
121 219 };
122 220
123 221 var enable_fields = ${"false" if c.preview_mode else "true"};
124 222 $("#compare_source").select2({
125 223 placeholder: "${'%s@%s' % (c.source_repo.repo_name, c.source_ref)}",
126 224 containerCssClass: "drop-menu",
127 225 dropdownCssClass: "drop-menu-dropdown",
128 226 formatSelection: formatSelection("${c.source_repo.repo_name}"),
129 227 dropdownAutoWidth: true,
130 228 query: function(query) {
131 229 var repoName = '${c.source_repo.repo_name}';
132 230 var cachedValue = cache[repoName];
133 231
134 232 if (cachedValue){
135 233 feedCompareData(query, cachedValue);
136 234 }
137 235 else {
138 236 loadCompareData(repoName, query, cache);
139 237 }
140 238 }
141 239 }).select2("enable", enable_fields);
142 240
143 241 $("#compare_target").select2({
144 242 placeholder: "${'%s@%s' % (c.target_repo.repo_name, c.target_ref)}",
145 243 dropdownAutoWidth: true,
146 244 containerCssClass: "drop-menu",
147 245 dropdownCssClass: "drop-menu-dropdown",
148 246 formatSelection: formatSelection("${c.target_repo.repo_name}"),
149 247 query: function(query) {
150 248 var repoName = '${c.target_repo.repo_name}';
151 249 var cachedValue = cache[repoName];
152 250
153 251 if (cachedValue){
154 252 feedCompareData(query, cachedValue);
155 253 }
156 254 else {
157 255 loadCompareData(repoName, query, cache);
158 256 }
159 257 }
160 258 }).select2("enable", enable_fields);
161 259 var initial_compare_source = {id: "${c.source_ref}", type:"${c.source_ref_type}"};
162 260 var initial_compare_target = {id: "${c.target_ref}", type:"${c.target_ref_type}"};
163 261
164 262 $('#compare_revs').on('click', function(e) {
165 263 var source = $('#compare_source').select2('data') || initial_compare_source;
166 264 var target = $('#compare_target').select2('data') || initial_compare_target;
167 265 if (source && target) {
168 266 var url_data = {
169 267 repo_name: "${c.repo_name}",
170 268 source_ref: source.id,
171 269 source_ref_type: source.type,
172 270 target_ref: target.id,
173 271 target_ref_type: target.type
174 272 };
175 273 window.location = pyroutes.url('compare_url', url_data);
176 274 }
177 275 });
178 276 $('#compare_changeset_status_toggle').on('click', function(e) {
179 277 $('#compare_changeset_status').toggle();
180 278 });
181 279
182 280 </script>
183 281
184 282 ## changeset status form
185 283 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
186 284 ## main comment form and it status
187 285 <%
188 286 def revs(_revs):
189 287 form_inputs = []
190 288 for cs in _revs:
191 289 tmpl = '<input type="hidden" data-commit-id="%(cid)s" name="commit_ids" value="%(cid)s">' % {'cid': cs.raw_id}
192 290 form_inputs.append(tmpl)
193 291 return form_inputs
194 292 %>
195 293 <div id="compare_changeset_status" style="display: none;">
196 294 ${comment.comments(h.url('changeset_comment', repo_name=c.repo_name, revision='0'*16), None, is_compare=True, form_extras=revs(c.commit_ranges))}
197 295 <script type="text/javascript">
198 296
199 297 mainCommentForm.setHandleFormSubmit(function(o) {
200 298 var text = mainCommentForm.cm.getValue();
201 299 var status = mainCommentForm.getCommentStatus();
202 300
203 301 if (text === "" && !status) {
204 302 return;
205 303 }
206 304
207 305 // we can pick which commits we want to make the comment by
208 306 // selecting them via click on preview pane, this will alter the hidden inputs
209 307 var cherryPicked = $('#changeset_compare_view_content .compare_select.hl').length > 0;
210 308
211 309 var commitIds = [];
212 310 $('#changeset_compare_view_content .compare_select').each(function(el) {
213 311 var commitId = this.id.replace('row-', '');
214 312 if ($(this).hasClass('hl') || !cherryPicked) {
215 313 $("input[data-commit-id='{0}']".format(commitId)).val(commitId)
216 314 commitIds.push(commitId);
217 315 } else {
218 316 $("input[data-commit-id='{0}']".format(commitId)).val('')
219 317 }
220 318 });
221 319
222 320 mainCommentForm.setActionButtonsDisabled(true);
223 321 mainCommentForm.cm.setOption("readOnly", true);
224 322 var postData = {
225 323 'text': text,
226 324 'changeset_status': status,
227 325 'commit_ids': commitIds,
228 326 'csrf_token': CSRF_TOKEN
229 327 };
230 328
231 329 var submitSuccessCallback = function(o) {
232 330 location.reload(true);
233 331 };
234 332 var submitFailCallback = function(){
235 333 mainCommentForm.resetCommentFormState(text)
236 334 };
237 335 mainCommentForm.submitAjaxPOST(
238 336 mainCommentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
239 337 });
240 338 </script>
241 339
242 340 </div>
243 341
244 %if c.compare_home:
342 %if not c.compare_home:
245 343 <div id="changeset_compare_view_content">
246 <div class="help-block">${_('Compare commits, branches, bookmarks or tags.')}</div>
344 <div class="pull-left">
345 <div class="btn-group">
346 <a
347 class="btn"
348 href="#"
349 onclick="$('.compare_select').show();$('.compare_select_hidden').hide(); return false">
350 ${ungettext('Expand %s commit','Expand %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
351 </a>
352 <a
353 class="btn"
354 href="#"
355 onclick="$('.compare_select').hide();$('.compare_select_hidden').show(); return false">
356 ${ungettext('Collapse %s commit','Collapse %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
357 </a>
247 358 </div>
248 %else:
249 <div id="changeset_compare_view_content">
250 ##CS
359 </div>
360 <div style="padding:0 10px 10px 0px" class="pull-left"></div>
361 ## commit compare generated below
251 362 <%include file="compare_commits.html"/>
252 363 ${cbdiffs.render_diffset_menu()}
253 364 ${cbdiffs.render_diffset(c.diffset)}
254 365 </div>
255 366 %endif
256 367 </div>
257 368 </div>
258 369 </div>
259 370 </%def>
@@ -1,1162 +1,1160 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%namespace name="base" file="/base/base.html"/>
3 3 <%inherit file="/debug_style/index.html"/>
4 4
5 5 <%def name="breadcrumbs_links()">
6 6 ${h.link_to(_('Style'), h.url('debug_style_home'))}
7 7 &raquo;
8 8 ${c.active}
9 9 </%def>
10 10
11 11 <%def name="js_extra()">
12 <script type="text/javascript" src="${h.asset('js/mergerly.js', ver=c.rhodecode_version_hash)}"></script>
13 12 </%def>
14 13
15 14 <%def name="css_extra()">
16 <link rel="stylesheet" type="text/css" href="${h.asset('css/mergerly.css', ver=c.rhodecode_version_hash)}"/>
17 15 </%def>
18 16
19 17
20 18 <%def name="real_main()">
21 19 <div class="box">
22 20 <div class="title">
23 21 ${self.breadcrumbs()}
24 22 </div>
25 23
26 24 ##main
27 25 <div class='sidebar-col-wrapper'>
28 26 ${self.sidebar()}
29 27
30 28 <div class="main-content">
31 29
32 30
33 31
34 32 <h2>Code Blocks</h2>
35 33
36 34 <dl class="dl-horizontal">
37 35 <dt><code>.codeblock</code></dt>
38 36 <dd>Used as a wrapping element around <code>.code-header</code> and
39 37 <code>.code-body</code>. Used to show the content of a file or a
40 38 Gist.</dd>
41 39
42 40 <dt><code>.diffblock</code></dt>
43 41 <dd>Used as a wrapping element to show a diff in a Commit or Pull
44 42 Request page. Contains usually <code>.code-header</code>,
45 43 <code>.code-body</code> and in the edit case a <code>.message</code>.
46 44 </dd>
47 45 </dl>
48 46
49 47
50 48 <p>Code Blocks are used in the following areas:</p>
51 49
52 50 <ul>
53 51 <li>Commit: Showing the Diff (still called Changeset in a few
54 52 places).</li>
55 53 <li>File: Display a file, annotations, and edit a file.</li>
56 54 <li>Gist: Show the Gist and edit it.</li>
57 55 <li>Pull Request: Display the Diff of a Pull Request.</li>
58 56 </ul>
59 57
60 58
61 59
62 60 <!--
63 61 Compare Commits
64 62 -->
65 63 <h2>Compare Commits</h2>
66 64
67 65 <div id="c-e589e34d6be8-5ab783e6d81b" class="diffblock margined comm">
68 66 <div class="code-header">
69 67 <div title="Go back to changed files overview">
70 68 <a href="#changes_box">
71 69 <i class="icon-circle-arrow-up"></i>
72 70 </a>
73 71 </div>
74 72 <div class="changeset_header">
75 73 <div class="changeset_file">
76 74 <i class="icon-file"></i>
77 75 <a href="/example/files/e589e34d6be8ec2b44017f6c2e0bbe782f1aba6d/rhodecode/public/css/code-block.less">rhodecode/public/css/code-block.less</a>
78 76 </div>
79 77 <div class="diff-actions">
80 78 <a href="/example/diff/rhodecode/public/css/code-block.less?fulldiff=1&amp;diff1=d12301bafcc0aea15c9283d3af018daee2b04cd9&amp;diff=diff&amp;diff2=e589e34d6be8ec2b44017f6c2e0bbe782f1aba6d" class="tooltip" title="Show full diff for this file">
81 79 <img class="icon" src="/images/icons/page_white_go.png">
82 80 </a>
83 81 <a href="/example/diff-2way/rhodecode/public/css/code-block.less?fulldiff=1&amp;diff1=d12301bafcc0aea15c9283d3af018daee2b04cd9&amp;diff=diff&amp;diff2=e589e34d6be8ec2b44017f6c2e0bbe782f1aba6d" class="tooltip" title="Show full side-by-side diff for this file">
84 82 <img class="icon" src="/images/icons/application_double.png">
85 83 </a>
86 84 <a href="/example/diff/rhodecode/public/css/code-block.less?diff1=d12301bafcc0aea15c9283d3af018daee2b04cd9&amp;diff=raw&amp;diff2=e589e34d6be8ec2b44017f6c2e0bbe782f1aba6d" class="tooltip" title="Raw diff" tt_title="Raw diff">
87 85 <img class="icon" src="/images/icons/page_white.png">
88 86 </a>
89 87 <a href="/example/diff/rhodecode/public/css/code-block.less?diff1=d12301bafcc0aea15c9283d3af018daee2b04cd9&amp;diff=download&amp;diff2=e589e34d6be8ec2b44017f6c2e0bbe782f1aba6d" class="tooltip" title="Download diff">
90 88 <img class="icon" src="/images/icons/page_save.png">
91 89 </a>
92 90 <a class="tooltip" href="/example/changeset/d12301bafcc0aea15c9283d3af018daee2b04cd9...80ead1899f50a894889e19ffeb49c9cebf5bf045?c-e589e34d6be8-5ab783e6d81b=WS%3A1&amp;c-e589e34d6be8-5ab783e6d81b=C%3A3#c-e589e34d6be8-5ab783e6d81b" title="Ignore white space"><img alt="Ignore white space" class="icon" src="/images/icons/text_strikethrough.png"></a>
93 91 <a class="tooltip" href="/example/changeset/d12301bafcc0aea15c9283d3af018daee2b04cd9...80ead1899f50a894889e19ffeb49c9cebf5bf045?c-e589e34d6be8-5ab783e6d81b=C%3A6#c-e589e34d6be8-5ab783e6d81b" title="increase diff context to 6 lines"><img alt="increase diff context to 6 lines" class="icon" src="/images/icons/table_add.png"></a>
94 92 </div>
95 93 <span>
96 94 <label>
97 95 Show inline comments
98 96 <input checked="checked" class="show-inline-comments" id="" id_for="c-e589e34d6be8-5ab783e6d81b" name="" type="checkbox" value="1">
99 97 </label>
100 98 </span>
101 99 </div>
102 100 </div>
103 101 <div class="code-body">
104 102 <div class="full_f_path" path="rhodecode/public/css/code-block.less"></div>
105 103 <table class="code-difftable">
106 104 <tbody><tr class="line context">
107 105 <td class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o...">...</a></td>
108 106 <td class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n...">...</a></td>
109 107 <td class="code no-comment">
110 108 <pre>@@ -391,7 +391,7 @@
111 109 </pre>
112 110 </td>
113 111 </tr>
114 112 <tr class="line unmod">
115 113 <td id="rhodecodepubliccsscode-blockless_o391" class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o391">391</a></td>
116 114 <td id="rhodecodepubliccsscode-blockless_n391" class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n391">391</a></td>
117 115 <td class="code no-comment">
118 116 <pre>} /* Existing line, it might have a quite long content actually and in this case we might need some horizontal scrolling. The remaining text here is just used to make this line very long.
119 117 </pre>
120 118 </td>
121 119 </tr>
122 120 <tr class="line unmod">
123 121 <td id="rhodecodepubliccsscode-blockless_o392" class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o392">392</a></td>
124 122 <td id="rhodecodepubliccsscode-blockless_n392" class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n392">392</a></td>
125 123 <td class="code no-comment">
126 124 <pre></pre>
127 125 </td>
128 126 </tr>
129 127 <tr class="line unmod">
130 128 <td id="rhodecodepubliccsscode-blockless_o393" class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o393">393</a></td>
131 129 <td id="rhodecodepubliccsscode-blockless_n393" class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n393">393</a></td>
132 130 <td class="code no-comment">
133 131 <pre>.code-body.textarea.editor,
134 132 </pre>
135 133 </td>
136 134 </tr>
137 135 <tr class="line del">
138 136 <td id="rhodecodepubliccsscode-blockless_o394" class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o394">394</a></td>
139 137 <td class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n"></a></td>
140 138 <td class="code no-comment">
141 139 <pre>div.code-body{
142 140 </pre>
143 141 </td>
144 142 </tr>
145 143 <tr class="line add">
146 144 <td class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o"></a></td>
147 145 <td id="rhodecodepubliccsscode-blockless_n394" class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n394">394</a></td>
148 146 <td class="code no-comment">
149 147 <pre>div.code-body<ins> </ins>{
150 148 </pre>
151 149 </td>
152 150 </tr>
153 151 <tr class="line unmod">
154 152 <td id="rhodecodepubliccsscode-blockless_o395" class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o395">395</a></td>
155 153 <td id="rhodecodepubliccsscode-blockless_n395" class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n395">395</a></td>
156 154 <td class="code no-comment">
157 155 <pre> float: left;
158 156 </pre>
159 157 </td>
160 158 </tr>
161 159 <tr class="line unmod">
162 160 <td id="rhodecodepubliccsscode-blockless_o396" class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o396">396</a></td>
163 161 <td id="rhodecodepubliccsscode-blockless_n396" class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n396">396</a></td>
164 162 <td class="code no-comment">
165 163 <pre> position: relative;
166 164 </pre>
167 165 </td>
168 166 </tr>
169 167 <tr class="line unmod">
170 168 <td id="rhodecodepubliccsscode-blockless_o397" class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o397">397</a></td>
171 169 <td id="rhodecodepubliccsscode-blockless_n397" class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n397">397</a></td>
172 170 <td class="code no-comment">
173 171 <pre> max-width: none;
174 172 </pre>
175 173 </td>
176 174 </tr>
177 175 <tr class="line context">
178 176 <td class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o...">...</a></td>
179 177 <td class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n...">...</a></td>
180 178 <td class="code no-comment">
181 179 <pre>@@ -399,3 +399,6 @@
182 180 </pre>
183 181 </td>
184 182 </tr>
185 183 <tr class="line unmod">
186 184 <td id="rhodecodepubliccsscode-blockless_o399" class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o399">399</a></td>
187 185 <td id="rhodecodepubliccsscode-blockless_n399" class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n399">399</a></td>
188 186 <td class="code no-comment">
189 187 <pre> box-sizing: border-box;
190 188 </pre>
191 189 </td>
192 190 </tr>
193 191 <tr class="line unmod">
194 192 <td id="rhodecodepubliccsscode-blockless_o400" class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o400">400</a></td>
195 193 <td id="rhodecodepubliccsscode-blockless_n400" class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n400">400</a></td>
196 194 <td class="code no-comment">
197 195 <pre>}
198 196 </pre>
199 197 </td>
200 198 </tr>
201 199 <tr class="line unmod">
202 200 <td id="rhodecodepubliccsscode-blockless_o401" class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o401">401</a></td>
203 201 <td id="rhodecodepubliccsscode-blockless_n401" class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n401">401</a></td>
204 202 <td class="code no-comment">
205 203 <pre></pre>
206 204 </td>
207 205 </tr>
208 206 <tr class="line add">
209 207 <td class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o"></a></td>
210 208 <td id="rhodecodepubliccsscode-blockless_n402" class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n402">402</a></td>
211 209 <td class="code no-comment">
212 210 <pre>.code-body td{
213 211 </pre>
214 212 </td>
215 213 </tr>
216 214 <tr class="line add">
217 215 <td class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o"></a></td>
218 216 <td id="rhodecodepubliccsscode-blockless_n403" class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n403">403</a></td>
219 217 <td class="code no-comment">
220 218 <pre> line-height: 1.2em;
221 219 </pre>
222 220 </td>
223 221 </tr>
224 222 <tr class="line add">
225 223 <td class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o"></a></td>
226 224 <td id="rhodecodepubliccsscode-blockless_n404" class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n404">404</a></td>
227 225 <td class="code no-comment">
228 226 <pre>}
229 227 </pre>
230 228 </td>
231 229 </tr>
232 230 <tr class="line context">
233 231 <td class="lineno old"><a href="#rhodecodepubliccsscode-blockless_o...">...</a></td>
234 232 <td class="lineno new"><a href="#rhodecodepubliccsscode-blockless_n...">...</a></td>
235 233 <td class="code no-comment">
236 234 <pre> No newline at end of file
237 235 </pre>
238 236 </td>
239 237 </tr>
240 238 </tbody></table>
241 239 </div>
242 240 </div>
243 241
244 242
245 243
246 244
247 245
248 246
249 247 <!--
250 248 Pull Request
251 249 -->
252 250
253 251 <h2>Pull Request</h2>
254 252
255 253 <div class="cs_files">
256 254 <table class="compare_view_files">
257 255
258 256 <tbody><tr class="cs_M collapse_file" fid="c--5f1d017cf13b">
259 257 <td class="cs_icon_td">
260 258 <span class="collapse_file_icon" fid="c--5f1d017cf13b"></span>
261 259 </td>
262 260 <td class="cs_icon_td">
263 261 <div class="flag_status not_reviewed hidden"></div>
264 262 </td>
265 263 <td id="a_c--5f1d017cf13b">
266 264 <a class="compare_view_filepath" href="#a_c--5f1d017cf13b">
267 265 rhodecode/public/css/main.less
268 266 </a>
269 267 <span id="diff_c--5f1d017cf13b" class="diff_links" style="">
270 268 <a href="/example/diff/rhodecode/public/css/main.less?fulldiff=1&amp;diff1=f73e9946825c8a7ef2c1178cd1e67986d5831f8f&amp;diff=diff&amp;diff2=27eb56cf467ca849112536d62decb2ed020b3ebc">
271 269 Unified Diff
272 270 </a>
273 271 |
274 272 <a href="/example/diff-2way/rhodecode/public/css/main.less?fulldiff=1&amp;diff1=f73e9946825c8a7ef2c1178cd1e67986d5831f8f&amp;diff=diff&amp;diff2=27eb56cf467ca849112536d62decb2ed020b3ebc">
275 273 Side-by-side Diff
276 274 </a>
277 275 </span>
278 276 </td>
279 277 <td>
280 278 <div class="changes pull-right"><div style="width:100px"><div class="added top-left-rounded-corner-mid bottom-left-rounded-corner-mid" style="width:33.3333333333%">1</div><div class="deleted top-right-rounded-corner-mid bottom-right-rounded-corner-mid" style="width:66.6666666667%">2</div></div></div>
281 279 <div class="comment-bubble pull-right" data-path="rhodecode/public/css/main.less">
282 280 <i class="icon-comment"></i>
283 281 </div>
284 282 </td>
285 283 </tr>
286 284 <tr id="tr_c--5f1d017cf13b">
287 285 <td></td>
288 286 <td></td>
289 287 <td class="injected_diff" colspan="2">
290 288
291 289 <div class="diff-container" id="diff-container-140360026534904">
292 290 <div id="c--5f1d017cf13b_target"></div>
293 291 <div id="c--5f1d017cf13b" class="diffblock margined comm">
294 292 <div class="code-body">
295 293 <div class="full_f_path" path="rhodecode/public/css/main.less" style="display: none;"></div>
296 294 <table class="code-difftable">
297 295 <tbody><tr class="line context">
298 296 <td class="lineno old"><a href="#rhodecodepubliccssmainless_o...">...</a></td>
299 297 <td class="lineno new"><a href="#rhodecodepubliccssmainless_n...">...</a></td>
300 298 <td class="code ">
301 299 <pre>@@ -2110,7 +2110,6 @@
302 300 </pre>
303 301 </td>
304 302 </tr>
305 303 <tr class="line unmod">
306 304 <td id="rhodecodepubliccssmainless_o2110" class="lineno old"><a href="#rhodecodepubliccssmainless_o2110">2110</a></td>
307 305 <td id="rhodecodepubliccssmainless_n2110" class="lineno new"><a href="#rhodecodepubliccssmainless_n2110">2110</a></td>
308 306 <td class="code ">
309 307 <pre><span class="tab-escape"> </span>width: auto !important;
310 308 </pre>
311 309 </td>
312 310 </tr>
313 311 <tr class="line unmod">
314 312 <td id="rhodecodepubliccssmainless_o2111" class="lineno old"><a href="#rhodecodepubliccssmainless_o2111">2111</a></td>
315 313 <td id="rhodecodepubliccssmainless_n2111" class="lineno new"><a href="#rhodecodepubliccssmainless_n2111">2111</a></td>
316 314 <td class="code ">
317 315 <pre><span class="tab-escape"> </span>min-width: 160px;
318 316 </pre>
319 317 </td>
320 318 </tr>
321 319 <tr class="line unmod">
322 320 <td id="rhodecodepubliccssmainless_o2112" class="lineno old"><a href="#rhodecodepubliccssmainless_o2112">2112</a></td>
323 321 <td id="rhodecodepubliccssmainless_n2112" class="lineno new"><a href="#rhodecodepubliccssmainless_n2112">2112</a></td>
324 322 <td class="code ">
325 323 <pre><span class="tab-escape"> </span>margin: @padding @padding @padding 0;
326 324 </pre>
327 325 </td>
328 326 </tr>
329 327 <tr class="line del">
330 328 <td id="rhodecodepubliccssmainless_o2113" class="lineno old"><a href="#rhodecodepubliccssmainless_o2113">2113</a></td>
331 329 <td class="lineno new"><a href="#rhodecodepubliccssmainless_n"></a></td>
332 330 <td class="code ">
333 331 <pre><span class="tab-escape"> </span>padding: .9em; /* Old comment which was making this line a very long line so that we might have to deal with it by either adding horizontal scrolling or some smart way of breaking this line. */
334 332 </pre>
335 333 </td>
336 334 </tr>
337 335 <tr class="line unmod">
338 336 <td id="rhodecodepubliccssmainless_o2114" class="lineno old"><a href="#rhodecodepubliccssmainless_o2114">2114</a></td>
339 337 <td id="rhodecodepubliccssmainless_n2113" class="lineno new"><a href="#rhodecodepubliccssmainless_n2113">2113</a></td>
340 338 <td class="code ">
341 339 <pre> line-height: 1em;
342 340 </pre>
343 341 </td>
344 342 </tr>
345 343 <tr class="line unmod">
346 344 <td id="rhodecodepubliccssmainless_o2115" class="lineno old"><a href="#rhodecodepubliccssmainless_o2115">2115</a></td>
347 345 <td id="rhodecodepubliccssmainless_n2114" class="lineno new"><a href="#rhodecodepubliccssmainless_n2114">2114</a></td>
348 346 <td class="code ">
349 347 <pre><span class="tab-escape"> </span>z-index: 100;//js sets the menu below it to 9999
350 348 </pre>
351 349 </td>
352 350 </tr>
353 351 <tr class="line unmod">
354 352 <td id="rhodecodepubliccssmainless_o2116" class="lineno old"><a href="#rhodecodepubliccssmainless_o2116">2116</a></td>
355 353 <td id="rhodecodepubliccssmainless_n2115" class="lineno new"><a href="#rhodecodepubliccssmainless_n2115">2115</a></td>
356 354 <td class="code ">
357 355 <pre><span class="tab-escape"> </span>background-color: white;
358 356 </pre>
359 357 </td>
360 358 </tr>
361 359 <tr class="line context">
362 360 <td class="lineno old"><a href="#rhodecodepubliccssmainless_o...">...</a></td>
363 361 <td class="lineno new"><a href="#rhodecodepubliccssmainless_n...">...</a></td>
364 362 <td class="code ">
365 363 <pre>@@ -2118,7 +2117,7 @@
366 364 </pre>
367 365 </td>
368 366 </tr>
369 367 <tr class="line unmod">
370 368 <td id="rhodecodepubliccssmainless_o2118" class="lineno old"><a href="#rhodecodepubliccssmainless_o2118">2118</a></td>
371 369 <td id="rhodecodepubliccssmainless_n2117" class="lineno new"><a href="#rhodecodepubliccssmainless_n2117">2117</a></td>
372 370 <td class="code ">
373 371 <pre></pre>
374 372 </td>
375 373 </tr>
376 374 <tr class="line unmod">
377 375 <td id="rhodecodepubliccssmainless_o2119" class="lineno old"><a href="#rhodecodepubliccssmainless_o2119">2119</a></td>
378 376 <td id="rhodecodepubliccssmainless_n2118" class="lineno new"><a href="#rhodecodepubliccssmainless_n2118">2118</a></td>
379 377 <td class="code ">
380 378 <pre><span class="tab-escape"> </span>a {
381 379 </pre>
382 380 </td>
383 381 </tr>
384 382 <tr class="line unmod">
385 383 <td id="rhodecodepubliccssmainless_o2120" class="lineno old"><a href="#rhodecodepubliccssmainless_o2120">2120</a></td>
386 384 <td id="rhodecodepubliccssmainless_n2119" class="lineno new"><a href="#rhodecodepubliccssmainless_n2119">2119</a></td>
387 385 <td class="code ">
388 386 <pre><span class="tab-escape"> </span><span class="tab-escape"> </span>display:block;
389 387 </pre>
390 388 </td>
391 389 </tr>
392 390 <tr class="line del">
393 391 <td id="rhodecodepubliccssmainless_o2121" class="lineno old"><a href="#rhodecodepubliccssmainless_o2121">2121</a></td>
394 392 <td class="lineno new"><a href="#rhodecodepubliccssmainless_n"></a></td>
395 393 <td class="code ">
396 394 <pre><span class="tab-escape"> </span><del><span< del=""> <del>class=</del><del>"tab-escape"</del><del>&gt; </del>padding: <del>0</del>;
397 395 </span<></del></pre>
398 396 </td>
399 397 </tr>
400 398 <tr class="line add">
401 399 <td class="lineno old"><a href="#rhodecodepubliccssmainless_o"></a></td>
402 400 <td id="rhodecodepubliccssmainless_n2120" class="lineno new"><a href="#rhodecodepubliccssmainless_n2120">2120</a></td>
403 401 <td class="code ">
404 402 <pre><span class="tab-escape"> </span><ins> </ins> <ins> </ins><ins> </ins>padding: <ins>.9em</ins>;
405 403 </pre>
406 404 </td>
407 405 </tr>
408 406 <tr class="line unmod">
409 407 <td id="rhodecodepubliccssmainless_o2122" class="lineno old"><a href="#rhodecodepubliccssmainless_o2122">2122</a></td>
410 408 <td id="rhodecodepubliccssmainless_n2121" class="lineno new"><a href="#rhodecodepubliccssmainless_n2121">2121</a></td>
411 409 <td class="code ">
412 410 <pre></pre>
413 411 </td>
414 412 </tr>
415 413 <tr class="line unmod">
416 414 <td id="rhodecodepubliccssmainless_o2123" class="lineno old"><a href="#rhodecodepubliccssmainless_o2123">2123</a></td>
417 415 <td id="rhodecodepubliccssmainless_n2122" class="lineno new"><a href="#rhodecodepubliccssmainless_n2122">2122</a></td>
418 416 <td class="code ">
419 417 <pre><span class="tab-escape"> </span><span class="tab-escape"> </span>&amp;:after {
420 418 </pre>
421 419 </td>
422 420 </tr>
423 421 <tr class="line unmod">
424 422 <td id="rhodecodepubliccssmainless_o2124" class="lineno old"><a href="#rhodecodepubliccssmainless_o2124">2124</a></td>
425 423 <td id="rhodecodepubliccssmainless_n2123" class="lineno new"><a href="#rhodecodepubliccssmainless_n2123">2123</a></td>
426 424 <td class="code ">
427 425 <pre><span class="tab-escape"> </span><span class="tab-escape"> </span><span class="tab-escape"> </span>content: "\00A0\25BE";
428 426 </pre>
429 427 </td>
430 428 </tr>
431 429 </tbody></table>
432 430 </div>
433 431 </div>
434 432 </div>
435 433
436 434 </td>
437 435 </tr>
438 436 </tbody></table>
439 437 </div>
440 438
441 439
442 440
443 441
444 442
445 443
446 444
447 445
448 446
449 447 <!--
450 448 File View
451 449 -->
452 450
453 451 ##TODO: lisa: I believe this needs to be updated as the layout has changed.
454 452 <h2>File View</h2>
455 453
456 454 <div class="codeblock">
457 455 <div class="code-header">
458 456 <div class="stats">
459 457 <div class="img">
460 458 <i class="icon-file"></i>
461 459 <span class="revision_id item"><a href="/example/changeset/fc252256eb0fcb4f2613e66f0126ea27967ae28c">r5487:fc252256eb0f</a></span>
462 460 <span>1.2 KiB</span>
463 461 <span class="item last">text/x-python</span>
464 462 <div class="buttons">
465 463
466 464 <a id="file_history_overview" class="btn btn-mini" href="#">
467 465 <i class="icon-time"></i> history
468 466 </a>
469 467 <a id="file_history_overview_full" class="btn btn-mini" style="display: none" href="/example/changelog/fc252256eb0fcb4f2613e66f0126ea27967ae28c/rhodecode/websetup.py">
470 468 <i class="icon-time"></i> show full history
471 469 </a>
472 470 <a class="btn btn-mini" href="/example/annotate/fc252256eb0fcb4f2613e66f0126ea27967ae28c/rhodecode/websetup.py">annotation</a>
473 471 <a class="btn btn-mini" href="/example/raw/fc252256eb0fcb4f2613e66f0126ea27967ae28c/rhodecode/websetup.py">raw</a>
474 472 <a class="btn btn-mini" href="/example/rawfile/fc252256eb0fcb4f2613e66f0126ea27967ae28c/rhodecode/websetup.py">
475 473 <i class="icon-archive"></i> download
476 474 </a>
477 475
478 476 <a class="btn btn-mini disabled tooltip" href="#" title="Editing files allowed only when on branch head commit">edit</a>
479 477 <a class="btn btn-mini btn-danger disabled tooltip" href="#" title="Deleting files allowed only when on branch head commit">delete</a>
480 478 </div>
481 479 </div>
482 480 </div>
483 481 <div id="file_history_container"></div>
484 482 <div class="author">
485 483 <div class="gravatar">
486 484 <img alt="gravatar" src="https://secure.gravatar.com/avatar/99e27b99c64003ca8c9875c9e3843495?d=identicon&amp;s=32" height="16" width="16">
487 485 </div>
488 486 <div title="Marcin Kuzminski <marcin@python-works.com>" class="user">Marcin Kuzminski - <span class="tooltip" title="Wed, 02 Jul 2014 08:48:15">6m and 12d ago</span></div>
489 487 </div>
490 488 <div id="trimmed_message_box" class="commit">License changes</div>
491 489 <div id="message_expand" style="display: none;">
492 490 <i class="icon-resize-vertical"></i>
493 491 expand
494 492 <i class="icon-resize-vertical"></i>
495 493 </div>
496 494 </div>
497 495 <div class="code-body">
498 496 <table class="code-highlighttable"><tbody><tr><td class="linenos"><div class="linenodiv"><pre><a href="#L1"> 1</a>
499 497 <a href="#L2"> 2</a>
500 498 <a href="#L3"> 3</a>
501 499 <a href="#L4"> 4</a>
502 500 <a href="#L5"> 5</a>
503 501 <a href="#L6"> 6</a>
504 502 <a href="#L7"> 7</a>
505 503 <a href="#L8"> 8</a>
506 504 <a href="#L9"> 9</a>
507 505 <a href="#L10">10</a>
508 506 <a href="#L11">11</a>
509 507 <a href="#L12">12</a>
510 508 <a href="#L13">13</a>
511 509 <a href="#L14">14</a>
512 510 <a href="#L15">15</a>
513 511 <a href="#L16">16</a>
514 512 <a href="#L17">17</a>
515 513 <a href="#L18">18</a>
516 514 <a href="#L19">19</a>
517 515 <a href="#L20">20</a>
518 516 <a href="#L21">21</a>
519 517 <a href="#L22">22</a>
520 518 <a href="#L23">23</a>
521 519 <a href="#L24">24</a>
522 520 <a href="#L25">25</a>
523 521 <a href="#L26">26</a>
524 522 <a href="#L27">27</a>
525 523 <a href="#L28">28</a>
526 524 <a href="#L29">29</a>
527 525 <a href="#L30">30</a>
528 526 <a href="#L31">31</a>
529 527 <a href="#L32">32</a>
530 528 <a href="#L33">33</a>
531 529 <a href="#L34">34</a>
532 530 <a href="#L35">35</a>
533 531 <a href="#L36">36</a>
534 532 <a href="#L37">37</a>
535 533 <a href="#L38">38</a>
536 534 <a href="#L39">39</a>
537 535 <a href="#L40">40</a>
538 536 <a href="#L41">41</a>
539 537 <a href="#L42">42</a></pre></div></td><td id="hlcode" class="code"><div class="code-highlight"><pre><div id="L1"><a name="L-1"></a><span class="c"># -*- coding: utf-8 -*-</span>
540 538 </div><div id="L2"><a name="L-2"></a>
541 539 </div><div id="L3"><a name="L-3"></a><span class="c"># Published under Business Source License.</span>
542 540 </div><div id="L4"><a name="L-4"></a><span class="c"># Read the full license text at https://rhodecode.com/licenses.</span>
543 541 </div><div id="L5"><a name="L-5"></a><span class="sd">"""</span>
544 542 </div><div id="L6"><a name="L-6"></a><span class="sd">rhodecode.websetup</span>
545 543 </div><div id="L7"><a name="L-7"></a><span class="sd">~~~~~~~~~~~~~~~~~~</span>
546 544 </div><div id="L8"><a name="L-8"></a>
547 545 </div><div id="L9"><a name="L-9"></a><span class="sd">Weboperations and setup for rhodecode. Intentionally long line to show what will happen if this line does not fit onto the screen. It might have some horizontal scrolling applied or some other fancy mechanism to deal with it.</span>
548 546 </div><div id="L10"><a name="L-10"></a>
549 547 </div><div id="L11"><a name="L-11"></a><span class="sd">:created_on: Dec 11, 2010</span>
550 548 </div><div id="L12"><a name="L-12"></a><span class="sd">:author: marcink</span>
551 549 </div><div id="L13"><a name="L-13"></a><span class="sd">:copyright: (c) 2013-2015 RhodeCode GmbH.</span>
552 550 </div><div id="L14"><a name="L-14"></a><span class="sd">:license: Business Source License, see LICENSE for more details.</span>
553 551 </div><div id="L15"><a name="L-15"></a><span class="sd">"""</span>
554 552 </div><div id="L16"><a name="L-16"></a>
555 553 </div><div id="L17"><a name="L-17"></a><span class="kn">import</span> <span class="nn">logging</span>
556 554 </div><div id="L18"><a name="L-18"></a>
557 555 </div><div id="L19"><a name="L-19"></a><span class="kn">from</span> <span class="nn">rhodecode.config.environment</span> <span class="kn">import</span> <span class="n">load_environment</span>
558 556 </div><div id="L20"><a name="L-20"></a><span class="kn">from</span> <span class="nn">rhodecode.lib.db_manage</span> <span class="kn">import</span> <span class="n">DbManage</span>
559 557 </div><div id="L21"><a name="L-21"></a><span class="kn">from</span> <span class="nn">rhodecode.model.meta</span> <span class="kn">import</span> <span class="n">Session</span>
560 558 </div><div id="L22"><a name="L-22"></a>
561 559 </div><div id="L23"><a name="L-23"></a>
562 560 </div><div id="L24"><a name="L-24"></a><span class="n">log</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
563 561 </div><div id="L25"><a name="L-25"></a>
564 562 </div><div id="L26"><a name="L-26"></a>
565 563 </div><div id="L27"><a name="L-27"></a><span class="k">def</span> <span class="nf">setup_app</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="n">conf</span><span class="p">,</span> <span class="nb">vars</span><span class="p">):</span>
566 564 </div><div id="L28"><a name="L-28"></a> <span class="sd">"""Place any commands to setup rhodecode here"""</span>
567 565 </div><div id="L29"><a name="L-29"></a> <span class="n">dbconf</span> <span class="o">=</span> <span class="n">conf</span><span class="p">[</span><span class="s">'sqlalchemy.db1.url'</span><span class="p">]</span>
568 566 </div><div id="L30"><a name="L-30"></a> <span class="n">dbmanage</span> <span class="o">=</span> <span class="n">DbManage</span><span class="p">(</span><span class="n">log_sql</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">dbconf</span><span class="o">=</span><span class="n">dbconf</span><span class="p">,</span> <span class="n">root</span><span class="o">=</span><span class="n">conf</span><span class="p">[</span><span class="s">'here'</span><span class="p">],</span>
569 567 </div><div id="L31"><a name="L-31"></a> <span class="n">tests</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">cli_args</span><span class="o">=</span><span class="n">command</span><span class="o">.</span><span class="n">options</span><span class="o">.</span><span class="n">__dict__</span><span class="p">)</span>
570 568 </div><div id="L32"><a name="L-32"></a> <span class="n">dbmanage</span><span class="o">.</span><span class="n">create_tables</span><span class="p">(</span><span class="n">override</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
571 569 </div><div id="L33"><a name="L-33"></a> <span class="n">dbmanage</span><span class="o">.</span><span class="n">set_db_version</span><span class="p">()</span>
572 570 </div><div id="L34"><a name="L-34"></a> <span class="n">opts</span> <span class="o">=</span> <span class="n">dbmanage</span><span class="o">.</span><span class="n">config_prompt</span><span class="p">(</span><span class="bp">None</span><span class="p">)</span>
573 571 </div><div id="L35"><a name="L-35"></a> <span class="n">dbmanage</span><span class="o">.</span><span class="n">create_settings</span><span class="p">(</span><span class="n">opts</span><span class="p">)</span>
574 572 </div><div id="L36"><a name="L-36"></a> <span class="n">dbmanage</span><span class="o">.</span><span class="n">create_default_user</span><span class="p">()</span>
575 573 </div><div id="L37"><a name="L-37"></a> <span class="n">dbmanage</span><span class="o">.</span><span class="n">admin_prompt</span><span class="p">()</span>
576 574 </div><div id="L38"><a name="L-38"></a> <span class="n">dbmanage</span><span class="o">.</span><span class="n">create_permissions</span><span class="p">()</span>
577 575 </div><div id="L39"><a name="L-39"></a> <span class="n">dbmanage</span><span class="o">.</span><span class="n">populate_default_permissions</span><span class="p">()</span>
578 576 </div><div id="L40"><a name="L-40"></a> <span class="n">Session</span><span class="p">()</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
579 577 </div><div id="L41"><a name="L-41"></a> <span class="n">load_environment</span><span class="p">(</span><span class="n">conf</span><span class="o">.</span><span class="n">global_conf</span><span class="p">,</span> <span class="n">conf</span><span class="o">.</span><span class="n">local_conf</span><span class="p">,</span> <span class="n">initial</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
580 578 </div><div id="L42"><a name="L-42"></a> <span class="n">DbManage</span><span class="o">.</span><span class="n">check_waitress</span><span class="p">()</span>
581 579 </div></pre></div>
582 580 </td></tr></tbody></table>
583 581 </div>
584 582 </div>
585 583
586 584
587 585
588 586
589 587
590 588
591 589
592 590
593 591
594 592 <!--
595 593 Gist Edit
596 594 -->
597 595
598 596
599 597 <h2>Gist Edit</h2>
600 598
601 599 <div class="codeblock">
602 600 <div class="code-header">
603 601 <div class="form">
604 602 <div class="fields">
605 603 <input id="filename" name="filename" placeholder="name this file..." size="30" type="text">
606 604 <div class="select2-container drop-menu" id="s2id_mimetype"><a href="javascript:void(0)" class="select2-choice" tabindex="-1"> <span class="select2-chosen" id="select2-chosen-3">Python</span><abbr class="select2-search-choice-close"></abbr> <span class="select2-arrow" role="presentation"><b role="presentation"></b></span></a><label for="s2id_autogen3" class="select2-offscreen"></label><input class="select2-focusser select2-offscreen" type="text" aria-haspopup="true" role="button" aria-labelledby="select2-chosen-3" id="s2id_autogen3"><div class="select2-drop select2-display-none drop-menu-dropdown select2-with-searchbox"> <div class="select2-search"> <label for="s2id_autogen3_search" class="select2-offscreen"></label> <input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" class="select2-input" role="combobox" aria-expanded="true" aria-autocomplete="list" aria-owns="select2-results-3" id="s2id_autogen3_search" placeholder=""> </div> <ul class="select2-results" role="listbox" id="select2-results-3"> </ul></div></div><select id="mimetype" name="mimetype" tabindex="-1" title="" style="display: none;">
607 605 <option selected="selected" value="plain">plain</option>
608 606 <option value="text/apl" mode="apl">APL</option><option value="text/x-asterisk" mode="asterisk">Asterisk</option><option value="text/x-csrc" mode="clike">C</option><option value="text/x-c++src" mode="clike">C++</option><option value="text/x-cobol" mode="cobol">Cobol</option><option value="text/x-java" mode="clike">Java</option><option value="text/x-csharp" mode="clike">C#</option><option value="text/x-scala" mode="clike">Scala</option><option value="text/x-clojure" mode="clojure">Clojure</option><option value="text/x-coffeescript" mode="coffeescript">CoffeeScript</option><option value="text/x-common-lisp" mode="commonlisp">Common Lisp</option><option value="text/css" mode="css">CSS</option><option value="text/x-d" mode="d">D</option><option value="text/x-diff" mode="diff">diff</option><option value="application/xml-dtd" mode="dtd">DTD</option><option value="text/x-dylan" mode="dylan">Dylan</option><option value="text/x-ecl" mode="ecl">ECL</option><option value="text/x-eiffel" mode="eiffel">Eiffel</option><option value="text/x-erlang" mode="erlang">Erlang</option><option value="text/x-fortran" mode="fortran">Fortran</option><option value="text/x-fsharp" mode="mllike">F#</option><option value="text/x-gas" mode="gas">Gas</option><option value="text/x-go" mode="go">GO</option><option value="text/x-feature" mode="gherkin">Gherkin</option><option value="text/x-go" mode="go">Go</option><option value="text/x-groovy" mode="groovy">Groovy</option><option value="text/x-haml" mode="haml">HAML</option><option value="text/x-haskell" mode="haskell">Haskell</option><option value="text/x-haxe" mode="haxe">Haxe</option><option value="application/x-aspx" mode="htmlembedded">ASP.NET</option><option value="application/x-ejs" mode="htmlembedded">Embedded Javascript</option><option value="application/x-jsp" mode="htmlembedded">JavaServer Pages</option><option value="text/html" mode="htmlmixed">HTML</option><option value="message/http" mode="http">HTTP</option><option value="text/x-jade" mode="jade">Jade</option><option value="text/javascript" mode="javascript">JavaScript</option><option value="application/json" mode="javascript">JSON</option><option value="application/typescript" mode="javascript">TypeScript</option><option value="jinja2" mode="jinja2">Jinja2</option><option value="text/x-julia" mode="julia">Julia</option><option value="text/x-less" mode="less">LESS</option><option value="text/x-livescript" mode="livescript">LiveScript</option><option value="text/x-lua" mode="lua">Lua</option><option value="text/x-markdown" mode="markdown">Markdown (GitHub-flavour)</option><option value="text/mirc" mode="mirc">mIRC</option><option value="text/x-nginx-conf" mode="nginx">Nginx</option><option value="text/n-triples" mode="ntriples">NTriples</option><option value="text/x-ocaml" mode="ocaml">OCaml</option><option value="text/x-ocaml" mode="mllike">OCaml</option><option value="text/x-octave" mode="octave">Octave</option><option value="text/x-pascal" mode="pascal">Pascal</option><option value="null" mode="pegjs">PEG.js</option><option value="text/x-perl" mode="perl">Perl</option><option value="text/x-php" mode="php">PHP</option><option value="text/x-pig" mode="pig">Pig</option><option value="text/plain" mode="null">Plain Text</option><option value="text/x-properties" mode="properties">Properties files</option><option value="text/x-python" mode="python">Python</option><option value="text/x-puppet" mode="puppet">Puppet</option><option value="text/x-rsrc" mode="r">R</option><option value="text/x-rst" mode="rst">reStructuredText</option><option value="text/x-ruby" mode="ruby">Ruby</option><option value="text/x-rustsrc" mode="rust">Rust</option><option value="text/x-sass" mode="sass">Sass</option><option value="text/x-scheme" mode="scheme">Scheme</option><option value="text/x-scss" mode="css">SCSS</option><option value="text/x-sh" mode="shell">Shell</option><option value="application/sieve" mode="sieve">Sieve</option><option value="text/x-stsrc" mode="smalltalk">Smalltalk</option><option value="text/x-smarty" mode="smarty">Smarty</option><option value="text/x-smarty" mode="smartymixed">SmartyMixed</option><option value="text/x-solr" mode="solr">Solr</option><option value="application/x-sparql-query" mode="sparql">SPARQL</option><option value="text/x-sql" mode="sql">SQL</option><option value="text/x-mariadb" mode="sql">MariaDB</option><option value="text/x-stex" mode="stex">sTeX</option><option value="text/x-latex" mode="stex">LaTeX</option><option value="text/x-systemverilog" mode="verilog">SystemVerilog</option><option value="text/x-tcl" mode="tcl">Tcl</option><option value="text/x-tiddlywiki" mode="tiddlywiki">TiddlyWiki </option><option value="text/tiki" mode="tiki">Tiki wiki</option><option value="text/x-toml" mode="toml">TOML</option><option value="text/turtle" mode="turtle">Turtle</option><option value="text/x-vb" mode="vb">VB.NET</option><option value="text/vbscript" mode="vbscript">VBScript</option><option value="text/velocity" mode="velocity">Velocity</option><option value="text/x-verilog" mode="verilog">Verilog</option><option value="application/xml" mode="xml">XML</option><option value="text/html" mode="xml">HTML</option><option value="application/xquery" mode="xquery">XQuery</option><option value="text/x-yaml" mode="yaml">YAML</option><option value="text/x-z80" mode="z80">Z80</option></select>
609 607 <script>
610 608 $(document).ready(function() {
611 609 $('#mimetype').select2({
612 610 containerCssClass: 'drop-menu',
613 611 dropdownCssClass: 'drop-menu-dropdown',
614 612 dropdownAutoWidth: true
615 613 });
616 614 });
617 615 </script>
618 616
619 617 </div>
620 618 </div>
621 619 </div>
622 620 <div id="editor_container">
623 621 <div id="editor_pre"></div>
624 622 <textarea id="editor" name="content" style="display: none;"></textarea><div class="CodeMirror cm-s-default"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 484px; left: 219.4091796875px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" style="position: absolute; padding: 0px; width: 1000px; height: 1em; outline: none;" tabindex="0"></textarea></div><div class="CodeMirror-hscrollbar" style="left: 29px; min-height: 18px;"><div style="height: 100%; min-height: 1px; width: 0px;"></div></div><div class="CodeMirror-vscrollbar" style="min-width: 18px; display: block; bottom: 0px;"><div style="min-width: 1px; height: 619px;"></div></div><div class="CodeMirror-scrollbar-filler"></div><div class="CodeMirror-gutter-filler"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="min-width: 700.269653320313px; margin-left: 29px; min-height: 619px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines"><div style="position: relative; outline: none;"><div class="CodeMirror-measure"><div class="CodeMirror-linenumber CodeMirror-gutter-elt"><div>47</div></div></div><div style="position: relative; z-index: 1; display: none;"></div><div class="CodeMirror-code"><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">1</div></div><pre><span class="cm-keyword">import</span> <span class="cm-variable">re</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">2</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">3</div></div><pre><span class="cm-keyword">from</span> <span class="cm-variable">django</span>.<span class="cm-variable">utils</span>.<span class="cm-variable">text</span> <span class="cm-keyword">import</span> <span class="cm-variable">compress_sequence</span>, <span class="cm-variable">compress_string</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">4</div></div><pre><span class="cm-keyword">from</span> <span class="cm-variable">django</span>.<span class="cm-variable">utils</span>.<span class="cm-variable">cache</span> <span class="cm-keyword">import</span> <span class="cm-variable">patch_vary_headers</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">5</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">6</div></div><pre><span class="cm-variable">re_accepts_gzip</span> = <span class="cm-variable">re</span>.<span class="cm-builtin">compile</span>(<span class="cm-string">r'\bgzip\b'</span>)</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">7</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">8</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">9</div></div><pre><span class="cm-keyword">class</span> <span class="cm-def">GZipMiddleware</span>(<span class="cm-builtin">object</span>): # Intentionally long line to show what will happen if this line does not fit onto the screen. It might have some horizontal scrolling applied or some other fancy mechanism to deal with it.</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">10</div></div><pre> <span class="cm-string">"""</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">11</div></div><pre><span class="cm-string"> This middleware compresses content if the browser allows gzip compression.</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">12</div></div><pre><span class="cm-string"> It sets the Vary header accordingly, so that caches will base their storage</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">13</div></div><pre><span class="cm-string"> on the Accept-Encoding header.</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">14</div></div><pre><span class="cm-string"> """</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">15</div></div><pre> <span class="cm-keyword">def</span> <span class="cm-def">process_response</span>(<span class="cm-variable-2">self</span>, <span class="cm-variable">request</span>, <span class="cm-variable">response</span>):</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">16</div></div><pre> <span class="cm-comment"># It's not worth attempting to compress really short responses.</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">17</div></div><pre> <span class="cm-keyword">if</span> <span class="cm-operator">not</span> <span class="cm-variable">response</span>.<span class="cm-variable">streaming</span> <span class="cm-operator">and</span> <span class="cm-builtin">len</span>(<span class="cm-variable">response</span>.<span class="cm-variable">content</span>) <span class="cm-operator">&lt;</span> <span class="cm-number">200</span>:</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">18</div></div><pre> <span class="cm-keyword">return</span> <span class="cm-variable">response</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">19</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">20</div></div><pre> <span class="cm-comment"># Avoid gzipping if we've already got a content-encoding.</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">21</div></div><pre> <span class="cm-keyword">if</span> <span class="cm-variable">response</span>.<span class="cm-variable">has_header</span>(<span class="cm-string">'Content-Encoding'</span>):</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">22</div></div><pre> <span class="cm-keyword">return</span> <span class="cm-variable">response</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">23</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">24</div></div><pre> <span class="cm-variable">patch_vary_headers</span>(<span class="cm-variable">response</span>, (<span class="cm-string">'Accept-Encoding'</span>,))</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">25</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">26</div></div><pre> <span class="cm-variable">ae</span> = <span class="cm-variable">request</span>.<span class="cm-variable">META</span>.<span class="cm-variable">get</span>(<span class="cm-string">'HTTP_ACCEPT_ENCODING'</span>, <span class="cm-string">''</span>)</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">27</div></div><pre> <span class="cm-keyword">if</span> <span class="cm-operator">not</span> <span class="cm-variable">re_accepts_gzip</span>.<span class="cm-variable">search</span>(<span class="cm-variable">ae</span>):</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">28</div></div><pre> <span class="cm-keyword">return</span> <span class="cm-variable">response</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">29</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">30</div></div><pre> <span class="cm-keyword">if</span> <span class="cm-variable">response</span>.<span class="cm-variable">streaming</span>:</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">31</div></div><pre> <span class="cm-comment"># Delete the `Content-Length` header for streaming content, because</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">32</div></div><pre> <span class="cm-comment"># we won't know the compressed size until we stream it.</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">33</div></div><pre> <span class="cm-variable">response</span>.<span class="cm-variable">streaming_content</span> = <span class="cm-variable">compress_sequence</span>(<span class="cm-variable">response</span>.<span class="cm-variable">streaming_content</span>)</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">34</div></div><pre> <span class="cm-keyword">del</span> <span class="cm-variable">response</span>[<span class="cm-string">'Content-Length'</span>]</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">35</div></div><pre> <span class="cm-keyword">else</span>:</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">36</div></div><pre> <span class="cm-comment"># Return the compressed content only if it's actually shorter.</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">37</div></div><pre> <span class="cm-variable">compressed_content</span> = <span class="cm-variable">compress_string</span>(<span class="cm-variable">response</span>.<span class="cm-variable">content</span>)</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">38</div></div><pre> <span class="cm-keyword">if</span> <span class="cm-builtin">len</span>(<span class="cm-variable">compressed_content</span>) <span class="cm-operator">&gt;=</span> <span class="cm-builtin">len</span>(<span class="cm-variable">response</span>.<span class="cm-variable">content</span>):</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">39</div></div><pre> <span class="cm-keyword">return</span> <span class="cm-variable">response</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">40</div></div><pre> <span class="cm-variable">response</span>.<span class="cm-variable">content</span> = <span class="cm-variable">compressed_content</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">41</div></div><pre> <span class="cm-variable">response</span>[<span class="cm-string">'Content-Length'</span>] = <span class="cm-builtin">str</span>(<span class="cm-builtin">len</span>(<span class="cm-variable">response</span>.<span class="cm-variable">content</span>))</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">42</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">43</div></div><pre> <span class="cm-keyword">if</span> <span class="cm-variable">response</span>.<span class="cm-variable">has_header</span>(<span class="cm-string">'ETag'</span>):</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">44</div></div><pre> <span class="cm-variable">response</span>[<span class="cm-string">'ETag'</span>] = <span class="cm-variable">re</span>.<span class="cm-variable">sub</span>(<span class="cm-string">'"$'</span>, <span class="cm-string">';gzip"'</span>, <span class="cm-variable">response</span>[<span class="cm-string">'ETag'</span>])</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">45</div></div><pre> <span class="cm-variable">response</span>[<span class="cm-string">'Content-Encoding'</span>] = <span class="cm-string">'gzip'</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">46</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">47</div></div><pre> <span class="cm-keyword">return</span> <span class="cm-variable">response</span></pre></div></div><div class="CodeMirror-cursor" style="left: 189.4091796875px; top: 598px; height: 13px;">&nbsp;</div><div class="CodeMirror-cursor CodeMirror-secondarycursor" style="display: none;">&nbsp;</div></div></div></div></div><div style="position: absolute; height: 30px; width: 1px; top: 619px;"></div><div class="CodeMirror-gutters" style="height: 619px;"><div class="CodeMirror-gutter CodeMirror-linenumbers" style="width: 28px;"></div></div></div></div>
625 623 </div>
626 624 </div>
627 625
628 626
629 627
630 628
631 629
632 630 <!--
633 631 File Edit
634 632 -->
635 633
636 634 <h2>File Edit</h2>
637 635
638 636 <div class="codeblock">
639 637 <div class="code-header">
640 638 <div class="stats">
641 639 <i class="icon-file"></i>
642 640 <span class="item"><a href="/example/changeset/80ead1899f50a894889e19ffeb49c9cebf5bf045">r8248:80ead1899f50</a></span>
643 641 <span class="item">1.2 KiB</span>
644 642 <span class="item last">text/x-python</span>
645 643 <div class="buttons">
646 644 <a class="btn btn-mini" href="/example/changelog/80ead1899f50a894889e19ffeb49c9cebf5bf045/rhodecode/websetup.py">
647 645 <i class="icon-time"></i> history
648 646 </a>
649 647
650 648 <a class="btn btn-mini" href="/example/files/80ead1899f50a894889e19ffeb49c9cebf5bf045/rhodecode/websetup.py">source</a>
651 649 <a class="btn btn-mini" href="/example/raw/80ead1899f50a894889e19ffeb49c9cebf5bf045/rhodecode/websetup.py">raw</a>
652 650 <a class="btn btn-mini" href="/example/rawfile/80ead1899f50a894889e19ffeb49c9cebf5bf045/rhodecode/websetup.py">
653 651 <i class="icon-archive"></i> download
654 652 </a>
655 653 </div>
656 654 </div>
657 655 <div class="form">
658 656 <label for="set_mode">Editing file:</label>
659 657 rhodecode /
660 658 <input type="text" name="filename" value="websetup.py">
661 659
662 660 <div class="select2-container drop-menu" id="s2id_set_mode"><a href="javascript:void(0)" class="select2-choice" tabindex="-1"> <span class="select2-chosen" id="select2-chosen-2">plain</span><abbr class="select2-search-choice-close"></abbr> <span class="select2-arrow" role="presentation"><b role="presentation"></b></span></a><label for="s2id_autogen2" class="select2-offscreen">Editing file:</label><input class="select2-focusser select2-offscreen" type="text" aria-haspopup="true" role="button" aria-labelledby="select2-chosen-2" id="s2id_autogen2"><div class="select2-drop select2-display-none drop-menu-dropdown select2-with-searchbox"> <div class="select2-search"> <label for="s2id_autogen2_search" class="select2-offscreen">Editing file:</label> <input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" class="select2-input" role="combobox" aria-expanded="true" aria-autocomplete="list" aria-owns="select2-results-2" id="s2id_autogen2_search" placeholder=""> </div> <ul class="select2-results" role="listbox" id="select2-results-2"> </ul></div></div><select id="set_mode" name="set_mode" tabindex="-1" title="Editing file:" style="display: none;">
663 661 <option selected="selected" value="plain">plain</option>
664 662 <option value="apl">APL</option><option value="asterisk">Asterisk</option><option value="clike">C</option><option value="clike">C++</option><option value="cobol">Cobol</option><option value="clike">Java</option><option value="clike">C#</option><option value="clike">Scala</option><option value="clojure">Clojure</option><option value="coffeescript">CoffeeScript</option><option value="commonlisp">Common Lisp</option><option value="css">CSS</option><option value="d">D</option><option value="diff">diff</option><option value="dtd">DTD</option><option value="dylan">Dylan</option><option value="ecl">ECL</option><option value="eiffel">Eiffel</option><option value="erlang">Erlang</option><option value="fortran">Fortran</option><option value="mllike">F#</option><option value="gas">Gas</option><option value="go">GO</option><option value="gherkin">Gherkin</option><option value="go">Go</option><option value="groovy">Groovy</option><option value="haml">HAML</option><option value="haskell">Haskell</option><option value="haxe">Haxe</option><option value="htmlembedded">ASP.NET</option><option value="htmlembedded">Embedded Javascript</option><option value="htmlembedded">JavaServer Pages</option><option value="htmlmixed">HTML</option><option value="http">HTTP</option><option value="jade">Jade</option><option value="javascript">JavaScript</option><option value="javascript">JSON</option><option value="javascript">TypeScript</option><option value="jinja2">Jinja2</option><option value="julia">Julia</option><option value="less">LESS</option><option value="livescript">LiveScript</option><option value="lua">Lua</option><option value="markdown">Markdown (GitHub-flavour)</option><option value="mirc">mIRC</option><option value="nginx">Nginx</option><option value="ntriples">NTriples</option><option value="ocaml">OCaml</option><option value="mllike">OCaml</option><option value="octave">Octave</option><option value="pascal">Pascal</option><option value="pegjs">PEG.js</option><option value="perl">Perl</option><option value="php">PHP</option><option value="pig">Pig</option><option value="null">Plain Text</option><option value="properties">Properties files</option><option value="python" selected="selected">Python</option><option value="puppet">Puppet</option><option value="r">R</option><option value="rst">reStructuredText</option><option value="ruby">Ruby</option><option value="rust">Rust</option><option value="sass">Sass</option><option value="scheme">Scheme</option><option value="css">SCSS</option><option value="shell">Shell</option><option value="sieve">Sieve</option><option value="smalltalk">Smalltalk</option><option value="smarty">Smarty</option><option value="smartymixed">SmartyMixed</option><option value="solr">Solr</option><option value="sparql">SPARQL</option><option value="sql">SQL</option><option value="sql">MariaDB</option><option value="stex">sTeX</option><option value="stex">LaTeX</option><option value="verilog">SystemVerilog</option><option value="tcl">Tcl</option><option value="tiddlywiki">TiddlyWiki </option><option value="tiki">Tiki wiki</option><option value="toml">TOML</option><option value="turtle">Turtle</option><option value="vb">VB.NET</option><option value="vbscript">VBScript</option><option value="velocity">Velocity</option><option value="verilog">Verilog</option><option value="xml">XML</option><option value="xml">HTML</option><option value="xquery">XQuery</option><option value="yaml">YAML</option><option value="z80">Z80</option></select>
665 663 <script>
666 664 $(document).ready(function() {
667 665 $('#set_mode').select2({
668 666 containerCssClass: 'drop-menu',
669 667 dropdownCssClass: 'drop-menu-dropdown',
670 668 dropdownAutoWidth: true
671 669 });
672 670 });
673 671 </script>
674 672
675 673 <label for="line_wrap">line wraps</label>
676 674 <div class="select2-container drop-menu" id="s2id_line_wrap"><a href="javascript:void(0)" class="select2-choice" tabindex="-1"> <span class="select2-chosen" id="select2-chosen-3">off</span><abbr class="select2-search-choice-close"></abbr> <span class="select2-arrow" role="presentation"><b role="presentation"></b></span></a><label for="s2id_autogen3" class="select2-offscreen">line wraps</label><input class="select2-focusser select2-offscreen" type="text" aria-haspopup="true" role="button" aria-labelledby="select2-chosen-3" id="s2id_autogen3"><div class="select2-drop select2-display-none drop-menu-dropdown"> <div class="select2-search select2-search-hidden select2-offscreen"> <label for="s2id_autogen3_search" class="select2-offscreen">line wraps</label> <input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" class="select2-input" role="combobox" aria-expanded="true" aria-autocomplete="list" aria-owns="select2-results-3" id="s2id_autogen3_search" placeholder=""> </div> <ul class="select2-results" role="listbox" id="select2-results-3"> </ul></div></div><select id="line_wrap" name="line_wrap" tabindex="-1" title="line wraps" style="display: none;">
677 675 <option value="on">on</option>
678 676 <option selected="selected" value="off">off</option>
679 677 </select>
680 678 <script>
681 679 $(document).ready(function() {
682 680 $('#line_wrap').select2({
683 681 containerCssClass: 'drop-menu',
684 682 dropdownCssClass: 'drop-menu-dropdown',
685 683 dropdownAutoWidth: true,
686 684 minimumResultsForSearch: -1
687 685
688 686 });
689 687 });
690 688 </script>
691 689
692 690 <div id="render_preview" class="btn btn-mini hidden disabled">Preview</div>
693 691 </div>
694 692 </div>
695 693 <div id="editor_container">
696 694 <pre id="editor_pre"></pre>
697 695 <textarea id="editor" name="content" style="display: none;"># -*- coding: utf-8 -*-
698 696
699 697 # Published under Commercial License.
700 698 # Read the full license text at https://rhodecode.com/licenses.
701 699 """
702 700 rhodecode.websetup
703 701 ~~~~~~~~~~~~~~~~~~
704 702
705 703 Weboperations and setup for rhodecode
706 704
707 705 :created_on: Dec 11, 2010
708 706 :author: marcink
709 707 :copyright: (c) 2013-2015 RhodeCode GmbH.
710 708 :license: Commercial License, see LICENSE for more details.
711 709 """
712 710
713 711 import logging
714 712
715 713 from rhodecode.config.environment import load_environment
716 714 from rhodecode.lib.db_manage import DbManage
717 715 from rhodecode.model.meta import Session
718 716
719 717
720 718 log = logging.getLogger(__name__)
721 719
722 720
723 721 def setup_app(command, conf, vars):
724 722 """Place any commands to setup rhodecode here"""
725 723 dbconf = conf['sqlalchemy.db1.url']
726 724 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=conf['here'],
727 725 tests=False, cli_args=command.options.__dict__)
728 726 dbmanage.create_tables(override=True)
729 727 dbmanage.set_db_version()
730 728 opts = dbmanage.config_prompt(None)
731 729 dbmanage.create_settings(opts)
732 730 dbmanage.create_default_user()
733 731 dbmanage.admin_prompt()
734 732 dbmanage.create_permissions()
735 733 dbmanage.populate_default_permissions()
736 734 Session().commit()
737 735 load_environment(conf.global_conf, conf.local_conf, initial=True)
738 736 </textarea><div class="CodeMirror cm-s-default CodeMirror-focused"><div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 5px; left: 34px;"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" style="position: absolute; padding: 0px; width: 1000px; height: 1em; outline: none;" tabindex="0"></textarea></div><div class="CodeMirror-hscrollbar" style="left: 29px; min-height: 18px;"><div style="height: 100%; min-height: 1px; width: 0px;"></div></div><div class="CodeMirror-vscrollbar" style="display: block; bottom: 0px; min-width: 18px;"><div style="min-width: 1px; height: 554px;"></div></div><div class="CodeMirror-scrollbar-filler"></div><div class="CodeMirror-gutter-filler"></div><div class="CodeMirror-scroll" tabindex="-1"><div class="CodeMirror-sizer" style="min-width: 579.350463867188px; margin-left: 29px; min-height: 554px;"><div style="position: relative; top: 0px;"><div class="CodeMirror-lines"><div style="position: relative; outline: none;"><div class="CodeMirror-measure"><div style="width: 50px; height: 50px; overflow-x: scroll;"></div></div><div style="position: relative; z-index: 1; display: none;"></div><div class="CodeMirror-code"><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">1</div></div><pre><span class="cm-comment"># -*- coding: utf-8 -*-</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">2</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">3</div></div><pre><span class="cm-comment"># Published under Commercial License.</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">4</div></div><pre><span class="cm-comment"># Read the full license text at https://rhodecode.com/licenses.</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">5</div></div><pre><span class="cm-string">"""</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">6</div></div><pre><span class="cm-string">rhodecode.websetup</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">7</div></div><pre><span class="cm-string">~~~~~~~~~~~~~~~~~~</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">8</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">9</div></div><pre><span class="cm-string">Weboperations and setup for rhodecode</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">10</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">11</div></div><pre><span class="cm-string">:created_on: Dec 11, 2010</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">12</div></div><pre><span class="cm-string">:author: marcink</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">13</div></div><pre><span class="cm-string">:copyright: (c) 2013-2015 RhodeCode GmbH.</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">14</div></div><pre><span class="cm-string">:license: Commercial License, see LICENSE for more details.</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">15</div></div><pre><span class="cm-string">"""</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">16</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">17</div></div><pre><span class="cm-keyword">import</span> <span class="cm-variable">logging</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">18</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">19</div></div><pre><span class="cm-keyword">from</span> <span class="cm-variable">rhodecode</span>.<span class="cm-variable">config</span>.<span class="cm-variable">environment</span> <span class="cm-keyword">import</span> <span class="cm-variable">load_environment</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">20</div></div><pre><span class="cm-keyword">from</span> <span class="cm-variable">rhodecode</span>.<span class="cm-variable">lib</span>.<span class="cm-variable">db_manage</span> <span class="cm-keyword">import</span> <span class="cm-variable">DbManage</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">21</div></div><pre><span class="cm-keyword">from</span> <span class="cm-variable">rhodecode</span>.<span class="cm-variable">model</span>.<span class="cm-variable">meta</span> <span class="cm-keyword">import</span> <span class="cm-variable">Session</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">22</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">23</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">24</div></div><pre><span class="cm-variable">log</span> = <span class="cm-variable">logging</span>.<span class="cm-variable">getLogger</span>(<span class="cm-variable">__name__</span>) # Intentionally long line to show what will happen if this line does not fit onto the screen. It might have some horizontal scrolling applied or some other fancy mechanism to deal with it.</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">25</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">26</div></div><pre>&nbsp;</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">27</div></div><pre><span class="cm-keyword">def</span> <span class="cm-def">setup_app</span>(<span class="cm-variable">command</span>, <span class="cm-variable">conf</span>, <span class="cm-builtin">vars</span>):</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">28</div></div><pre> <span class="cm-string">"""Place any commands to setup rhodecode here"""</span></pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">29</div></div><pre> <span class="cm-variable">dbconf</span> = <span class="cm-variable">conf</span>[<span class="cm-string">'sqlalchemy.db1.url'</span>]</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">30</div></div><pre> <span class="cm-variable">dbmanage</span> = <span class="cm-variable">DbManage</span>(<span class="cm-variable">log_sql</span>=<span class="cm-builtin">True</span>, <span class="cm-variable">dbconf</span>=<span class="cm-variable">dbconf</span>, <span class="cm-variable">root</span>=<span class="cm-variable">conf</span>[<span class="cm-string">'here'</span>],</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">31</div></div><pre> <span class="cm-variable">tests</span>=<span class="cm-builtin">False</span>, <span class="cm-variable">cli_args</span>=<span class="cm-variable">command</span>.<span class="cm-variable">options</span>.<span class="cm-variable">__dict__</span>)</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">32</div></div><pre> <span class="cm-variable">dbmanage</span>.<span class="cm-variable">create_tables</span>(<span class="cm-variable">override</span>=<span class="cm-builtin">True</span>)</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">33</div></div><pre> <span class="cm-variable">dbmanage</span>.<span class="cm-variable">set_db_version</span>()</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">34</div></div><pre> <span class="cm-variable">opts</span> = <span class="cm-variable">dbmanage</span>.<span class="cm-variable">config_prompt</span>(<span class="cm-builtin">None</span>)</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">35</div></div><pre> <span class="cm-variable">dbmanage</span>.<span class="cm-variable">create_settings</span>(<span class="cm-variable">opts</span>)</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">36</div></div><pre> <span class="cm-variable">dbmanage</span>.<span class="cm-variable">create_default_user</span>()</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">37</div></div><pre> <span class="cm-variable">dbmanage</span>.<span class="cm-variable">admin_prompt</span>()</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">38</div></div><pre> <span class="cm-variable">dbmanage</span>.<span class="cm-variable">create_permissions</span>()</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">39</div></div><pre> <span class="cm-variable">dbmanage</span>.<span class="cm-variable">populate_default_permissions</span>()</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">40</div></div><pre> <span class="cm-variable">Session</span>().<span class="cm-variable">commit</span>()</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">41</div></div><pre> <span class="cm-variable">load_environment</span>(<span class="cm-variable">conf</span>.<span class="cm-variable">global_conf</span>, <span class="cm-variable">conf</span>.<span class="cm-variable">local_conf</span>, <span class="cm-variable">initial</span>=<span class="cm-builtin">True</span>)</pre></div><div style="position: relative;"><div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;"><div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">42</div></div><pre>&nbsp;</pre></div></div><div class="CodeMirror-cursor" style="left: 4px; top: 0px; height: 13px;">&nbsp;</div><div class="CodeMirror-cursor CodeMirror-secondarycursor" style="display: none;">&nbsp;</div></div></div></div></div><div style="position: absolute; height: 30px; width: 1px; top: 554px;"></div><div class="CodeMirror-gutters" style="height: 554px;"><div class="CodeMirror-gutter CodeMirror-linenumbers" style="width: 28px;"></div></div></div></div>
739 737 <div id="editor_preview"></div>
740 738 </div>
741 739 <div class="message">
742 740 <label class="codeblock-label">Commit Message</label>
743 741 <textarea id="commit" name="message" placeholder="Edited file rhodecode/websetup.py via RhodeCode"></textarea>
744 742 </div>
745 743 </div>
746 744
747 745
748 746
749 747
750 748
751 749
752 750 <!--
753 751 Commit with comments
754 752 -->
755 753
756 754 <h2>Commit with comments</h2>
757 755
758 756 <div class="diff-container" id="diff-container-140360037209920">
759 757 <div id="c-4e5ee86997c6-7046e4320b26_target"></div>
760 758 <div id="c-4e5ee86997c6-7046e4320b26" class="diffblock margined comm">
761 759 <div class="code-header">
762 760 <div title="Go back to changed files overview">
763 761 <a href="#changes_box">
764 762 <i class="icon-circle-arrow-up"></i>
765 763 </a>
766 764 </div>
767 765 <div class="changeset_header">
768 766 <div class="changeset_file">
769 767 <i class="icon-file"></i>
770 768 <a href="/andersonsantos/rhodecode-dev-fork/files/4e5ee86997c64981d85cf62283af448624e26929/rhodecode/tests/functional/test_compare_local.py">rhodecode/tests/functional/test_compare_local.py</a>
771 769 </div>
772 770 <div class="diff-actions">
773 771 <a href="/andersonsantos/rhodecode-dev-fork/diff/rhodecode/tests/functional/test_compare_local.py?fulldiff=1&amp;diff1=682135c2e3958d7c84db06d716efe482bd3ce7c6&amp;diff=diff&amp;diff2=4e5ee86997c64981d85cf62283af448624e26929" class="tooltip" title="Show full diff for this file">
774 772 <img class="icon" src="/images/icons/page_white_go.png">
775 773 </a>
776 774 <a href="/andersonsantos/rhodecode-dev-fork/diff-2way/rhodecode/tests/functional/test_compare_local.py?fulldiff=1&amp;diff1=682135c2e3958d7c84db06d716efe482bd3ce7c6&amp;diff=diff&amp;diff2=4e5ee86997c64981d85cf62283af448624e26929" class="tooltip" title="Show full side-by-side diff for this file">
777 775 <img class="icon" src="/images/icons/application_double.png">
778 776 </a>
779 777 <a href="/andersonsantos/rhodecode-dev-fork/diff/rhodecode/tests/functional/test_compare_local.py?diff1=682135c2e3958d7c84db06d716efe482bd3ce7c6&amp;diff=raw&amp;diff2=4e5ee86997c64981d85cf62283af448624e26929" class="tooltip" title="Raw diff">
780 778 <img class="icon" src="/images/icons/page_white.png">
781 779 </a>
782 780 <a href="/andersonsantos/rhodecode-dev-fork/diff/rhodecode/tests/functional/test_compare_local.py?diff1=682135c2e3958d7c84db06d716efe482bd3ce7c6&amp;diff=download&amp;diff2=4e5ee86997c64981d85cf62283af448624e26929" class="tooltip" title="Download diff">
783 781 <img class="icon" src="/images/icons/page_save.png">
784 782 </a>
785 783 <a class="tooltip" href="/andersonsantos/rhodecode-dev-fork/changeset/4e5ee86997c64981d85cf62283af448624e26929?c-4e5ee86997c6-7046e4320b26=WS%3A1&amp;c-4e5ee86997c6-7046e4320b26=C%3A3#c-4e5ee86997c6-7046e4320b26" title="Ignore white space"><img alt="Ignore white space" class="icon" src="/images/icons/text_strikethrough.png"></a>
786 784 <a class="tooltip" href="/andersonsantos/rhodecode-dev-fork/changeset/4e5ee86997c64981d85cf62283af448624e26929?c-4e5ee86997c6-7046e4320b26=C%3A6#c-4e5ee86997c6-7046e4320b26" title="increase diff context to 6 lines"><img alt="increase diff context to 6 lines" class="icon" src="/images/icons/table_add.png"></a>
787 785 </div>
788 786 <span>
789 787 <label>
790 788 Show inline comments
791 789 <input checked="checked" class="show-inline-comments" id="" id_for="c-4e5ee86997c6-7046e4320b26" name="" type="checkbox" value="1">
792 790 </label>
793 791 </span>
794 792 </div>
795 793 </div>
796 794 <div class="code-body">
797 795 <div class="full_f_path" path="rhodecode/tests/functional/test_compare_local.py"></div>
798 796 <table class="code-difftable">
799 797 <tbody><tr class="line context">
800 798 <td class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o...">...</a></td>
801 799 <td class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n...">...</a></td>
802 800 <td class="code ">
803 801 <pre>@@ -59,7 +59,7 @@
804 802 </pre>
805 803 </td>
806 804 </tr>
807 805 <tr class="line unmod">
808 806 <td id="rhodecodetestsfunctionaltest_compare_localpy_o59" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o59">59</a></td>
809 807 <td id="rhodecodetestsfunctionaltest_compare_localpy_n59" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n59">59</a></td>
810 808 <td class="code ">
811 809 <pre> 'tag': 'v0.2.0',
812 810 </pre>
813 811 </td>
814 812 </tr>
815 813 <tr class="line unmod">
816 814 <td id="rhodecodetestsfunctionaltest_compare_localpy_o60" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o60">60</a></td>
817 815 <td id="rhodecodetestsfunctionaltest_compare_localpy_n60" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n60">60</a></td>
818 816 <td class="code ">
819 817 <pre> 'branch': 'default',
820 818 </pre>
821 819 </td>
822 820 </tr>
823 821 <tr class="line unmod">
824 822 <td id="rhodecodetestsfunctionaltest_compare_localpy_o61" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o61">61</a></td>
825 823 <td id="rhodecodetestsfunctionaltest_compare_localpy_n61" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n61">61</a></td>
826 824 <td class="code ">
827 825 <pre> 'response': # Intentionally long line to show what will happen if this line does not fit onto the screen. It might have some horizontal scrolling applied or some other fancy mechanism to deal with it.
828 826 </pre>
829 827 </td>
830 828 </tr>
831 829 <tr class="line del">
832 830 <td id="rhodecodetestsfunctionaltest_compare_localpy_o62" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o62">62</a></td>
833 831 <td class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n"></a></td>
834 832 <td class="code ">
835 833 <pre> '147 files changed: 5700 inserted, 10176 deleted'
836 834 </pre>
837 835 </td>
838 836 </tr>
839 837 <tr class="line add">
840 838 <td class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o"></a></td>
841 839 <td id="rhodecodetestsfunctionaltest_compare_localpy_n62" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n62">62</a></td>
842 840 <td class="code ">
843 841 <pre><ins> </ins> '147 files changed: 5700 inserted, 10176 deleted'
844 842 </pre>
845 843 </td>
846 844 </tr>
847 845 <tr class="line unmod">
848 846 <td id="rhodecodetestsfunctionaltest_compare_localpy_o63" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o63">63</a></td>
849 847 <td id="rhodecodetestsfunctionaltest_compare_localpy_n63" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n63">63</a></td>
850 848 <td class="code ">
851 849 <pre> },
852 850 </pre>
853 851 </td>
854 852 </tr>
855 853 <tr class="line unmod">
856 854 <td id="rhodecodetestsfunctionaltest_compare_localpy_o64" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o64">64</a></td>
857 855 <td id="rhodecodetestsfunctionaltest_compare_localpy_n64" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n64">64</a></td>
858 856 <td class="code ">
859 857 <pre> 'git': {
860 858 </pre>
861 859 </td>
862 860 </tr>
863 861 <tr class="line unmod">
864 862 <td id="rhodecodetestsfunctionaltest_compare_localpy_o65" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o65">65</a></td>
865 863 <td id="rhodecodetestsfunctionaltest_compare_localpy_n65" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n65">65</a></td>
866 864 <td class="code ">
867 865 <pre> 'tag': 'v0.2.2',
868 866 </pre>
869 867 </td>
870 868 </tr>
871 869 <tr class="line context">
872 870 <td class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o...">...</a></td>
873 871 <td class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n...">...</a></td>
874 872 <td class="code ">
875 873 <pre>@@ -77,9 +77,11 @@
876 874 </pre>
877 875 </td>
878 876 </tr>
879 877 <tr class="line unmod">
880 878 <td id="rhodecodetestsfunctionaltest_compare_localpy_o77" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o77">77</a></td>
881 879 <td id="rhodecodetestsfunctionaltest_compare_localpy_n77" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n77">77</a></td>
882 880 <td class="code ">
883 881 <pre> target_ref=revisions[backend.alias]['tag'],
884 882 </pre>
885 883 </td>
886 884 </tr>
887 885 <tr class="line unmod">
888 886 <td id="rhodecodetestsfunctionaltest_compare_localpy_o78" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o78">78</a></td>
889 887 <td id="rhodecodetestsfunctionaltest_compare_localpy_n78" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n78">78</a></td>
890 888 <td class="code ">
891 889 <pre> ))
892 890 </pre>
893 891 </td>
894 892 </tr><tr id="comment-tr-3754" class="inline-comments"><td></td><td></td><td>
895 893
896 894 <div class="comment" id="comment-3754" line="n78">
897 895 <div class="comment-wrapp">
898 896 <div class="meta">
899 897 <span class="gravatar">
900 898 <img src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=40" height="20" width="20">
901 899 </span>
902 900 <span class="user">
903 901 anderson
904 902 </span>
905 903 <span class="date">
906 904 just now |
907 905 </span>
908 906 <span class="status-change">
909 907 Comment on commit
910 908 </span>
911 909 <a class="permalink" href="#comment-3754"></a>
912 910 </div>
913 911 <div class="text">
914 912 <div class="rst-block"><p>commented line
915 913 with multiple lines</p>
916 914 </div>
917 915 </div>
918 916 </div>
919 917 </div><div class="add-comment"><span class="btn btn-default">Add another comment</span></div>
920 918
921 919 </td></tr>
922 920 <tr class="line unmod">
923 921 <td id="rhodecodetestsfunctionaltest_compare_localpy_o79" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o79">79</a></td>
924 922 <td id="rhodecodetestsfunctionaltest_compare_localpy_n79" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n79">79</a></td>
925 923 <td class="code ">
926 924 <pre></pre>
927 925 </td>
928 926 </tr>
929 927 <tr class="line del form-open hl-comment">
930 928 <td id="rhodecodetestsfunctionaltest_compare_localpy_o80" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o80">80</a></td>
931 929 <td class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n"></a></td>
932 930 <td class="code ">
933 931 <pre> response.mustcontain('%s@%s' % (<del>backend.repo_name,</del>
934 932 </pre>
935 933 </td>
936 934 </tr><tr id="comment-tr-undefined" class="comment-form-inline"><td></td><td></td><td>
937 935 <div class="comment-inline-form ac">
938 936 <div class="overlay"><div class="overlay-text">Submitting...</div></div>
939 937 <form action="#" class="inline-form" method="get">
940 938 <div id="edit-container_o80" class="clearfix">
941 939 <div class="comment-title pull-left">
942 940 Commenting on line o80.
943 941 </div>
944 942 <div class="comment-help pull-right">
945 943 Comments parsed using <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">RST</a> syntax with <span class="tooltip" title="Use @username inside this text to send notification to this RhodeCode user">@mention</span> support.
946 944 </div>
947 945 <div style="clear: both"></div>
948 946 <textarea id="text_o80" name="text" class="comment-block-ta ac-input" autocomplete="off"></textarea>
949 947 </div>
950 948 <div id="preview-container_o80" class="clearfix" style="display: none;">
951 949 <div class="comment-help">
952 950 Comment preview
953 951 </div>
954 952 <div id="preview-box_o80" class="preview-box"></div>
955 953 </div>
956 954 <div class="comment-button pull-right">
957 955 <input type="hidden" name="f_path" value="rhodecode/tests/functional/test_compare_local.py">
958 956 <input type="hidden" name="line" value="o80">
959 957 <div id="preview-btn_o80" class="btn btn-default">Preview</div>
960 958 <div id="edit-btn_o80" class="btn" style="display: none;">Edit</div>
961 959 <input class="btn btn-success save-inline-form" id="save" name="save" type="submit" value="Comment">
962 960 </div>
963 961 <div class="comment-button hide-inline-form-button">
964 962 <input class="btn hide-inline-form" id="hide-inline-form" name="hide-inline-form" type="reset" value="Cancel">
965 963 </div>
966 964 </form>
967 965 </div>
968 966 </td></tr>
969 967 <tr class="line add">
970 968 <td class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o"></a></td>
971 969 <td id="rhodecodetestsfunctionaltest_compare_localpy_n80" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n80">80</a></td>
972 970 <td class="code ">
973 971 <pre> response.mustcontain('%s@%s' % (
974 972 </pre>
975 973 </td>
976 974 </tr>
977 975 <tr class="line add">
978 976 <td class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o"></a></td>
979 977 <td id="rhodecodetestsfunctionaltest_compare_localpy_n81" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n81">81</a></td>
980 978 <td class="code ">
981 979 <pre> backend.repo_name,
982 980 </pre>
983 981 </td>
984 982 </tr>
985 983 <tr class="line unmod">
986 984 <td id="rhodecodetestsfunctionaltest_compare_localpy_o81" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o81">81</a></td>
987 985 <td id="rhodecodetestsfunctionaltest_compare_localpy_n82" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n82">82</a></td>
988 986 <td class="code ">
989 987 <pre> revisions[backend.alias]['branch']))
990 988 </pre>
991 989 </td>
992 990 </tr>
993 991 <tr class="line del">
994 992 <td id="rhodecodetestsfunctionaltest_compare_localpy_o82" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o82">82</a></td>
995 993 <td class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n"></a></td>
996 994 <td class="code ">
997 995 <pre> response.mustcontain('%s@%s' % (<del>backend.repo_name,</del>
998 996 </pre>
999 997 </td>
1000 998 </tr>
1001 999 <tr class="line add">
1002 1000 <td class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o"></a></td>
1003 1001 <td id="rhodecodetestsfunctionaltest_compare_localpy_n83" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n83">83</a></td>
1004 1002 <td class="code ">
1005 1003 <pre> response.mustcontain('%s@%s' % (
1006 1004 </pre>
1007 1005 </td>
1008 1006 </tr>
1009 1007 <tr class="line add">
1010 1008 <td class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o"></a></td>
1011 1009 <td id="rhodecodetestsfunctionaltest_compare_localpy_n84" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n84">84</a></td>
1012 1010 <td class="code ">
1013 1011 <pre> backend.repo_name,
1014 1012 </pre>
1015 1013 </td>
1016 1014 </tr>
1017 1015 <tr class="line unmod">
1018 1016 <td id="rhodecodetestsfunctionaltest_compare_localpy_o83" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o83">83</a></td>
1019 1017 <td id="rhodecodetestsfunctionaltest_compare_localpy_n85" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n85">85</a></td>
1020 1018 <td class="code ">
1021 1019 <pre> revisions[backend.alias]['tag']))
1022 1020 </pre>
1023 1021 </td>
1024 1022 </tr>
1025 1023 <tr class="line unmod">
1026 1024 <td id="rhodecodetestsfunctionaltest_compare_localpy_o84" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o84">84</a></td>
1027 1025 <td id="rhodecodetestsfunctionaltest_compare_localpy_n86" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n86">86</a></td>
1028 1026 <td class="code ">
1029 1027 <pre> response.mustcontain(revisions[backend.alias]['response'])
1030 1028 </pre>
1031 1029 </td>
1032 1030 </tr>
1033 1031 <tr class="line unmod">
1034 1032 <td id="rhodecodetestsfunctionaltest_compare_localpy_o85" class="lineno old"><a href="#rhodecodetestsfunctionaltest_compare_localpy_o85">85</a></td>
1035 1033 <td id="rhodecodetestsfunctionaltest_compare_localpy_n87" class="lineno new"><a href="#rhodecodetestsfunctionaltest_compare_localpy_n87">87</a></td>
1036 1034 <td class="code ">
1037 1035 <pre></pre>
1038 1036 </td>
1039 1037 </tr>
1040 1038 </tbody></table>
1041 1039 </div>
1042 1040 </div>
1043 1041 </div>
1044 1042
1045 1043
1046 1044
1047 1045 <!--
1048 1046 Side-by-side diff
1049 1047 -->
1050 1048
1051 1049 <h2>Side-by-side diff</h2>
1052 1050
1053 1051 <div class="box">
1054 1052 <div class="diff-container" style="overflow-x: hidden">
1055 1053 <div class="diffblock comm" style="margin:3px; padding:1px">
1056 1054 <div class="code-header">
1057 1055 <div class="changeset_header">
1058 1056 <div class="changeset_file">
1059 1057 <i class="icon-file"></i>
1060 1058 <a href="/pygments/files/ea295cfb622620f5ba13e226ec531e3fe5296399/tests/test_basic_api.py">tests/test_basic_api.py</a>
1061 1059 [mode: <span id="selected_mode">python</span>]
1062 1060 </div>
1063 1061 <div class="diff-actions">
1064 1062 <a href="/pygments/diff/tests/test_basic_api.py?diff2=ea295cfb622620f5ba13e226ec531e3fe5296399&amp;diff=diff&amp;diff1=de45f950b669e2d991c4ba512fa6fe450c6616db&amp;fulldiff=1" class="tooltip" title="Show full diff for this file">
1065 1063 <img class="icon" src="/images/icons/page_white_go.png">
1066 1064 </a>
1067 1065 <a href="/pygments/diff-2way/tests/test_basic_api.py?diff2=ea295cfb622620f5ba13e226ec531e3fe5296399&amp;diff=diff&amp;diff1=de45f950b669e2d991c4ba512fa6fe450c6616db&amp;fulldiff=1" class="tooltip" title="Show full side-by-side diff for this file" tt_title="Show full side-by-side diff for this file">
1068 1066 <img class="icon" src="/images/icons/application_double.png">
1069 1067 </a>
1070 1068 <a href="/pygments/diff/tests/test_basic_api.py?diff2=ea295cfb622620f5ba13e226ec531e3fe5296399&amp;diff1=de45f950b669e2d991c4ba512fa6fe450c6616db&amp;diff=raw" class="tooltip" title="Raw diff">
1071 1069 <img class="icon" src="/images/icons/page_white.png">
1072 1070 </a>
1073 1071 <a href="/pygments/diff/tests/test_basic_api.py?diff2=ea295cfb622620f5ba13e226ec531e3fe5296399&amp;diff1=de45f950b669e2d991c4ba512fa6fe450c6616db&amp;diff=download" class="tooltip" title="Download diff">
1074 1072 <img class="icon" src="/images/icons/page_save.png">
1075 1073 </a>
1076 1074 <label><input id="ignorews" name="ignorews" type="checkbox" value="1">ignore white space</label>
1077 1075 <label><input id="edit_mode" name="edit_mode" type="checkbox" value="1">turn on edit mode</label>
1078 1076
1079 1077 </div>
1080 1078 <div style="float: right; padding: 0px 10px 0px 0px">
1081 1079 r1538:de45f950b669 ... r1539:ea295cfb6226
1082 1080 </div>
1083 1081 </div>
1084 1082 </div>
1085 1083 <div id="compare"></div>
1086 1084 </div>
1087 1085 </div>
1088 1086
1089 1087 <script>
1090 1088 $(document).ready(function () {
1091 1089 var example_lines = '1\n2\n3\n4\n5\n6\n7\n8\n9\n \n';
1092 1090
1093 1091 $('#compare').mergely({
1094 1092 width: 'auto',
1095 1093 height: '600',
1096 1094 fgcolor: {a:'#ddffdd',c:'#cccccc',d:'#ffdddd'},
1097 1095 bgcolor: '#fff',
1098 1096 viewport: true,
1099 1097 cmsettings: {mode: 'text/plain', readOnly: true, lineWrapping: false, lineNumbers: true},
1100 1098 lhs: function(setValue) {
1101 1099 if("False" == "True"){
1102 1100 setValue('Binary file')
1103 1101 }
1104 1102 else if("MercurialCommit" == "EmptyCommit"){
1105 1103 setValue('');
1106 1104 }
1107 1105 else{
1108 1106 var left_value = example_lines.slice(0, 10) +
1109 1107 '123456789 '.repeat(10) +
1110 1108 '\n'+
1111 1109 example_lines.slice(10, 20);
1112 1110 setValue(left_value + example_lines.repeat(9));
1113 1111 }
1114 1112
1115 1113 },
1116 1114 rhs: function(setValue) {
1117 1115 if("False" == "True"){
1118 1116 setValue('Binary file')
1119 1117 }
1120 1118 else if("MercurialCommit" == "EmptyCommit"){
1121 1119 setValue('');
1122 1120 }
1123 1121 else{
1124 1122 var right_value = example_lines +
1125 1123 example_lines.slice(0, 8) +
1126 1124 'abcdefghi '.repeat(10) +
1127 1125 '\n'+
1128 1126 example_lines.slice(8, 20);
1129 1127 setValue(right_value + example_lines.repeat(9));
1130 1128 }
1131 1129 },
1132 1130 });
1133 1131
1134 1132 var detected_mode = detectCodeMirrorModeFromExt('test_basic_api.py', true);
1135 1133 if(detected_mode){
1136 1134 setCodeMirrorMode($('#compare').mergely('cm', 'lhs'), detected_mode);
1137 1135 setCodeMirrorMode($('#compare').mergely('cm', 'rhs'), detected_mode);
1138 1136 $('#selected_mode').html(detected_mode);
1139 1137 }
1140 1138
1141 1139 $('#ignorews').change(function(e){
1142 1140 var val = e.currentTarget.checked;
1143 1141 $('#compare').mergely('options', {ignorews: val});
1144 1142 $('#compare').mergely('update');
1145 1143 })
1146 1144 $('#edit_mode').change(function(e){
1147 1145 var val = !e.currentTarget.checked;
1148 1146 $('#compare').mergely('cm', 'lhs').setOption('readOnly', val);
1149 1147 $('#compare').mergely('cm', 'rhs').setOption('readOnly', val);
1150 1148 $('#compare').mergely('update');
1151 1149 })
1152 1150 });
1153 1151 </script>
1154 1152
1155 1153 </div>
1156 1154
1157 1155 <!-- end examples -->
1158 1156
1159 1157 </div>
1160 1158 </div>
1161 1159 </div>
1162 1160 </%def>
@@ -1,286 +1,324 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title(*args)">
4 4 ${_('%s Files') % c.repo_name}
5 5 %if hasattr(c,'file'):
6 6 &middot; ${h.safe_unicode(c.file.path) or '\\'}
7 7 %endif
8 8
9 9 %if c.rhodecode_name:
10 10 &middot; ${h.branding(c.rhodecode_name)}
11 11 %endif
12 12 </%def>
13 13
14 14 <%def name="breadcrumbs_links()">
15 15 ${_('Files')}
16 16 %if c.file:
17 17 @ ${h.show_id(c.commit)}
18 18 %endif
19 19 </%def>
20 20
21 21 <%def name="menu_bar_nav()">
22 22 ${self.menu_items(active='repositories')}
23 23 </%def>
24 24
25 25 <%def name="menu_bar_subnav()">
26 26 ${self.repo_menu(active='files')}
27 27 </%def>
28 28
29 29 <%def name="main()">
30 30 <div class="title">
31 31 ${self.repo_page_title(c.rhodecode_db_repo)}
32 32 </div>
33 33
34 34 <div id="pjax-container" class="summary">
35 35 <div id="files_data">
36 36 <%include file='files_pjax.html'/>
37 37 </div>
38 38 </div>
39 39 <script>
40 40 var curState = {
41 41 commit_id: "${c.commit.raw_id}"
42 42 };
43 43
44 44 var getState = function(context) {
45 45 var url = $(location).attr('href');
46 46 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
47 47 var _annotate_url = '${h.url("files_annotate_home",repo_name=c.repo_name,revision='',f_path='')}';
48 48 _base_url = _base_url.replace('//', '/');
49 49 _annotate_url = _annotate_url.replace('//', '/');
50 50
51 51 //extract f_path from url.
52 52 var parts = url.split(_base_url);
53 53 if (parts.length != 2) {
54 54 parts = url.split(_annotate_url);
55 55 if (parts.length != 2) {
56 56 var rev = "tip";
57 57 var f_path = "";
58 58 } else {
59 59 var parts2 = parts[1].split('/');
60 60 var rev = parts2.shift(); // pop the first element which is the revision
61 61 var f_path = parts2.join('/');
62 62 }
63 63
64 64 } else {
65 65 var parts2 = parts[1].split('/');
66 66 var rev = parts2.shift(); // pop the first element which is the revision
67 67 var f_path = parts2.join('/');
68 68 }
69 69
70 70 var _node_list_url = pyroutes.url('files_nodelist_home',
71 71 {repo_name: templateContext.repo_name,
72 72 revision: rev, f_path: f_path});
73 73 var _url_base = pyroutes.url('files_home',
74 74 {repo_name: templateContext.repo_name,
75 75 revision: rev, f_path:'__FPATH__'});
76 76 return {
77 77 url: url,
78 78 f_path: f_path,
79 79 rev: rev,
80 80 commit_id: curState.commit_id,
81 81 node_list_url: _node_list_url,
82 82 url_base: _url_base
83 83 };
84 84 };
85 85
86 86 var metadataRequest = null;
87 87 var getFilesMetadata = function() {
88 88 if (metadataRequest && metadataRequest.readyState != 4) {
89 89 metadataRequest.abort();
90 90 }
91 91 if (fileSourcePage) {
92 92 return false;
93 93 }
94 94
95 95 if ($('#file-tree-wrapper').hasClass('full-load')) {
96 96 // in case our HTML wrapper has full-load class we don't
97 97 // trigger the async load of metadata
98 98 return false;
99 99 }
100 100
101 101 var state = getState('metadata');
102 102 var url_data = {
103 103 'repo_name': templateContext.repo_name,
104 104 'commit_id': state.commit_id,
105 105 'f_path': state.f_path
106 106 };
107 107
108 108 var url = pyroutes.url('files_nodetree_full', url_data);
109 109
110 110 metadataRequest = $.ajax({url: url});
111 111
112 112 metadataRequest.done(function(data) {
113 113 $('#file-tree').html(data);
114 114 timeagoActivate();
115 115 });
116 116 metadataRequest.fail(function (data, textStatus, errorThrown) {
117 117 console.log(data);
118 118 if (data.status != 0) {
119 119 alert("Error while fetching metadata.\nError code {0} ({1}).Please consider reloading the page".format(data.status,data.statusText));
120 120 }
121 121 });
122 122 };
123 123
124 124 var callbacks = function() {
125 125 var state = getState('callbacks');
126 126 timeagoActivate();
127 127
128 128 // used for history, and switch to
129 129 var initialCommitData = {
130 130 id: null,
131 text: '${_("Switch To Commit")}',
131 text: '${_("Pick Commit")}',
132 132 type: 'sha',
133 133 raw_id: null,
134 134 files_url: null
135 135 };
136 136
137 137 if ($('#trimmed_message_box').height() < 50) {
138 138 $('#message_expand').hide();
139 139 }
140 140
141 141 $('#message_expand').on('click', function(e) {
142 142 $('#trimmed_message_box').css('max-height', 'none');
143 143 $(this).hide();
144 144 });
145 145
146 146 if (fileSourcePage) {
147 147 // variants for with source code, not tree view
148 148
149 149 // select code link event
150 150 $("#hlcode").mouseup(getSelectionLink);
151 151
152 152 // file history select2
153 153 select2FileHistorySwitcher('#diff1', initialCommitData, state);
154
155 // show at, diff to actions handlers
154 156 $('#diff1').on('change', function(e) {
155 $('#diff').removeClass('disabled').removeAttr("disabled");
156 $('#show_rev').removeClass('disabled').removeAttr("disabled");
157 $('#diff_to_commit').removeClass('disabled').removeAttr("disabled");
158 $('#diff_to_commit').val(_gettext('Diff to Commit ') + e.val.truncateAfter(8, '...'));
159
160 $('#show_at_commit').removeClass('disabled').removeAttr("disabled");
161 $('#show_at_commit').val(_gettext('Show at Commit ') + e.val.truncateAfter(8, '...'));
162 });
163
164 $('#diff_to_commit').on('click', function(e) {
165 var diff1 = $('#diff1').val();
166 var diff2 = $('#diff2').val();
167
168 var url_data = {
169 repo_name: templateContext.repo_name,
170 source_ref: diff1,
171 source_ref_type: 'rev',
172 target_ref: diff2,
173 target_ref_type: 'rev',
174 merge: 1,
175 f_path: state.f_path
176 };
177 window.location = pyroutes.url('compare_url', url_data);
178 });
179
180 $('#show_at_commit').on('click', function(e) {
181 var diff1 = $('#diff1').val();
182
183 var annotate = $('#annotate').val();
184 if (annotate === "True") {
185 var url = pyroutes.url('files_annotate_home',
186 {'repo_name': templateContext.repo_name,
187 'revision': diff1, 'f_path': state.f_path});
188 } else {
189 var url = pyroutes.url('files_home',
190 {'repo_name': templateContext.repo_name,
191 'revision': diff1, 'f_path': state.f_path});
192 }
193 window.location = url;
194
157 195 });
158 196
159 197 // show more authors
160 198 $('#show_authors').on('click', function(e) {
161 199 e.preventDefault();
162 200 var url = pyroutes.url('files_authors_home',
163 201 {'repo_name': templateContext.repo_name,
164 202 'revision': state.rev, 'f_path': state.f_path});
165 203
166 204 $.pjax({
167 205 url: url,
168 206 data: 'annotate=${"1" if c.annotate else "0"}',
169 207 container: '#file_authors',
170 208 push: false,
171 209 timeout: pjaxTimeout
172 210 }).complete(function(){
173 211 $('#show_authors').hide();
174 212 })
175 213 });
176 214
177 215 // load file short history
178 216 $('#file_history_overview').on('click', function(e) {
179 217 e.preventDefault();
180 218 path = state.f_path;
181 219 if (path.indexOf("#") >= 0) {
182 220 path = path.slice(0, path.indexOf("#"));
183 221 }
184 222 var url = pyroutes.url('changelog_file_home',
185 223 {'repo_name': templateContext.repo_name,
186 224 'revision': state.rev, 'f_path': path, 'limit': 6});
187 225 $('#file_history_container').show();
188 226 $('#file_history_container').html('<div class="file-history-inner">{0}</div>'.format(_gettext('Loading ...')));
189 227
190 228 $.pjax({
191 229 url: url,
192 230 container: '#file_history_container',
193 231 push: false,
194 232 timeout: pjaxTimeout
195 233 })
196 234 });
197 235
198 236 }
199 237 else {
200 238 getFilesMetadata();
201 239
202 240 // fuzzy file filter
203 241 fileBrowserListeners(state.node_list_url, state.url_base);
204 242
205 243 // switch to widget
206 244 select2RefSwitcher('#refs_filter', initialCommitData);
207 245 $('#refs_filter').on('change', function(e) {
208 246 var data = $('#refs_filter').select2('data');
209 247 curState.commit_id = data.raw_id;
210 248 $.pjax({url: data.files_url, container: '#pjax-container', timeout: pjaxTimeout});
211 249 });
212 250
213 251 $("#prev_commit_link").on('click', function(e) {
214 252 var data = $(this).data();
215 253 curState.commit_id = data.commitId;
216 254 });
217 255
218 256 $("#next_commit_link").on('click', function(e) {
219 257 var data = $(this).data();
220 258 curState.commit_id = data.commitId;
221 259 });
222 260
223 261 $('#at_rev').on("keypress", function(e) {
224 262 /* ENTER PRESSED */
225 263 if (e.keyCode === 13) {
226 264 var rev = $('#at_rev').val();
227 265 // explicit reload page here. with pjax entering bad input
228 266 // produces not so nice results
229 267 window.location = pyroutes.url('files_home',
230 268 {'repo_name': templateContext.repo_name,
231 269 'revision': rev, 'f_path': state.f_path});
232 270 }
233 271 });
234 272 }
235 273 };
236 274
237 275 var pjaxTimeout = 5000;
238 276
239 277 $(document).pjax(".pjax-link", "#pjax-container", {
240 278 "fragment": "#pjax-content",
241 279 "maxCacheLength": 1000,
242 280 "timeout": pjaxTimeout
243 281 });
244 282
245 283 // define global back/forward states
246 284 var isPjaxPopState = false;
247 285 $(document).on('pjax:popstate', function() {
248 286 isPjaxPopState = true;
249 287 });
250 288
251 289 $(document).on('pjax:end', function(xhr, options) {
252 290 if (isPjaxPopState) {
253 291 isPjaxPopState = false;
254 292 callbacks();
255 293 _NODEFILTER.resetFilter();
256 294 }
257 295
258 296 // run callback for tracking if defined for google analytics etc.
259 297 // this is used to trigger tracking on pjax
260 298 if (typeof window.rhodecode_statechange_callback !== 'undefined') {
261 299 var state = getState('statechange');
262 300 rhodecode_statechange_callback(state.url, null)
263 301 }
264 302 });
265 303
266 304 $(document).on('pjax:success', function(event, xhr, options) {
267 305 if (event.target.id == "file_history_container") {
268 306 $('#file_history_overview').hide();
269 307 $('#file_history_overview_full').show();
270 308 timeagoActivate();
271 309 } else {
272 310 callbacks();
273 311 }
274 312 });
275 313
276 314 $(document).ready(function() {
277 315 callbacks();
278 316 var search_GET = "${request.GET.get('search','')}";
279 317 if (search_GET == "1") {
280 318 _NODEFILTER.initFilter();
281 319 }
282 320 });
283 321
284 322 </script>
285 323
286 324 </%def>
@@ -1,62 +1,74 b''
1 1 <%namespace name="file_base" file="/files/base.html"/>
2 2
3 3 <div class="fieldset collapsable-content no-hide" data-toggle="summary-details">
4 4 <div class="left-label">
5 5 ${_('Commit Description')}:
6 6 </div>
7 7 <div class="commit right-content truncate-wrap">${h.urlify_commit_message(h.chop_at_smart(c.commit.message, '\n', suffix_if_chopped='...'), c.repo_name)}</div>
8 8 </div>
9 9
10 10 <div class="fieldset collapsable-content" data-toggle="summary-details">
11 11 <div class="left-label">
12 12 ${_('Commit Description')}:
13 13 </div>
14 14 <div class="commit right-content">${h.urlify_commit_message(c.commit.message,c.repo_name)}</div>
15 15 </div>
16 16
17 17
18 18 <div class="fieldset " data-toggle="summary-details">
19 19 <div class="left-label">
20 20 ${_('References')}:
21 21 </div>
22 22 <div class="right-content">
23 23 <div class="tags tags-main">
24 24 <code>
25 25 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=c.commit.raw_id)}">${h.show_id(c.commit)}</a>
26 26 </code>
27 27
28 28 ${file_base.refs(c.commit)}
29 29 </div>
30 30 </div>
31 31 </div>
32 32
33 33 <div class="fieldset collapsable-content" data-toggle="summary-details">
34 34 <div class="left-label">
35 35 ${_('File last commit')}:
36 36 </div>
37 37 <div class="right-content">
38 38 <div class="tags">
39 39 <code>
40 40 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=c.file_last_commit.raw_id)}">${h.show_id(c.file_last_commit)}</a>
41 41 </code>
42 42
43 43 ${file_base.refs(c.file_last_commit)}
44 44 </div>
45 45 </div>
46 46 </div>
47 47
48 48
49 <div id="node_history" class="file_diff_buttons collapsable-content" data-toggle="summary-details">
50 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
49 <div class="fieldset collapsable-content" data-toggle="summary-details">
50 <div class="left-label">
51 ${_('Show/Diff file')}:
52 </div>
53 <div class="right-content">
51 54 ${h.hidden('diff1')}
52 ${h.hidden('diff2',c.file_last_commit.raw_id)}
55 ${h.hidden('diff2',c.commit.raw_id)}
56 ${h.hidden('annotate', c.annotate)}
57 </div>
58 </div>
53 59
54 ${h.submit('diff',_('Diff to Commit'),class_="btn disabled",disabled="true")}
55 ${h.submit('show_rev',_('Show at Commit'),class_="btn disabled",disabled="true")}
56 ${h.hidden('annotate', c.annotate)}
57 ${h.end_form()}
60
61 <div class="fieldset collapsable-content" data-toggle="summary-details">
62 <div class="left-label">
63 ${_('Action')}:
58 64 </div>
65 <div class="right-content">
66 ${h.submit('diff_to_commit',_('Diff to Commit'),class_="btn disabled",disabled="true")}
67 ${h.submit('show_at_commit',_('Show at Commit'),class_="btn disabled",disabled="true")}
68 </div>
69 </div>
70
59 71
60 72 <script>
61 73 collapsableContent();
62 74 </script> No newline at end of file
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (1669 lines changed) Show them Hide them
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now