##// END OF EJS Templates
ui: links to tags/branches/bookmarks render that in a context of summary page for better navigation.
super-admin -
r4977:184e6a08 default
parent child Browse files
Show More
@@ -1,840 +1,858 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import time
22 22 import logging
23 23 import operator
24 24
25 25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPBadRequest
26 26
27 27 from rhodecode.lib import helpers as h, diffs, rc_cache
28 28 from rhodecode.lib.utils import repo_name_slug
29 29 from rhodecode.lib.utils2 import (
30 30 StrictAttributeDict, str2bool, safe_int, datetime_to_time, safe_unicode)
31 31 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
32 32 from rhodecode.lib.vcs.backends.base import EmptyCommit
33 33 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
34 34 from rhodecode.model import repo
35 35 from rhodecode.model import repo_group
36 36 from rhodecode.model import user_group
37 37 from rhodecode.model import user
38 38 from rhodecode.model.db import User
39 39 from rhodecode.model.scm import ScmModel
40 40 from rhodecode.model.settings import VcsSettingsModel, IssueTrackerSettingsModel
41 41 from rhodecode.model.repo import ReadmeFinder
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45
46 46 ADMIN_PREFIX = '/_admin'
47 47 STATIC_FILE_PREFIX = '/_static'
48 48
49 49 URL_NAME_REQUIREMENTS = {
50 50 # group name can have a slash in them, but they must not end with a slash
51 51 'group_name': r'.*?[^/]',
52 52 'repo_group_name': r'.*?[^/]',
53 53 # repo names can have a slash in them, but they must not end with a slash
54 54 'repo_name': r'.*?[^/]',
55 55 # file path eats up everything at the end
56 56 'f_path': r'.*',
57 57 # reference types
58 58 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
59 59 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
60 60 }
61 61
62 62
63 63 def add_route_with_slash(config,name, pattern, **kw):
64 64 config.add_route(name, pattern, **kw)
65 65 if not pattern.endswith('/'):
66 66 config.add_route(name + '_slash', pattern + '/', **kw)
67 67
68 68
69 69 def add_route_requirements(route_path, requirements=None):
70 70 """
71 71 Adds regex requirements to pyramid routes using a mapping dict
72 72 e.g::
73 73 add_route_requirements('{repo_name}/settings')
74 74 """
75 75 requirements = requirements or URL_NAME_REQUIREMENTS
76 76 for key, regex in requirements.items():
77 77 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
78 78 return route_path
79 79
80 80
81 81 def get_format_ref_id(repo):
82 82 """Returns a `repo` specific reference formatter function"""
83 83 if h.is_svn(repo):
84 84 return _format_ref_id_svn
85 85 else:
86 86 return _format_ref_id
87 87
88 88
89 89 def _format_ref_id(name, raw_id):
90 90 """Default formatting of a given reference `name`"""
91 91 return name
92 92
93 93
94 94 def _format_ref_id_svn(name, raw_id):
95 95 """Special way of formatting a reference for Subversion including path"""
96 96 return '%s@%s' % (name, raw_id)
97 97
98 98
99 99 class TemplateArgs(StrictAttributeDict):
100 100 pass
101 101
102 102
103 103 class BaseAppView(object):
104 104
105 105 def __init__(self, context, request):
106 106 self.request = request
107 107 self.context = context
108 108 self.session = request.session
109 109 if not hasattr(request, 'user'):
110 110 # NOTE(marcink): edge case, we ended up in matched route
111 111 # but probably of web-app context, e.g API CALL/VCS CALL
112 112 if hasattr(request, 'vcs_call') or hasattr(request, 'rpc_method'):
113 113 log.warning('Unable to process request `%s` in this scope', request)
114 114 raise HTTPBadRequest()
115 115
116 116 self._rhodecode_user = request.user # auth user
117 117 self._rhodecode_db_user = self._rhodecode_user.get_instance()
118 118 self._maybe_needs_password_change(
119 119 request.matched_route.name, self._rhodecode_db_user)
120 120
121 121 def _maybe_needs_password_change(self, view_name, user_obj):
122 122
123 123 dont_check_views = [
124 124 'channelstream_connect',
125 125 'ops_ping'
126 126 ]
127 127 if view_name in dont_check_views:
128 128 return
129 129
130 130 log.debug('Checking if user %s needs password change on view %s',
131 131 user_obj, view_name)
132 132
133 133 skip_user_views = [
134 134 'logout', 'login',
135 135 'my_account_password', 'my_account_password_update'
136 136 ]
137 137
138 138 if not user_obj:
139 139 return
140 140
141 141 if user_obj.username == User.DEFAULT_USER:
142 142 return
143 143
144 144 now = time.time()
145 145 should_change = user_obj.user_data.get('force_password_change')
146 146 change_after = safe_int(should_change) or 0
147 147 if should_change and now > change_after:
148 148 log.debug('User %s requires password change', user_obj)
149 149 h.flash('You are required to change your password', 'warning',
150 150 ignore_duplicate=True)
151 151
152 152 if view_name not in skip_user_views:
153 153 raise HTTPFound(
154 154 self.request.route_path('my_account_password'))
155 155
156 156 def _log_creation_exception(self, e, repo_name):
157 157 _ = self.request.translate
158 158 reason = None
159 159 if len(e.args) == 2:
160 160 reason = e.args[1]
161 161
162 162 if reason == 'INVALID_CERTIFICATE':
163 163 log.exception(
164 164 'Exception creating a repository: invalid certificate')
165 165 msg = (_('Error creating repository %s: invalid certificate')
166 166 % repo_name)
167 167 else:
168 168 log.exception("Exception creating a repository")
169 169 msg = (_('Error creating repository %s')
170 170 % repo_name)
171 171 return msg
172 172
173 173 def _get_local_tmpl_context(self, include_app_defaults=True):
174 174 c = TemplateArgs()
175 175 c.auth_user = self.request.user
176 176 # TODO(marcink): migrate the usage of c.rhodecode_user to c.auth_user
177 177 c.rhodecode_user = self.request.user
178 178
179 179 if include_app_defaults:
180 180 from rhodecode.lib.base import attach_context_attributes
181 181 attach_context_attributes(c, self.request, self.request.user.user_id)
182 182
183 183 c.is_super_admin = c.auth_user.is_admin
184 184
185 185 c.can_create_repo = c.is_super_admin
186 186 c.can_create_repo_group = c.is_super_admin
187 187 c.can_create_user_group = c.is_super_admin
188 188
189 189 c.is_delegated_admin = False
190 190
191 191 if not c.auth_user.is_default and not c.is_super_admin:
192 192 c.can_create_repo = h.HasPermissionAny('hg.create.repository')(
193 193 user=self.request.user)
194 194 repositories = c.auth_user.repositories_admin or c.can_create_repo
195 195
196 196 c.can_create_repo_group = h.HasPermissionAny('hg.repogroup.create.true')(
197 197 user=self.request.user)
198 198 repository_groups = c.auth_user.repository_groups_admin or c.can_create_repo_group
199 199
200 200 c.can_create_user_group = h.HasPermissionAny('hg.usergroup.create.true')(
201 201 user=self.request.user)
202 202 user_groups = c.auth_user.user_groups_admin or c.can_create_user_group
203 203 # delegated admin can create, or manage some objects
204 204 c.is_delegated_admin = repositories or repository_groups or user_groups
205 205 return c
206 206
207 207 def _get_template_context(self, tmpl_args, **kwargs):
208 208
209 209 local_tmpl_args = {
210 210 'defaults': {},
211 211 'errors': {},
212 212 'c': tmpl_args
213 213 }
214 214 local_tmpl_args.update(kwargs)
215 215 return local_tmpl_args
216 216
217 217 def load_default_context(self):
218 218 """
219 219 example:
220 220
221 221 def load_default_context(self):
222 222 c = self._get_local_tmpl_context()
223 223 c.custom_var = 'foobar'
224 224
225 225 return c
226 226 """
227 227 raise NotImplementedError('Needs implementation in view class')
228 228
229 229
230 230 class RepoAppView(BaseAppView):
231 231
232 232 def __init__(self, context, request):
233 233 super(RepoAppView, self).__init__(context, request)
234 234 self.db_repo = request.db_repo
235 235 self.db_repo_name = self.db_repo.repo_name
236 236 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
237 237 self.db_repo_artifacts = ScmModel().get_artifacts(self.db_repo)
238 238 self.db_repo_patterns = IssueTrackerSettingsModel(repo=self.db_repo)
239 239
240 240 def _handle_missing_requirements(self, error):
241 241 log.error(
242 242 'Requirements are missing for repository %s: %s',
243 243 self.db_repo_name, safe_unicode(error))
244 244
245 def _prepare_and_set_clone_url(self, c):
246 username = ''
247 if self._rhodecode_user.username != User.DEFAULT_USER:
248 username = self._rhodecode_user.username
249
250 _def_clone_uri = c.clone_uri_tmpl
251 _def_clone_uri_id = c.clone_uri_id_tmpl
252 _def_clone_uri_ssh = c.clone_uri_ssh_tmpl
253
254 c.clone_repo_url = self.db_repo.clone_url(
255 user=username, uri_tmpl=_def_clone_uri)
256 c.clone_repo_url_id = self.db_repo.clone_url(
257 user=username, uri_tmpl=_def_clone_uri_id)
258 c.clone_repo_url_ssh = self.db_repo.clone_url(
259 uri_tmpl=_def_clone_uri_ssh, ssh=True)
260
245 261 def _get_local_tmpl_context(self, include_app_defaults=True):
246 262 _ = self.request.translate
247 263 c = super(RepoAppView, self)._get_local_tmpl_context(
248 264 include_app_defaults=include_app_defaults)
249 265
250 266 # register common vars for this type of view
251 267 c.rhodecode_db_repo = self.db_repo
252 268 c.repo_name = self.db_repo_name
253 269 c.repository_pull_requests = self.db_repo_pull_requests
254 270 c.repository_artifacts = self.db_repo_artifacts
255 271 c.repository_is_user_following = ScmModel().is_following_repo(
256 272 self.db_repo_name, self._rhodecode_user.user_id)
257 273 self.path_filter = PathFilter(None)
258 274
259 275 c.repository_requirements_missing = {}
260 276 try:
261 277 self.rhodecode_vcs_repo = self.db_repo.scm_instance()
262 278 # NOTE(marcink):
263 279 # comparison to None since if it's an object __bool__ is expensive to
264 280 # calculate
265 281 if self.rhodecode_vcs_repo is not None:
266 282 path_perms = self.rhodecode_vcs_repo.get_path_permissions(
267 283 c.auth_user.username)
268 284 self.path_filter = PathFilter(path_perms)
269 285 except RepositoryRequirementError as e:
270 286 c.repository_requirements_missing = {'error': str(e)}
271 287 self._handle_missing_requirements(e)
272 288 self.rhodecode_vcs_repo = None
273 289
274 290 c.path_filter = self.path_filter # used by atom_feed_entry.mako
275 291
276 292 if self.rhodecode_vcs_repo is None:
277 293 # unable to fetch this repo as vcs instance, report back to user
278 294 log.debug('Repository was not found on filesystem, check if it exists or is not damaged')
279 295 h.flash(_(
280 296 "The repository `%(repo_name)s` cannot be loaded in filesystem. "
281 297 "Please check if it exist, or is not damaged.") %
282 298 {'repo_name': c.repo_name},
283 299 category='error', ignore_duplicate=True)
284 300 if c.repository_requirements_missing:
285 301 route = self.request.matched_route.name
286 302 if route.startswith(('edit_repo', 'repo_summary')):
287 303 # allow summary and edit repo on missing requirements
288 304 return c
289 305
290 306 raise HTTPFound(
291 307 h.route_path('repo_summary', repo_name=self.db_repo_name))
292 308
293 309 else: # redirect if we don't show missing requirements
294 310 raise HTTPFound(h.route_path('home'))
295 311
296 312 c.has_origin_repo_read_perm = False
297 313 if self.db_repo.fork:
298 314 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
299 315 'repository.write', 'repository.read', 'repository.admin')(
300 316 self.db_repo.fork.repo_name, 'summary fork link')
301 317
302 318 return c
303 319
304 320 def _get_f_path_unchecked(self, matchdict, default=None):
305 321 """
306 322 Should only be used by redirects, everything else should call _get_f_path
307 323 """
308 324 f_path = matchdict.get('f_path')
309 325 if f_path:
310 326 # fix for multiple initial slashes that causes errors for GIT
311 327 return f_path.lstrip('/')
312 328
313 329 return default
314 330
315 331 def _get_f_path(self, matchdict, default=None):
316 332 f_path_match = self._get_f_path_unchecked(matchdict, default)
317 333 return self.path_filter.assert_path_permissions(f_path_match)
318 334
319 335 def _get_general_setting(self, target_repo, settings_key, default=False):
320 336 settings_model = VcsSettingsModel(repo=target_repo)
321 337 settings = settings_model.get_general_settings()
322 338 return settings.get(settings_key, default)
323 339
324 340 def _get_repo_setting(self, target_repo, settings_key, default=False):
325 341 settings_model = VcsSettingsModel(repo=target_repo)
326 342 settings = settings_model.get_repo_settings_inherited()
327 343 return settings.get(settings_key, default)
328 344
329 345 def _get_readme_data(self, db_repo, renderer_type, commit_id=None, path='/'):
330 346 log.debug('Looking for README file at path %s', path)
331 347 if commit_id:
332 348 landing_commit_id = commit_id
333 349 else:
334 350 landing_commit = db_repo.get_landing_commit()
335 351 if isinstance(landing_commit, EmptyCommit):
336 352 return None, None
337 353 landing_commit_id = landing_commit.raw_id
338 354
339 355 cache_namespace_uid = 'cache_repo.{}'.format(db_repo.repo_id)
340 356 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
341 357 start = time.time()
342 358
343 359 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid)
344 360 def generate_repo_readme(repo_id, _commit_id, _repo_name, _readme_search_path, _renderer_type):
345 361 readme_data = None
346 362 readme_filename = None
347 363
348 364 commit = db_repo.get_commit(_commit_id)
349 365 log.debug("Searching for a README file at commit %s.", _commit_id)
350 366 readme_node = ReadmeFinder(_renderer_type).search(commit, path=_readme_search_path)
351 367
352 368 if readme_node:
353 369 log.debug('Found README node: %s', readme_node)
354 370 relative_urls = {
355 371 'raw': h.route_path(
356 372 'repo_file_raw', repo_name=_repo_name,
357 373 commit_id=commit.raw_id, f_path=readme_node.path),
358 374 'standard': h.route_path(
359 375 'repo_files', repo_name=_repo_name,
360 376 commit_id=commit.raw_id, f_path=readme_node.path),
361 377 }
378
362 379 readme_data = self._render_readme_or_none(commit, readme_node, relative_urls)
363 380 readme_filename = readme_node.unicode_path
364 381
365 382 return readme_data, readme_filename
366 383
367 384 readme_data, readme_filename = generate_repo_readme(
368 385 db_repo.repo_id, landing_commit_id, db_repo.repo_name, path, renderer_type,)
386
369 387 compute_time = time.time() - start
370 388 log.debug('Repo README for path %s generated and computed in %.4fs',
371 389 path, compute_time)
372 390 return readme_data, readme_filename
373 391
374 392 def _render_readme_or_none(self, commit, readme_node, relative_urls):
375 393 log.debug('Found README file `%s` rendering...', readme_node.path)
376 394 renderer = MarkupRenderer()
377 395 try:
378 396 html_source = renderer.render(
379 397 readme_node.content, filename=readme_node.path)
380 398 if relative_urls:
381 399 return relative_links(html_source, relative_urls)
382 400 return html_source
383 401 except Exception:
384 402 log.exception(
385 403 "Exception while trying to render the README")
386 404
387 405 def get_recache_flag(self):
388 406 for flag_name in ['force_recache', 'force-recache', 'no-cache']:
389 407 flag_val = self.request.GET.get(flag_name)
390 408 if str2bool(flag_val):
391 409 return True
392 410 return False
393 411
394 412 def get_commit_preload_attrs(cls):
395 413 pre_load = ['author', 'branch', 'date', 'message', 'parents',
396 414 'obsolete', 'phase', 'hidden']
397 415 return pre_load
398 416
399 417
400 418 class PathFilter(object):
401 419
402 420 # Expects and instance of BasePathPermissionChecker or None
403 421 def __init__(self, permission_checker):
404 422 self.permission_checker = permission_checker
405 423
406 424 def assert_path_permissions(self, path):
407 425 if self.path_access_allowed(path):
408 426 return path
409 427 raise HTTPForbidden()
410 428
411 429 def path_access_allowed(self, path):
412 430 log.debug('Checking ACL permissions for PathFilter for `%s`', path)
413 431 if self.permission_checker:
414 432 has_access = path and self.permission_checker.has_access(path)
415 433 log.debug('ACL Permissions checker enabled, ACL Check has_access: %s', has_access)
416 434 return has_access
417 435
418 436 log.debug('ACL permissions checker not enabled, skipping...')
419 437 return True
420 438
421 439 def filter_patchset(self, patchset):
422 440 if not self.permission_checker or not patchset:
423 441 return patchset, False
424 442 had_filtered = False
425 443 filtered_patchset = []
426 444 for patch in patchset:
427 445 filename = patch.get('filename', None)
428 446 if not filename or self.permission_checker.has_access(filename):
429 447 filtered_patchset.append(patch)
430 448 else:
431 449 had_filtered = True
432 450 if had_filtered:
433 451 if isinstance(patchset, diffs.LimitedDiffContainer):
434 452 filtered_patchset = diffs.LimitedDiffContainer(patchset.diff_limit, patchset.cur_diff_size, filtered_patchset)
435 453 return filtered_patchset, True
436 454 else:
437 455 return patchset, False
438 456
439 457 def render_patchset_filtered(self, diffset, patchset, source_ref=None, target_ref=None):
440 458 filtered_patchset, has_hidden_changes = self.filter_patchset(patchset)
441 459 result = diffset.render_patchset(
442 460 filtered_patchset, source_ref=source_ref, target_ref=target_ref)
443 461 result.has_hidden_changes = has_hidden_changes
444 462 return result
445 463
446 464 def get_raw_patch(self, diff_processor):
447 465 if self.permission_checker is None:
448 466 return diff_processor.as_raw()
449 467 elif self.permission_checker.has_full_access:
450 468 return diff_processor.as_raw()
451 469 else:
452 470 return '# Repository has user-specific filters, raw patch generation is disabled.'
453 471
454 472 @property
455 473 def is_enabled(self):
456 474 return self.permission_checker is not None
457 475
458 476
459 477 class RepoGroupAppView(BaseAppView):
460 478 def __init__(self, context, request):
461 479 super(RepoGroupAppView, self).__init__(context, request)
462 480 self.db_repo_group = request.db_repo_group
463 481 self.db_repo_group_name = self.db_repo_group.group_name
464 482
465 483 def _get_local_tmpl_context(self, include_app_defaults=True):
466 484 _ = self.request.translate
467 485 c = super(RepoGroupAppView, self)._get_local_tmpl_context(
468 486 include_app_defaults=include_app_defaults)
469 487 c.repo_group = self.db_repo_group
470 488 return c
471 489
472 490 def _revoke_perms_on_yourself(self, form_result):
473 491 _updates = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
474 492 form_result['perm_updates'])
475 493 _additions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
476 494 form_result['perm_additions'])
477 495 _deletions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
478 496 form_result['perm_deletions'])
479 497 admin_perm = 'group.admin'
480 498 if _updates and _updates[0][1] != admin_perm or \
481 499 _additions and _additions[0][1] != admin_perm or \
482 500 _deletions and _deletions[0][1] != admin_perm:
483 501 return True
484 502 return False
485 503
486 504
487 505 class UserGroupAppView(BaseAppView):
488 506 def __init__(self, context, request):
489 507 super(UserGroupAppView, self).__init__(context, request)
490 508 self.db_user_group = request.db_user_group
491 509 self.db_user_group_name = self.db_user_group.users_group_name
492 510
493 511
494 512 class UserAppView(BaseAppView):
495 513 def __init__(self, context, request):
496 514 super(UserAppView, self).__init__(context, request)
497 515 self.db_user = request.db_user
498 516 self.db_user_id = self.db_user.user_id
499 517
500 518 _ = self.request.translate
501 519 if not request.db_user_supports_default:
502 520 if self.db_user.username == User.DEFAULT_USER:
503 521 h.flash(_("Editing user `{}` is disabled.".format(
504 522 User.DEFAULT_USER)), category='warning')
505 523 raise HTTPFound(h.route_path('users'))
506 524
507 525
508 526 class DataGridAppView(object):
509 527 """
510 528 Common class to have re-usable grid rendering components
511 529 """
512 530
513 531 def _extract_ordering(self, request, column_map=None):
514 532 column_map = column_map or {}
515 533 column_index = safe_int(request.GET.get('order[0][column]'))
516 534 order_dir = request.GET.get(
517 535 'order[0][dir]', 'desc')
518 536 order_by = request.GET.get(
519 537 'columns[%s][data][sort]' % column_index, 'name_raw')
520 538
521 539 # translate datatable to DB columns
522 540 order_by = column_map.get(order_by) or order_by
523 541
524 542 search_q = request.GET.get('search[value]')
525 543 return search_q, order_by, order_dir
526 544
527 545 def _extract_chunk(self, request):
528 546 start = safe_int(request.GET.get('start'), 0)
529 547 length = safe_int(request.GET.get('length'), 25)
530 548 draw = safe_int(request.GET.get('draw'))
531 549 return draw, start, length
532 550
533 551 def _get_order_col(self, order_by, model):
534 552 if isinstance(order_by, str):
535 553 try:
536 554 return operator.attrgetter(order_by)(model)
537 555 except AttributeError:
538 556 return None
539 557 else:
540 558 return order_by
541 559
542 560
543 561 class BaseReferencesView(RepoAppView):
544 562 """
545 563 Base for reference view for branches, tags and bookmarks.
546 564 """
547 565 def load_default_context(self):
548 566 c = self._get_local_tmpl_context()
549 567 return c
550 568
551 569 def load_refs_context(self, ref_items, partials_template):
552 570 _render = self.request.get_partial_renderer(partials_template)
553 571 pre_load = ["author", "date", "message", "parents"]
554 572
555 573 is_svn = h.is_svn(self.rhodecode_vcs_repo)
556 574 is_hg = h.is_hg(self.rhodecode_vcs_repo)
557 575
558 576 format_ref_id = get_format_ref_id(self.rhodecode_vcs_repo)
559 577
560 578 closed_refs = {}
561 579 if is_hg:
562 580 closed_refs = self.rhodecode_vcs_repo.branches_closed
563 581
564 582 data = []
565 583 for ref_name, commit_id in ref_items:
566 584 commit = self.rhodecode_vcs_repo.get_commit(
567 585 commit_id=commit_id, pre_load=pre_load)
568 586 closed = ref_name in closed_refs
569 587
570 588 # TODO: johbo: Unify generation of reference links
571 589 use_commit_id = '/' in ref_name or is_svn
572 590
573 591 if use_commit_id:
574 592 files_url = h.route_path(
575 593 'repo_files',
576 594 repo_name=self.db_repo_name,
577 595 f_path=ref_name if is_svn else '',
578 596 commit_id=commit_id,
579 597 _query=dict(at=ref_name)
580 598 )
581 599
582 600 else:
583 601 files_url = h.route_path(
584 602 'repo_files',
585 603 repo_name=self.db_repo_name,
586 604 f_path=ref_name if is_svn else '',
587 605 commit_id=ref_name,
588 606 _query=dict(at=ref_name)
589 607 )
590 608
591 609 data.append({
592 610 "name": _render('name', ref_name, files_url, closed),
593 611 "name_raw": ref_name,
594 612 "date": _render('date', commit.date),
595 613 "date_raw": datetime_to_time(commit.date),
596 614 "author": _render('author', commit.author),
597 615 "commit": _render(
598 616 'commit', commit.message, commit.raw_id, commit.idx),
599 617 "commit_raw": commit.idx,
600 618 "compare": _render(
601 619 'compare', format_ref_id(ref_name, commit.raw_id)),
602 620 })
603 621
604 622 return data
605 623
606 624
607 625 class RepoRoutePredicate(object):
608 626 def __init__(self, val, config):
609 627 self.val = val
610 628
611 629 def text(self):
612 630 return 'repo_route = %s' % self.val
613 631
614 632 phash = text
615 633
616 634 def __call__(self, info, request):
617 635 if hasattr(request, 'vcs_call'):
618 636 # skip vcs calls
619 637 return
620 638
621 639 repo_name = info['match']['repo_name']
622 640
623 641 repo_name_parts = repo_name.split('/')
624 642 repo_slugs = [x for x in map(lambda x: repo_name_slug(x), repo_name_parts)]
625 643
626 644 if repo_name_parts != repo_slugs:
627 645 # short-skip if the repo-name doesn't follow slug rule
628 646 log.warning('repo_name: %s is different than slug %s', repo_name_parts, repo_slugs)
629 647 return False
630 648
631 649 repo_model = repo.RepoModel()
632 650
633 651 by_name_match = repo_model.get_by_repo_name(repo_name, cache=False)
634 652
635 653 def redirect_if_creating(route_info, db_repo):
636 654 skip_views = ['edit_repo_advanced_delete']
637 655 route = route_info['route']
638 656 # we should skip delete view so we can actually "remove" repositories
639 657 # if they get stuck in creating state.
640 658 if route.name in skip_views:
641 659 return
642 660
643 661 if db_repo.repo_state in [repo.Repository.STATE_PENDING]:
644 662 repo_creating_url = request.route_path(
645 663 'repo_creating', repo_name=db_repo.repo_name)
646 664 raise HTTPFound(repo_creating_url)
647 665
648 666 if by_name_match:
649 667 # register this as request object we can re-use later
650 668 request.db_repo = by_name_match
651 669 redirect_if_creating(info, by_name_match)
652 670 return True
653 671
654 672 by_id_match = repo_model.get_repo_by_id(repo_name)
655 673 if by_id_match:
656 674 request.db_repo = by_id_match
657 675 redirect_if_creating(info, by_id_match)
658 676 return True
659 677
660 678 return False
661 679
662 680
663 681 class RepoForbidArchivedRoutePredicate(object):
664 682 def __init__(self, val, config):
665 683 self.val = val
666 684
667 685 def text(self):
668 686 return 'repo_forbid_archived = %s' % self.val
669 687
670 688 phash = text
671 689
672 690 def __call__(self, info, request):
673 691 _ = request.translate
674 692 rhodecode_db_repo = request.db_repo
675 693
676 694 log.debug(
677 695 '%s checking if archived flag for repo for %s',
678 696 self.__class__.__name__, rhodecode_db_repo.repo_name)
679 697
680 698 if rhodecode_db_repo.archived:
681 699 log.warning('Current view is not supported for archived repo:%s',
682 700 rhodecode_db_repo.repo_name)
683 701
684 702 h.flash(
685 703 h.literal(_('Action not supported for archived repository.')),
686 704 category='warning')
687 705 summary_url = request.route_path(
688 706 'repo_summary', repo_name=rhodecode_db_repo.repo_name)
689 707 raise HTTPFound(summary_url)
690 708 return True
691 709
692 710
693 711 class RepoTypeRoutePredicate(object):
694 712 def __init__(self, val, config):
695 713 self.val = val or ['hg', 'git', 'svn']
696 714
697 715 def text(self):
698 716 return 'repo_accepted_type = %s' % self.val
699 717
700 718 phash = text
701 719
702 720 def __call__(self, info, request):
703 721 if hasattr(request, 'vcs_call'):
704 722 # skip vcs calls
705 723 return
706 724
707 725 rhodecode_db_repo = request.db_repo
708 726
709 727 log.debug(
710 728 '%s checking repo type for %s in %s',
711 729 self.__class__.__name__, rhodecode_db_repo.repo_type, self.val)
712 730
713 731 if rhodecode_db_repo.repo_type in self.val:
714 732 return True
715 733 else:
716 734 log.warning('Current view is not supported for repo type:%s',
717 735 rhodecode_db_repo.repo_type)
718 736 return False
719 737
720 738
721 739 class RepoGroupRoutePredicate(object):
722 740 def __init__(self, val, config):
723 741 self.val = val
724 742
725 743 def text(self):
726 744 return 'repo_group_route = %s' % self.val
727 745
728 746 phash = text
729 747
730 748 def __call__(self, info, request):
731 749 if hasattr(request, 'vcs_call'):
732 750 # skip vcs calls
733 751 return
734 752
735 753 repo_group_name = info['match']['repo_group_name']
736 754
737 755 repo_group_name_parts = repo_group_name.split('/')
738 756 repo_group_slugs = [x for x in map(lambda x: repo_name_slug(x), repo_group_name_parts)]
739 757 if repo_group_name_parts != repo_group_slugs:
740 758 # short-skip if the repo-name doesn't follow slug rule
741 759 log.warning('repo_group_name: %s is different than slug %s', repo_group_name_parts, repo_group_slugs)
742 760 return False
743 761
744 762 repo_group_model = repo_group.RepoGroupModel()
745 763 by_name_match = repo_group_model.get_by_group_name(repo_group_name, cache=False)
746 764
747 765 if by_name_match:
748 766 # register this as request object we can re-use later
749 767 request.db_repo_group = by_name_match
750 768 return True
751 769
752 770 return False
753 771
754 772
755 773 class UserGroupRoutePredicate(object):
756 774 def __init__(self, val, config):
757 775 self.val = val
758 776
759 777 def text(self):
760 778 return 'user_group_route = %s' % self.val
761 779
762 780 phash = text
763 781
764 782 def __call__(self, info, request):
765 783 if hasattr(request, 'vcs_call'):
766 784 # skip vcs calls
767 785 return
768 786
769 787 user_group_id = info['match']['user_group_id']
770 788 user_group_model = user_group.UserGroup()
771 789 by_id_match = user_group_model.get(user_group_id, cache=False)
772 790
773 791 if by_id_match:
774 792 # register this as request object we can re-use later
775 793 request.db_user_group = by_id_match
776 794 return True
777 795
778 796 return False
779 797
780 798
781 799 class UserRoutePredicateBase(object):
782 800 supports_default = None
783 801
784 802 def __init__(self, val, config):
785 803 self.val = val
786 804
787 805 def text(self):
788 806 raise NotImplementedError()
789 807
790 808 def __call__(self, info, request):
791 809 if hasattr(request, 'vcs_call'):
792 810 # skip vcs calls
793 811 return
794 812
795 813 user_id = info['match']['user_id']
796 814 user_model = user.User()
797 815 by_id_match = user_model.get(user_id, cache=False)
798 816
799 817 if by_id_match:
800 818 # register this as request object we can re-use later
801 819 request.db_user = by_id_match
802 820 request.db_user_supports_default = self.supports_default
803 821 return True
804 822
805 823 return False
806 824
807 825
808 826 class UserRoutePredicate(UserRoutePredicateBase):
809 827 supports_default = False
810 828
811 829 def text(self):
812 830 return 'user_route = %s' % self.val
813 831
814 832 phash = text
815 833
816 834
817 835 class UserRouteWithDefaultPredicate(UserRoutePredicateBase):
818 836 supports_default = True
819 837
820 838 def text(self):
821 839 return 'user_with_default_route = %s' % self.val
822 840
823 841 phash = text
824 842
825 843
826 844 def includeme(config):
827 845 config.add_route_predicate(
828 846 'repo_route', RepoRoutePredicate)
829 847 config.add_route_predicate(
830 848 'repo_accepted_types', RepoTypeRoutePredicate)
831 849 config.add_route_predicate(
832 850 'repo_forbid_when_archived', RepoForbidArchivedRoutePredicate)
833 851 config.add_route_predicate(
834 852 'repo_group_route', RepoGroupRoutePredicate)
835 853 config.add_route_predicate(
836 854 'user_group_route', UserGroupRoutePredicate)
837 855 config.add_route_predicate(
838 856 'user_route_with_default', UserRouteWithDefaultPredicate)
839 857 config.add_route_predicate(
840 858 'user_route', UserRoutePredicate)
@@ -1,50 +1,53 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20 import logging
21 21
22 22 from pyramid.httpexceptions import HTTPNotFound
23 23
24 24 from rhodecode.apps._base import BaseReferencesView
25 25 from rhodecode.lib import ext_json
26 26 from rhodecode.lib import helpers as h
27 27 from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator)
28
28 from rhodecode.model.scm import ScmModel
29 29
30 30 log = logging.getLogger(__name__)
31 31
32 32
33 33 class RepoBookmarksView(BaseReferencesView):
34 34
35 35 @LoginRequired()
36 36 @HasRepoPermissionAnyDecorator(
37 37 'repository.read', 'repository.write', 'repository.admin')
38 38 def bookmarks(self):
39 39 c = self.load_default_context()
40 self._prepare_and_set_clone_url(c)
41 c.rhodecode_repo = self.rhodecode_vcs_repo
42 c.repository_forks = ScmModel().get_forks(self.db_repo)
40 43
41 44 if not h.is_hg(self.db_repo):
42 45 raise HTTPNotFound()
43 46
44 47 ref_items = self.rhodecode_vcs_repo.bookmarks.items()
45 48 data = self.load_refs_context(
46 49 ref_items=ref_items, partials_template='bookmarks/bookmarks_data.mako')
47 50
48 51 c.has_references = bool(data)
49 52 c.data = ext_json.str_json(data)
50 53 return self._get_template_context(c)
@@ -1,46 +1,49 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23
24 24 from rhodecode.apps._base import BaseReferencesView
25 25 from rhodecode.lib import ext_json
26 26 from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator)
27
27 from rhodecode.model.scm import ScmModel
28 28
29 29 log = logging.getLogger(__name__)
30 30
31 31
32 32 class RepoBranchesView(BaseReferencesView):
33 33
34 34 @LoginRequired()
35 35 @HasRepoPermissionAnyDecorator(
36 36 'repository.read', 'repository.write', 'repository.admin')
37 37 def branches(self):
38 38 c = self.load_default_context()
39 self._prepare_and_set_clone_url(c)
40 c.rhodecode_repo = self.rhodecode_vcs_repo
41 c.repository_forks = ScmModel().get_forks(self.db_repo)
39 42
40 43 ref_items = self.rhodecode_vcs_repo.branches_all.items()
41 44 data = self.load_refs_context(
42 45 ref_items=ref_items, partials_template='branches/branches_data.mako')
43 46
44 47 c.has_references = bool(data)
45 48 c.data = ext_json.str_json(data)
46 49 return self._get_template_context(c)
@@ -1,290 +1,274 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import string
23 23 import time
24 24
25 25 import rhodecode
26 26
27 27
28 28
29 29 from rhodecode.lib.view_utils import get_format_ref_id
30 30 from rhodecode.apps._base import RepoAppView
31 31 from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP)
32 32 from rhodecode.lib import helpers as h, rc_cache
33 33 from rhodecode.lib.utils2 import safe_str, safe_int
34 34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 35 from rhodecode.lib.ext_json import json
36 36 from rhodecode.lib.vcs.backends.base import EmptyCommit
37 37 from rhodecode.lib.vcs.exceptions import (
38 38 CommitError, EmptyRepositoryError, CommitDoesNotExistError)
39 39 from rhodecode.model.db import Statistics, CacheKey, User
40 40 from rhodecode.model.meta import Session
41 41 from rhodecode.model.scm import ScmModel
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45
46 46 class RepoSummaryView(RepoAppView):
47 47
48 48 def load_default_context(self):
49 49 c = self._get_local_tmpl_context(include_app_defaults=True)
50 50 c.rhodecode_repo = None
51 51 if not c.repository_requirements_missing:
52 52 c.rhodecode_repo = self.rhodecode_vcs_repo
53 53 return c
54 54
55 55 def _load_commits_context(self, c):
56 56 p = safe_int(self.request.GET.get('page'), 1)
57 57 size = safe_int(self.request.GET.get('size'), 10)
58 58
59 59 def url_generator(page_num):
60 60 query_params = {
61 61 'page': page_num,
62 62 'size': size
63 63 }
64 64 return h.route_path(
65 65 'repo_summary_commits',
66 66 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
67 67
68 68 pre_load = self.get_commit_preload_attrs()
69 69
70 70 try:
71 71 collection = self.rhodecode_vcs_repo.get_commits(
72 72 pre_load=pre_load, translate_tags=False)
73 73 except EmptyRepositoryError:
74 74 collection = self.rhodecode_vcs_repo
75 75
76 76 c.repo_commits = h.RepoPage(
77 77 collection, page=p, items_per_page=size, url_maker=url_generator)
78 78 page_ids = [x.raw_id for x in c.repo_commits]
79 79 c.comments = self.db_repo.get_comments(page_ids)
80 80 c.statuses = self.db_repo.statuses(page_ids)
81 81
82 def _prepare_and_set_clone_url(self, c):
83 username = ''
84 if self._rhodecode_user.username != User.DEFAULT_USER:
85 username = safe_str(self._rhodecode_user.username)
86
87 _def_clone_uri = c.clone_uri_tmpl
88 _def_clone_uri_id = c.clone_uri_id_tmpl
89 _def_clone_uri_ssh = c.clone_uri_ssh_tmpl
90
91 c.clone_repo_url = self.db_repo.clone_url(
92 user=username, uri_tmpl=_def_clone_uri)
93 c.clone_repo_url_id = self.db_repo.clone_url(
94 user=username, uri_tmpl=_def_clone_uri_id)
95 c.clone_repo_url_ssh = self.db_repo.clone_url(
96 uri_tmpl=_def_clone_uri_ssh, ssh=True)
97
98 82 @LoginRequired()
99 83 @HasRepoPermissionAnyDecorator(
100 84 'repository.read', 'repository.write', 'repository.admin')
101 85 def summary_commits(self):
102 86 c = self.load_default_context()
103 87 self._prepare_and_set_clone_url(c)
104 88 self._load_commits_context(c)
105 89 return self._get_template_context(c)
106 90
107 91 @LoginRequired()
108 92 @HasRepoPermissionAnyDecorator(
109 93 'repository.read', 'repository.write', 'repository.admin')
110 94 def summary(self):
111 95 c = self.load_default_context()
112 96
113 97 # Prepare the clone URL
114 98 self._prepare_and_set_clone_url(c)
115 99
116 100 # If enabled, get statistics data
117 101 c.show_stats = bool(self.db_repo.enable_statistics)
118 102
119 103 stats = Session().query(Statistics) \
120 104 .filter(Statistics.repository == self.db_repo) \
121 105 .scalar()
122 106
123 107 c.stats_percentage = 0
124 108
125 109 if stats and stats.languages:
126 110 c.no_data = False is self.db_repo.enable_statistics
127 111 lang_stats_d = json.loads(stats.languages)
128 112
129 113 # Sort first by decreasing count and second by the file extension,
130 114 # so we have a consistent output.
131 115 lang_stats_items = sorted(lang_stats_d.items(),
132 116 key=lambda k: (-k[1], k[0]))[:10]
133 117 lang_stats = [(x, {"count": y,
134 118 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
135 119 for x, y in lang_stats_items]
136 120
137 121 c.trending_languages = json.dumps(lang_stats)
138 122 else:
139 123 c.no_data = True
140 124 c.trending_languages = json.dumps({})
141 125
142 126 scm_model = ScmModel()
143 127 c.enable_downloads = self.db_repo.enable_downloads
144 128 c.repository_followers = scm_model.get_followers(self.db_repo)
145 129 c.repository_forks = scm_model.get_forks(self.db_repo)
146 130
147 131 # first interaction with the VCS instance after here...
148 132 if c.repository_requirements_missing:
149 133 self.request.override_renderer = \
150 134 'rhodecode:templates/summary/missing_requirements.mako'
151 135 return self._get_template_context(c)
152 136
153 137 c.readme_data, c.readme_file = \
154 138 self._get_readme_data(self.db_repo, c.visual.default_renderer)
155 139
156 140 # loads the summary commits template context
157 141 self._load_commits_context(c)
158 142
159 143 return self._get_template_context(c)
160 144
161 145 @LoginRequired()
162 146 @HasRepoPermissionAnyDecorator(
163 147 'repository.read', 'repository.write', 'repository.admin')
164 148 def repo_stats(self):
165 149 show_stats = bool(self.db_repo.enable_statistics)
166 150 repo_id = self.db_repo.repo_id
167 151
168 152 landing_commit = self.db_repo.get_landing_commit()
169 153 if isinstance(landing_commit, EmptyCommit):
170 154 return {'size': 0, 'code_stats': {}}
171 155
172 156 cache_seconds = safe_int(rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
173 157 cache_on = cache_seconds > 0
174 158
175 159 log.debug(
176 160 'Computing REPO STATS for repo_id %s commit_id `%s` '
177 161 'with caching: %s[TTL: %ss]' % (
178 162 repo_id, landing_commit, cache_on, cache_seconds or 0))
179 163
180 164 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
181 165 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
182 166
183 167 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
184 168 condition=cache_on)
185 169 def compute_stats(repo_id, commit_id, _show_stats):
186 170 code_stats = {}
187 171 size = 0
188 172 try:
189 173 commit = self.db_repo.get_commit(commit_id)
190 174
191 175 for node in commit.get_filenodes_generator():
192 176 size += node.size
193 177 if not _show_stats:
194 178 continue
195 179 ext = node.extension.lower()
196 180 ext_info = LANGUAGES_EXTENSIONS_MAP.get(ext)
197 181 if ext_info:
198 182 if ext in code_stats:
199 183 code_stats[ext]['count'] += 1
200 184 else:
201 185 code_stats[ext] = {"count": 1, "desc": ext_info}
202 186 except (EmptyRepositoryError, CommitDoesNotExistError):
203 187 pass
204 188 return {'size': h.format_byte_size_binary(size),
205 189 'code_stats': code_stats}
206 190
207 191 stats = compute_stats(self.db_repo.repo_id, landing_commit.raw_id, show_stats)
208 192 return stats
209 193
210 194 @LoginRequired()
211 195 @HasRepoPermissionAnyDecorator(
212 196 'repository.read', 'repository.write', 'repository.admin')
213 197 def repo_refs_data(self):
214 198 _ = self.request.translate
215 199 self.load_default_context()
216 200
217 201 repo = self.rhodecode_vcs_repo
218 202 refs_to_create = [
219 203 (_("Branch"), repo.branches, 'branch'),
220 204 (_("Tag"), repo.tags, 'tag'),
221 205 (_("Bookmark"), repo.bookmarks, 'book'),
222 206 ]
223 207 res = self._create_reference_data(repo, self.db_repo_name, refs_to_create)
224 208 data = {
225 209 'more': False,
226 210 'results': res
227 211 }
228 212 return data
229 213
230 214 @LoginRequired()
231 215 @HasRepoPermissionAnyDecorator(
232 216 'repository.read', 'repository.write', 'repository.admin')
233 217 def repo_refs_changelog_data(self):
234 218 _ = self.request.translate
235 219 self.load_default_context()
236 220
237 221 repo = self.rhodecode_vcs_repo
238 222
239 223 refs_to_create = [
240 224 (_("Branches"), repo.branches, 'branch'),
241 225 (_("Closed branches"), repo.branches_closed, 'branch_closed'),
242 226 # TODO: enable when vcs can handle bookmarks filters
243 227 # (_("Bookmarks"), repo.bookmarks, "book"),
244 228 ]
245 229 res = self._create_reference_data(
246 230 repo, self.db_repo_name, refs_to_create)
247 231 data = {
248 232 'more': False,
249 233 'results': res
250 234 }
251 235 return data
252 236
253 237 def _create_reference_data(self, repo, full_repo_name, refs_to_create):
254 238 format_ref_id = get_format_ref_id(repo)
255 239
256 240 result = []
257 241 for title, refs, ref_type in refs_to_create:
258 242 if refs:
259 243 result.append({
260 244 'text': title,
261 245 'children': self._create_reference_items(
262 246 repo, full_repo_name, refs, ref_type,
263 247 format_ref_id),
264 248 })
265 249 return result
266 250
267 251 def _create_reference_items(self, repo, full_repo_name, refs, ref_type, format_ref_id):
268 252 result = []
269 253 is_svn = h.is_svn(repo)
270 254 for ref_name, raw_id in refs.items():
271 255 files_url = self._create_files_url(
272 256 repo, full_repo_name, ref_name, raw_id, is_svn)
273 257 result.append({
274 258 'text': ref_name,
275 259 'id': format_ref_id(ref_name, raw_id),
276 260 'raw_id': raw_id,
277 261 'type': ref_type,
278 262 'files_url': files_url,
279 263 'idx': 0,
280 264 })
281 265 return result
282 266
283 267 def _create_files_url(self, repo, full_repo_name, ref_name, raw_id, is_svn):
284 268 use_commit_id = '/' in ref_name or is_svn
285 269 return h.route_path(
286 270 'repo_files',
287 271 repo_name=full_repo_name,
288 272 f_path=ref_name if is_svn else '',
289 273 commit_id=raw_id if use_commit_id else ref_name,
290 274 _query=dict(at=ref_name))
@@ -1,44 +1,48 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 from rhodecode.apps._base import BaseReferencesView
24 24 from rhodecode.lib import ext_json
25 25 from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator)
26 from rhodecode.model.scm import ScmModel
26 27
27 28 log = logging.getLogger(__name__)
28 29
29 30
30 31 class RepoTagsView(BaseReferencesView):
31 32
32 33 @LoginRequired()
33 34 @HasRepoPermissionAnyDecorator(
34 35 'repository.read', 'repository.write', 'repository.admin')
35 36 def tags(self):
36 37 c = self.load_default_context()
38 self._prepare_and_set_clone_url(c)
39 c.rhodecode_repo = self.rhodecode_vcs_repo
40 c.repository_forks = ScmModel().get_forks(self.db_repo)
37 41
38 42 ref_items = self.rhodecode_vcs_repo.tags.items()
39 43 data = self.load_refs_context(
40 44 ref_items=ref_items, partials_template='tags/tags_data.mako')
41 45
42 46 c.has_references = bool(data)
43 47 c.data = ext_json.str_json(data)
44 48 return self._get_template_context(c)
@@ -1,217 +1,217 b''
1 1 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
2 2
3 3 <%def name="form_item(position=None, title=None, redirect_url=None, repo=None, repo_group=None)">
4 4 <tr>
5 5 <td class="td-align-top" >
6 6 <div class="label">
7 7 <label for="position">${_('Position')}:</label>
8 8 </div>
9 9 <div class="input">
10 10 <input type="text" name="position" value="${position}" style="width: 40px"/>
11 11 ${h.hidden('cur_position', position)}
12 12 </div>
13 13 </td>
14 14
15 15 <td>
16 16 <div class="label">
17 17 <label for="title">${_('Bookmark title (max 30 characters, optional)')}:</label>
18 18 </div>
19 19 <div class="input">
20 20 <input type="text" name="title" value="${title}" style="width: 300px" maxlength="30"/>
21 21
22 22 <div class="field pull-right">
23 23 <div>
24 24 <label class="btn-link btn-danger">${_('Clear')}:</label>
25 25 ${h.checkbox('remove', value=True)}
26 26 </div>
27 27 </div>
28 28 </div>
29 29
30 30 <div class="label" style="margin-top:10px">
31 31 <label for="redirect_url">${_('Redirect URL')}:</label>
32 32 </div>
33 33 <div class="input">
34 34 <input type="text" name="redirect_url" value="${redirect_url}" style="width: 600px"/>
35 35 </div>
36 36 <p class="help-block help-block-inline">
37 37 ${_('Server URL is available as ${server_url} variable. E.g. Redirect url: ${server_url}/_admin/exception_tracker')}
38 38 </p>
39 39
40 40 <div class="select" style="margin-top:5px">
41 41 <div class="label">
42 42 <label for="redirect_url">${_('Templates')}:</label>
43 43 </div>
44 44
45 45 % if repo:
46 ${dt.repo_name(name=repo.repo_name, rtype=repo.repo_type,rstate=None,private=None,archived=False,fork_of=False)}
46 ${dt.repo_name(name=repo.repo_name, rtype=repo.repo_type,rstate=None,private=None,archived=False, fork_repo_name=None)}
47 47 ${h.hidden('bookmark_repo', repo.repo_id)}
48 48 % elif repo_group:
49 49 ${dt.repo_group_name(repo_group.group_name)}
50 50 ${h.hidden('bookmark_repo_group', repo_group.group_id)}
51 51 % else:
52 52 <div>
53 53 ${h.hidden('bookmark_repo', class_='bookmark_repo')}
54 54 <p class="help-block help-block-inline">${_('Available as ${repo_url} e.g. Redirect url: ${repo_url}/changelog')}</p>
55 55 </div>
56 56 <div style="margin-top:5px">
57 57 ${h.hidden('bookmark_repo_group', class_='bookmark_repo_group')}
58 58 <p class="help-block help-block-inline">${_('Available as ${repo_group_url} e.g. Redirect url: ${repo_group_url}')}</p>
59 59 </div>
60 60
61 61 % endif
62 62 </div>
63 63
64 64 </td>
65 65
66 66 </tr>
67 67 </%def>
68 68
69 69 <div class="panel panel-default">
70 70 <div class="panel-heading">
71 71 <h3 class="panel-title">${_('Your Bookmarks')}</h3>
72 72 </div>
73 73
74 74 <div class="panel-body">
75 75 <p>
76 76 ${_('Store upto 10 bookmark links to favorite repositories, external issue tracker or CI server. ')}
77 77 <br/>
78 78 ${_('Bookmarks are accessible from your username dropdown or by keyboard shortcut `g 0-9`')}
79 79 </p>
80 80
81 81 ${h.secure_form(h.route_path('my_account_bookmarks_update'), request=request)}
82 82 <div class="form-vertical">
83 83 <table class="rctable">
84 84 ## generate always 10 entries
85 85 <input type="hidden" name="__start__" value="bookmarks:sequence"/>
86 86 % for item in (c.bookmark_items + [None for i in range(10)])[:10]:
87 87 <input type="hidden" name="__start__" value="bookmark:mapping"/>
88 88 % if item is None:
89 89 ## empty placehodlder
90 90 ${form_item()}
91 91 % else:
92 92 ## actual entry
93 93 ${form_item(position=item.position, title=item.title, redirect_url=item.redirect_url, repo=item.repository, repo_group=item.repository_group)}
94 94 % endif
95 95 <input type="hidden" name="__end__" value="bookmark:mapping"/>
96 96 % endfor
97 97 <input type="hidden" name="__end__" value="bookmarks:sequence"/>
98 98 </table>
99 99 <div class="buttons">
100 100 ${h.submit('save',_('Save'),class_="btn")}
101 101 </div>
102 102 </div>
103 103 ${h.end_form()}
104 104 </div>
105 105 </div>
106 106
107 107 <script>
108 108 $(document).ready(function(){
109 109
110 110
111 111 var repoFilter = function (data) {
112 112 var results = [];
113 113
114 114 if (!data.results[0]) {
115 115 return data
116 116 }
117 117
118 118 $.each(data.results[0].children, function () {
119 119 // replace name to ID for submision
120 120 this.id = this.repo_id;
121 121 results.push(this);
122 122 });
123 123
124 124 data.results[0].children = results;
125 125 return data;
126 126 };
127 127
128 128
129 129 $(".bookmark_repo").select2({
130 130 cachedDataSource: {},
131 131 minimumInputLength: 2,
132 132 placeholder: "${_('repository')}",
133 133 dropdownAutoWidth: true,
134 134 containerCssClass: "drop-menu",
135 135 dropdownCssClass: "drop-menu-dropdown",
136 136 formatResult: formatRepoResult,
137 137 query: $.debounce(250, function (query) {
138 138 self = this;
139 139 var cacheKey = query.term;
140 140 var cachedData = self.cachedDataSource[cacheKey];
141 141
142 142 if (cachedData) {
143 143 query.callback({results: cachedData.results});
144 144 } else {
145 145 $.ajax({
146 146 url: pyroutes.url('repo_list_data'),
147 147 data: {'query': query.term},
148 148 dataType: 'json',
149 149 type: 'GET',
150 150 success: function (data) {
151 151 data = repoFilter(data);
152 152 self.cachedDataSource[cacheKey] = data;
153 153 query.callback({results: data.results});
154 154 },
155 155 error: function (data, textStatus, errorThrown) {
156 156 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
157 157 }
158 158 })
159 159 }
160 160 }),
161 161 });
162 162
163 163 var repoGroupFilter = function (data) {
164 164 var results = [];
165 165
166 166 if (!data.results[0]) {
167 167 return data
168 168 }
169 169
170 170 $.each(data.results[0].children, function () {
171 171 // replace name to ID for submision
172 172 this.id = this.repo_group_id;
173 173 results.push(this);
174 174 });
175 175
176 176 data.results[0].children = results;
177 177 return data;
178 178 };
179 179
180 180 $(".bookmark_repo_group").select2({
181 181 cachedDataSource: {},
182 182 minimumInputLength: 2,
183 183 placeholder: "${_('repository group')}",
184 184 dropdownAutoWidth: true,
185 185 containerCssClass: "drop-menu",
186 186 dropdownCssClass: "drop-menu-dropdown",
187 187 formatResult: formatRepoGroupResult,
188 188 query: $.debounce(250, function (query) {
189 189 self = this;
190 190 var cacheKey = query.term;
191 191 var cachedData = self.cachedDataSource[cacheKey];
192 192
193 193 if (cachedData) {
194 194 query.callback({results: cachedData.results});
195 195 } else {
196 196 $.ajax({
197 197 url: pyroutes.url('repo_group_list_data'),
198 198 data: {'query': query.term},
199 199 dataType: 'json',
200 200 type: 'GET',
201 201 success: function (data) {
202 202 data = repoGroupFilter(data);
203 203 self.cachedDataSource[cacheKey] = data;
204 204 query.callback({results: data.results});
205 205 },
206 206 error: function (data, textStatus, errorThrown) {
207 207 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
208 208 }
209 209 })
210 210 }
211 211 })
212 212 });
213 213
214 214
215 215 });
216 216
217 217 </script>
@@ -1,114 +1,119 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.mako"/>
3 <%namespace name="components" file="/summary/components.mako"/>
3 4
4 5 <%def name="title()">
5 ${_('%s Bookmarks') % c.repo_name}
6 ${_('{} Bookmarks').format(c.repo_name)}
6 7 %if c.rhodecode_name:
7 8 &middot; ${h.branding(c.rhodecode_name)}
8 9 %endif
9 10 </%def>
10 11
11 12 <%def name="breadcrumbs_links()"></%def>
12 13
13 14 <%def name="menu_bar_nav()">
14 15 ${self.menu_items(active='repositories')}
15 16 </%def>
16 17
17 18 <%def name="menu_bar_subnav()">
18 19 ${self.repo_menu(active='summary')}
19 20 </%def>
20 21
21 22 <%def name="main()">
23 <div id="repo-summary" class="summary">
24 ${components.summary_detail(breadcrumbs_links=self.breadcrumbs_links(), show_downloads=False, simplified=True)}
25 </div>
26
22 27 <div class="box">
23 28 <div class="title">
24 29
25 30 %if c.has_references:
26 31 <ul class="links">
27 32 <li>
28 33 <input type="submit" id="compare_action" class="btn" disabled="disabled" value="${_('Compare Selected Bookmarks')}">
29 34 </li>
30 35 </ul>
31 36 %endif
32 37 %if c.has_references:
33 38 <div class="grid-quick-filter">
34 39 <ul class="grid-filter-box">
35 40 <li class="grid-filter-box-icon">
36 41 <i class="icon-search"></i>
37 42 </li>
38 43 <li class="grid-filter-box-input">
39 44 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
40 45 </li>
41 46 </ul>
42 47 </div>
43 48 <div id="obj_count">0</div>
44 49 %endif
45 50 </div>
46 51
47 52 <table id="obj_list_table" class="rctable table-bordered"></table>
48 53 </div>
49 54
50 55
51 56 <script type="text/javascript">
52 57 $(document).ready(function() {
53 58
54 59 var get_datatable_count = function(){
55 60 var api = $('#obj_list_table').dataTable().api();
56 61 var total = api.page.info().recordsDisplay
57 62 var _text = _ngettext('{0} bookmark', '{0} bookmarks', total).format(total);
58 63 $('#obj_count').text(_text);
59 64 };
60 65
61 66 // object list
62 67 $('#obj_list_table').DataTable({
63 68 data: ${c.data|n},
64 69 dom: 'rtp',
65 70 pageLength: ${c.visual.dashboard_items},
66 71 order: [[ 0, "asc" ]],
67 72 columns: [
68 73 { data: {"_": "name",
69 74 "sort": "name_raw"}, title: "${_('Name')}", className: "td-tags" },
70 75 { data: {"_": "date",
71 76 "sort": "date_raw"}, title: "${_('Date')}", className: "td-time" },
72 77 { data: {"_": "author",
73 78 "sort": "author"}, title: "${_('Author')}", className: "td-user" },
74 79 { data: {"_": "commit",
75 80 "sort": "commit_raw",
76 81 "type": Number}, title: "${_('Commit')}", className: "td-hash" },
77 82 { data: {"_": "compare",
78 83 "sort": "compare"}, title: "${_('Compare')}", className: "td-compare" }
79 84 ],
80 85 language: {
81 86 paginate: DEFAULT_GRID_PAGINATION,
82 87 emptyTable: _gettext("No bookmarks available yet.")
83 88 },
84 89 "initComplete": function(settings, json) {
85 90 get_datatable_count();
86 91 timeagoActivate();
87 92 tooltipActivate();
88 93 compare_radio_buttons("${c.repo_name}", 'book');
89 94 }
90 95 });
91 96
92 97 // update when things change
93 98 $('#obj_list_table').on('draw.dt', function() {
94 99 get_datatable_count();
95 100 timeagoActivate();
96 101 tooltipActivate();
97 102 });
98 103
99 104 // filter, filter both grids
100 105 $('#q_filter').on('keyup', function() {
101 106 var obj_api = $('#obj_list_table').dataTable().api();
102 107 obj_api
103 108 .columns(0)
104 109 .search(this.value)
105 110 .draw();
106 111 });
107 112
108 113 // refilter table if page load via back button
109 114 $("#q_filter").trigger('keyup');
110 115
111 116 });
112 117
113 118 </script>
114 119 </%def>
@@ -1,113 +1,118 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.mako"/>
3 <%namespace name="components" file="/summary/components.mako"/>
3 4
4 5 <%def name="title()">
5 ${_('%s Branches') % c.repo_name}
6 ${_('{} Branches').format(c.repo_name)}
6 7 %if c.rhodecode_name:
7 8 &middot; ${h.branding(c.rhodecode_name)}
8 9 %endif
9 10 </%def>
10 11
11 12 <%def name="breadcrumbs_links()"></%def>
12 13
13 14 <%def name="menu_bar_nav()">
14 15 ${self.menu_items(active='repositories')}
15 16 </%def>
16 17
17 18 <%def name="menu_bar_subnav()">
18 19 ${self.repo_menu(active='summary')}
19 20 </%def>
20 21
21 22 <%def name="main()">
23 <div id="repo-summary" class="summary">
24 ${components.summary_detail(breadcrumbs_links=self.breadcrumbs_links(), show_downloads=False, simplified=True)}
25 </div>
26
22 27 <div class="box">
23 28 <div class="title">
24 29
25 30 %if c.has_references:
26 31 <ul class="links">
27 32 <li>
28 33 <input type="submit" id="compare_action" class="btn" disabled="disabled" value="${_('Compare Selected Branches')}"/>
29 34 </li>
30 35 </ul>
31 36 %endif
32 37 %if c.has_references:
33 38 <div class="grid-quick-filter">
34 39 <ul class="grid-filter-box">
35 40 <li class="grid-filter-box-icon">
36 41 <i class="icon-search"></i>
37 42 </li>
38 43 <li class="grid-filter-box-input">
39 44 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
40 45 </li>
41 46 </ul>
42 47 </div>
43 48 <div id="obj_count">0</div>
44 49 %endif
45 50 </div>
46 51 <table id="obj_list_table" class="rctable table-bordered"></table>
47 52 </div>
48 53
49 54 <script type="text/javascript">
50 55 $(document).ready(function() {
51 56
52 57 var get_datatable_count = function(){
53 58 var api = $('#obj_list_table').dataTable().api();
54 59 var total = api.page.info().recordsDisplay
55 60 var _text = _ngettext('{0} branch', '{0} branches', total).format(total);
56 61
57 62 $('#obj_count').text(_text);
58 63 };
59 64
60 65 // object list
61 66 $('#obj_list_table').DataTable({
62 67 data: ${c.data|n},
63 68 dom: 'rtp',
64 69 pageLength: ${c.visual.dashboard_items},
65 70 order: [[ 0, "asc" ]],
66 71 columns: [
67 72 { data: {"_": "name",
68 73 "sort": "name_raw"}, title: "${_('Name')}", className: "td-tags" },
69 74 { data: {"_": "date",
70 75 "sort": "date_raw"}, title: "${_('Date')}", className: "td-time" },
71 76 { data: {"_": "author",
72 77 "sort": "author"}, title: "${_('Author')}", className: "td-user" },
73 78 { data: {"_": "commit",
74 79 "sort": "commit_raw",
75 80 "type": Number}, title: "${_('Commit')}", className: "td-hash" },
76 81 { data: {"_": "compare",
77 82 "sort": "compare"}, title: "${_('Compare')}", className: "td-compare" }
78 83 ],
79 84 language: {
80 85 paginate: DEFAULT_GRID_PAGINATION,
81 86 emptyTable: _gettext("No branches available yet.")
82 87 },
83 88 "initComplete": function( settings, json ) {
84 89 get_datatable_count();
85 90 timeagoActivate();
86 91 tooltipActivate();
87 92 compare_radio_buttons("${c.repo_name}", 'branch');
88 93 }
89 94 });
90 95
91 96 // update when things change
92 97 $('#obj_list_table').on('draw.dt', function() {
93 98 get_datatable_count();
94 99 timeagoActivate();
95 100 tooltipActivate();
96 101 });
97 102
98 103 // filter, filter both grids
99 104 $('#q_filter').on( 'keyup', function () {
100 105 var obj_api = $('#obj_list_table').dataTable().api();
101 106 obj_api
102 107 .columns(0)
103 108 .search(this.value)
104 109 .draw();
105 110 });
106 111
107 112 // refilter table if page load via back button
108 113 $("#q_filter").trigger('keyup');
109 114
110 115 });
111 116
112 117 </script>
113 118 </%def>
@@ -1,499 +1,499 b''
1 1 ## DATA TABLE RE USABLE ELEMENTS
2 2 ## usage:
3 3 ## <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
4 4 <%namespace name="base" file="/base/base.mako"/>
5 5
6 6 <%def name="metatags_help()">
7 7 <table>
8 8 <%
9 9 example_tags = [
10 10 ('state','[stable]'),
11 11 ('state','[stale]'),
12 12 ('state','[featured]'),
13 13 ('state','[dev]'),
14 14 ('state','[dead]'),
15 15 ('state','[deprecated]'),
16 16
17 17 ('label','[personal]'),
18 18 ('generic','[v2.0.0]'),
19 19
20 20 ('lang','[lang =&gt; JavaScript]'),
21 21 ('license','[license =&gt; LicenseName]'),
22 22
23 23 ('ref','[requires =&gt; RepoName]'),
24 24 ('ref','[recommends =&gt; GroupName]'),
25 25 ('ref','[conflicts =&gt; SomeName]'),
26 26 ('ref','[base =&gt; SomeName]'),
27 27 ('url','[url =&gt; [linkName](https://rhodecode.com)]'),
28 28 ('see','[see =&gt; http://rhodecode.com]'),
29 29 ]
30 30 %>
31 31 % for tag_type, tag in example_tags:
32 32 <tr>
33 33 <td>${tag|n}</td>
34 34 <td>${h.style_metatag(tag_type, tag)|n}</td>
35 35 </tr>
36 36 % endfor
37 37 </table>
38 38 </%def>
39 39
40 40 <%def name="render_description(description, stylify_metatags)">
41 41 <%
42 42 tags = []
43 43 if stylify_metatags:
44 44 tags, description = h.extract_metatags(description)
45 45 %>
46 46 % for tag_type, tag in tags:
47 47 ${h.style_metatag(tag_type, tag)|n,trim}
48 48 % endfor
49 49 <code style="white-space: pre-wrap">${description}</code>
50 50 </%def>
51 51
52 52 ## REPOSITORY RENDERERS
53 53 <%def name="quick_menu(repo_name)">
54 54 <i class="icon-more"></i>
55 55 <div class="menu_items_container hidden">
56 56 <ul class="menu_items">
57 57 <li>
58 58 <a title="${_('Summary')}" href="${h.route_path('repo_summary',repo_name=repo_name)}">
59 59 <span>${_('Summary')}</span>
60 60 </a>
61 61 </li>
62 62 <li>
63 63 <a title="${_('Commits')}" href="${h.route_path('repo_commits',repo_name=repo_name)}">
64 64 <span>${_('Commits')}</span>
65 65 </a>
66 66 </li>
67 67 <li>
68 68 <a title="${_('Files')}" href="${h.route_path('repo_files:default_commit',repo_name=repo_name)}">
69 69 <span>${_('Files')}</span>
70 70 </a>
71 71 </li>
72 72 <li>
73 73 <a title="${_('Fork')}" href="${h.route_path('repo_fork_new',repo_name=repo_name)}">
74 74 <span>${_('Fork')}</span>
75 75 </a>
76 76 </li>
77 77 </ul>
78 78 </div>
79 79 </%def>
80 80
81 <%def name="repo_name(name,rtype,rstate,private,archived,fork_of,short_name=False,admin=False)">
81 <%def name="repo_name(name,rtype,rstate,private,archived,fork_repo_name,short_name=False,admin=False)">
82 82 <%
83 83 def get_name(name,short_name=short_name):
84 84 if short_name:
85 85 return name.split('/')[-1]
86 86 else:
87 87 return name
88 88 %>
89 89 <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate">
90 90 ##NAME
91 91 <a href="${h.route_path('edit_repo',repo_name=name) if admin else h.route_path('repo_summary',repo_name=name)}">
92 92
93 93 ##TYPE OF REPO
94 94 %if h.is_hg(rtype):
95 95 <span title="${_('Mercurial repository')}"><i class="icon-hg" style="font-size: 14px;"></i></span>
96 96 %elif h.is_git(rtype):
97 97 <span title="${_('Git repository')}"><i class="icon-git" style="font-size: 14px"></i></span>
98 98 %elif h.is_svn(rtype):
99 99 <span title="${_('Subversion repository')}"><i class="icon-svn" style="font-size: 14px"></i></span>
100 100 %endif
101 101
102 102 ##PRIVATE/PUBLIC
103 103 %if private is True and c.visual.show_private_icon:
104 104 <i class="icon-lock" title="${_('Private repository')}"></i>
105 105 %elif private is False and c.visual.show_public_icon:
106 106 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
107 107 %else:
108 108 <span></span>
109 109 %endif
110 110 ${get_name(name)}
111 111 </a>
112 %if fork_of:
113 <a href="${h.route_path('repo_summary',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
112 %if fork_repo_name:
113 <a href="${h.route_path('repo_summary',repo_name=fork_repo_name)}"><i class="icon-code-fork"></i></a>
114 114 %endif
115 115 %if rstate == 'repo_state_pending':
116 116 <span class="creation_in_progress tooltip" title="${_('This repository is being created in a background task')}">
117 117 (${_('creating...')})
118 118 </span>
119 119 %endif
120 120
121 121 </div>
122 122 </%def>
123 123
124 124 <%def name="repo_desc(description, stylify_metatags)">
125 125 <%
126 126 tags, description = h.extract_metatags(description)
127 127 %>
128 128
129 129 <div class="truncate-wrap">
130 130 % if stylify_metatags:
131 131 % for tag_type, tag in tags:
132 132 ${h.style_metatag(tag_type, tag)|n}
133 133 % endfor
134 134 % endif
135 135 ${description}
136 136 </div>
137 137
138 138 </%def>
139 139
140 140 <%def name="last_change(last_change)">
141 141 ${h.age_component(last_change, time_is_local=True)}
142 142 </%def>
143 143
144 144 <%def name="revision(repo_name, rev, commit_id, author, last_msg, commit_date)">
145 145 <div>
146 146 %if rev >= 0:
147 147 <code><a class="tooltip-hovercard" data-hovercard-alt=${h.tooltip(last_msg)} data-hovercard-url="${h.route_path('hovercard_repo_commit', repo_name=repo_name, commit_id=commit_id)}" href="${h.route_path('repo_commit',repo_name=repo_name,commit_id=commit_id)}">${'r{}:{}'.format(rev,h.short_id(commit_id))}</a></code>
148 148 %else:
149 149 ${_('No commits yet')}
150 150 %endif
151 151 </div>
152 152 </%def>
153 153
154 154 <%def name="rss(name)">
155 155 %if c.rhodecode_user.username != h.DEFAULT_USER:
156 156 <a title="${h.tooltip(_('Subscribe to %s rss feed')% name)}" href="${h.route_path('rss_feed_home', repo_name=name, _query=dict(auth_token=c.rhodecode_user.feed_token))}"><i class="icon-rss-sign"></i></a>
157 157 %else:
158 158 <a title="${h.tooltip(_('Subscribe to %s rss feed')% name)}" href="${h.route_path('rss_feed_home', repo_name=name)}"><i class="icon-rss-sign"></i></a>
159 159 %endif
160 160 </%def>
161 161
162 162 <%def name="atom(name)">
163 163 %if c.rhodecode_user.username != h.DEFAULT_USER:
164 164 <a title="${h.tooltip(_('Subscribe to %s atom feed')% name)}" href="${h.route_path('atom_feed_home', repo_name=name, _query=dict(auth_token=c.rhodecode_user.feed_token))}"><i class="icon-rss-sign"></i></a>
165 165 %else:
166 166 <a title="${h.tooltip(_('Subscribe to %s atom feed')% name)}" href="${h.route_path('atom_feed_home', repo_name=name)}"><i class="icon-rss-sign"></i></a>
167 167 %endif
168 168 </%def>
169 169
170 170 <%def name="repo_actions(repo_name, super_user=True)">
171 171 <div>
172 172 <div class="grid_edit">
173 173 <a href="${h.route_path('edit_repo',repo_name=repo_name)}" title="${_('Edit')}">
174 174 Edit
175 175 </a>
176 176 </div>
177 177 <div class="grid_delete">
178 178 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=repo_name), request=request)}
179 179 <input class="btn btn-link btn-danger" id="remove_${repo_name}" name="remove_${repo_name}"
180 180 onclick="submitConfirm(event, this, _gettext('Confirm to delete this repository'), _gettext('Delete'), '${repo_name}')"
181 181 type="submit" value="Delete"
182 182 >
183 183 ${h.end_form()}
184 184 </div>
185 185 </div>
186 186 </%def>
187 187
188 188 <%def name="repo_state(repo_state)">
189 189 <div>
190 190 %if repo_state == 'repo_state_pending':
191 191 <div class="tag tag4">${_('Creating')}</div>
192 192 %elif repo_state == 'repo_state_created':
193 193 <div class="tag tag1">${_('Created')}</div>
194 194 %else:
195 195 <div class="tag alert2" title="${h.tooltip(repo_state)}">invalid</div>
196 196 %endif
197 197 </div>
198 198 </%def>
199 199
200 200
201 201 ## REPO GROUP RENDERERS
202 202 <%def name="quick_repo_group_menu(repo_group_name)">
203 203 <i class="icon-more"></i>
204 204 <div class="menu_items_container hidden">
205 205 <ul class="menu_items">
206 206 <li>
207 207 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">${_('Summary')}</a>
208 208 </li>
209 209
210 210 </ul>
211 211 </div>
212 212 </%def>
213 213
214 214 <%def name="repo_group_name(repo_group_name, children_groups=None)">
215 215 <div>
216 216 <a href="${h.route_path('repo_group_home', repo_group_name=repo_group_name)}">
217 217 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
218 218 %if children_groups:
219 219 ${h.literal(' &raquo; '.join(children_groups))}
220 220 %else:
221 221 ${repo_group_name}
222 222 %endif
223 223 </a>
224 224 </div>
225 225 </%def>
226 226
227 227 <%def name="repo_group_desc(description, personal, stylify_metatags)">
228 228
229 229 <%
230 230 if stylify_metatags:
231 231 tags, description = h.extract_metatags(description)
232 232 %>
233 233
234 234 <div class="truncate-wrap">
235 235 % if personal:
236 236 <div class="metatag" tag="personal">${_('personal')}</div>
237 237 % endif
238 238
239 239 % if stylify_metatags:
240 240 % for tag_type, tag in tags:
241 241 ${h.style_metatag(tag_type, tag)|n}
242 242 % endfor
243 243 % endif
244 244 ${description}
245 245 </div>
246 246
247 247 </%def>
248 248
249 249 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
250 250 <div class="grid_edit">
251 251 <a href="${h.route_path('edit_repo_group',repo_group_name=repo_group_name)}" title="${_('Edit')}">Edit</a>
252 252 </div>
253 253 <div class="grid_delete">
254 254 ${h.secure_form(h.route_path('edit_repo_group_advanced_delete', repo_group_name=repo_group_name), request=request)}
255 255 <input class="btn btn-link btn-danger" id="remove_${repo_group_name}" name="remove_${repo_group_name}"
256 256 onclick="submitConfirm(event, this, _gettext('Confirm to delete this repository group'), _gettext('Delete'), '${_ungettext('`{}` with {} repository','`{}` with {} repositories',gr_count).format(repo_group_name, gr_count)}')"
257 257 type="submit" value="Delete"
258 258 >
259 259 ${h.end_form()}
260 260 </div>
261 261 </%def>
262 262
263 263
264 264 <%def name="user_actions(user_id, username)">
265 265 <div class="grid_edit">
266 266 <a href="${h.route_path('user_edit',user_id=user_id)}" title="${_('Edit')}">
267 267 ${_('Edit')}
268 268 </a>
269 269 </div>
270 270 <div class="grid_delete">
271 271 ${h.secure_form(h.route_path('user_delete', user_id=user_id), request=request)}
272 272 <input class="btn btn-link btn-danger" id="remove_user_${user_id}" name="remove_user_${user_id}"
273 273 onclick="submitConfirm(event, this, _gettext('Confirm to delete this user'), _gettext('Delete'), '${username}')"
274 274 type="submit" value="Delete"
275 275 >
276 276 ${h.end_form()}
277 277 </div>
278 278 </%def>
279 279
280 280 <%def name="user_group_actions(user_group_id, user_group_name)">
281 281 <div class="grid_edit">
282 282 <a href="${h.route_path('edit_user_group', user_group_id=user_group_id)}" title="${_('Edit')}">Edit</a>
283 283 </div>
284 284 <div class="grid_delete">
285 285 ${h.secure_form(h.route_path('user_groups_delete', user_group_id=user_group_id), request=request)}
286 286 <input class="btn btn-link btn-danger" id="remove_group_${user_group_id}" name="remove_group_${user_group_id}"
287 287 onclick="submitConfirm(event, this, _gettext('Confirm to delete this user group'), _gettext('Delete'), '${user_group_name}')"
288 288 type="submit" value="Delete"
289 289 >
290 290 ${h.end_form()}
291 291 </div>
292 292 </%def>
293 293
294 294
295 295 <%def name="user_name(user_id, username)">
296 296 ${h.link_to(h.person(username, 'username_or_name_or_email'), h.route_path('user_edit', user_id=user_id))}
297 297 </%def>
298 298
299 299 <%def name="user_profile(username)">
300 300 ${base.gravatar_with_user(username, 16, tooltip=True)}
301 301 </%def>
302 302
303 303 <%def name="user_group_name(user_group_name)">
304 304 <div>
305 305 <i class="icon-user-group" title="${_('User group')}"></i>
306 306 ${h.link_to_group(user_group_name)}
307 307 </div>
308 308 </%def>
309 309
310 310
311 311 ## GISTS
312 312
313 313 <%def name="gist_gravatar(full_contact)">
314 314 <div class="gist_gravatar">
315 315 ${base.gravatar(full_contact, 30)}
316 316 </div>
317 317 </%def>
318 318
319 319 <%def name="gist_access_id(gist_access_id, full_contact)">
320 320 <div>
321 321 <code>
322 322 <a href="${h.route_path('gist_show', gist_id=gist_access_id)}">${gist_access_id}</a>
323 323 </code>
324 324 </div>
325 325 </%def>
326 326
327 327 <%def name="gist_author(full_contact, created_on, expires)">
328 328 ${base.gravatar_with_user(full_contact, 16, tooltip=True)}
329 329 </%def>
330 330
331 331
332 332 <%def name="gist_created(created_on)">
333 333 <div class="created">
334 334 ${h.age_component(created_on, time_is_local=True)}
335 335 </div>
336 336 </%def>
337 337
338 338 <%def name="gist_expires(expires)">
339 339 <div class="created">
340 340 %if expires == -1:
341 341 ${_('never')}
342 342 %else:
343 343 ${h.age_component(h.time_to_utcdatetime(expires))}
344 344 %endif
345 345 </div>
346 346 </%def>
347 347
348 348 <%def name="gist_type(gist_type)">
349 349 %if gist_type == 'public':
350 350 <span class="tag tag-gist-public disabled">${_('Public Gist')}</span>
351 351 %else:
352 352 <span class="tag tag-gist-private disabled">${_('Private Gist')}</span>
353 353 %endif
354 354 </%def>
355 355
356 356 <%def name="gist_description(gist_description)">
357 357 ${gist_description}
358 358 </%def>
359 359
360 360
361 361 ## PULL REQUESTS GRID RENDERERS
362 362
363 363 <%def name="pullrequest_target_repo(repo_name)">
364 364 <div class="truncate">
365 365 ${h.link_to(repo_name,h.route_path('repo_summary',repo_name=repo_name))}
366 366 </div>
367 367 </%def>
368 368
369 369 <%def name="pullrequest_status(status)">
370 370 <i class="icon-circle review-status-${status}"></i>
371 371 </%def>
372 372
373 373 <%def name="pullrequest_title(title, description)">
374 374 ${title}
375 375 </%def>
376 376
377 377 <%def name="pullrequest_comments(comments_nr)">
378 378 <i class="icon-comment"></i> ${comments_nr}
379 379 </%def>
380 380
381 381 <%def name="pullrequest_name(pull_request_id, state, is_wip, target_repo_name, short=False)">
382 382 <code>
383 383 <a href="${h.route_path('pullrequest_show',repo_name=target_repo_name,pull_request_id=pull_request_id)}">
384 384 % if short:
385 385 !${pull_request_id}
386 386 % else:
387 387 ${_('Pull request !{}').format(pull_request_id)}
388 388 % endif
389 389 </a>
390 390 </code>
391 391 % if state not in ['created']:
392 392 <span class="tag tag-merge-state-${state} tooltip" title="Pull request state is changing">${state}</span>
393 393 % endif
394 394
395 395 % if is_wip:
396 396 <span class="tag tooltip" title="${_('Work in progress')}">wip</span>
397 397 % endif
398 398 </%def>
399 399
400 400 <%def name="pullrequest_updated_on(updated_on, pr_version=None)">
401 401 % if pr_version:
402 402 <code>v${pr_version}</code>
403 403 % endif
404 404 ${h.age_component(h.time_to_utcdatetime(updated_on))}
405 405 </%def>
406 406
407 407 <%def name="pullrequest_author(full_contact)">
408 408 ${base.gravatar_with_user(full_contact, 16, tooltip=True)}
409 409 </%def>
410 410
411 411
412 412 ## ARTIFACT RENDERERS
413 413 <%def name="repo_artifact_name(repo_name, file_uid, artifact_display_name)">
414 414 <a href="${h.route_path('repo_artifacts_get', repo_name=repo_name, uid=file_uid)}">
415 415 ${artifact_display_name or '_EMPTY_NAME_'}
416 416 </a>
417 417 </%def>
418 418
419 419 <%def name="repo_artifact_admin_name(file_uid, artifact_display_name)">
420 420 <a href="${h.route_path('admin_artifacts_show_info', uid=file_uid)}">
421 421 ${(artifact_display_name or '_EMPTY_NAME_')}
422 422 </a>
423 423 </%def>
424 424
425 425 <%def name="repo_artifact_uid(repo_name, file_uid)">
426 426 <code>${h.shorter(file_uid, size=24, prefix=True)}</code>
427 427 </%def>
428 428
429 429 <%def name="repo_artifact_sha256(artifact_sha256)">
430 430 <div class="code">${h.shorter(artifact_sha256, 12)}</div>
431 431 </%def>
432 432
433 433 <%def name="repo_artifact_actions(repo_name, file_store_id, file_uid)">
434 434 ## <div class="grid_edit">
435 435 ## <a href="#Edit" title="${_('Edit')}">${_('Edit')}</a>
436 436 ## </div>
437 437 <div class="grid_edit">
438 438 <a href="${h.route_path('repo_artifacts_info', repo_name=repo_name, uid=file_store_id)}" title="${_('Info')}">${_('Info')}</a>
439 439 </div>
440 440 % if h.HasRepoPermissionAny('repository.admin')(c.repo_name):
441 441 <div class="grid_delete">
442 442 ${h.secure_form(h.route_path('repo_artifacts_delete', repo_name=repo_name, uid=file_store_id), request=request)}
443 443 <input class="btn btn-link btn-danger" id="remove_artifact_${file_store_id}" name="remove_artifact_${file_store_id}"
444 444 onclick="submitConfirm(event, this, _gettext('Confirm to delete this artifact'), _gettext('Delete'), '${file_uid}')"
445 445 type="submit" value="${_('Delete')}"
446 446 >
447 447 ${h.end_form()}
448 448 </div>
449 449 % endif
450 450 </%def>
451 451
452 452
453 453 <%def name="markup_form(form_id, form_text='', help_text=None)">
454 454
455 455 <div class="markup-form">
456 456 <div class="markup-form-area">
457 457 <div class="markup-form-area-header">
458 458 <ul class="nav-links clearfix">
459 459 <li class="active">
460 460 <a href="#edit-text" tabindex="-1" id="edit-btn_${form_id}">${_('Write')}</a>
461 461 </li>
462 462 <li class="">
463 463 <a href="#preview-text" tabindex="-1" id="preview-btn_${form_id}">${_('Preview')}</a>
464 464 </li>
465 465 </ul>
466 466 </div>
467 467
468 468 <div class="markup-form-area-write" style="display: block;">
469 469 <div id="edit-container_${form_id}" style="margin-top: -1px">
470 470 <textarea id="${form_id}" name="${form_id}" class="comment-block-ta ac-input">${form_text if form_text else ''}</textarea>
471 471 </div>
472 472 <div id="preview-container_${form_id}" class="clearfix" style="display: none;">
473 473 <div id="preview-box_${form_id}" class="preview-box"></div>
474 474 </div>
475 475 </div>
476 476
477 477 <div class="markup-form-area-footer">
478 478 <div class="toolbar">
479 479 <div class="toolbar-text">
480 480 ${(_('Parsed using %s syntax') % (
481 481 ('<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
482 482 )
483 483 )|n}
484 484 </div>
485 485 </div>
486 486 </div>
487 487 </div>
488 488
489 489 <div class="markup-form-footer">
490 490 % if help_text:
491 491 <span class="help-block">${help_text}</span>
492 492 % endif
493 493 </div>
494 494 </div>
495 495 <script type="text/javascript">
496 496 new MarkupForm('${form_id}');
497 497 </script>
498 498
499 499 </%def>
@@ -1,189 +1,189 b''
1 1 <%namespace name="sourceblock" file="/codeblocks/source.mako"/>
2 2
3 3 <%
4 4 at_ref = request.GET.get('at')
5 5 if at_ref:
6 6 query={'at': at_ref}
7 7 default_commit_id = at_ref or c.rhodecode_db_repo.landing_ref_name
8 8 else:
9 9 query=None
10 10 default_commit_id = c.commit.raw_id
11 11 %>
12 12
13 13 <div id="codeblock" class="browserblock">
14 14 <div class="browser-header">
15 15 <div class="browser-nav">
16 16 <div class="pull-left">
17 17 ## loads the history for a file
18 18 ${h.hidden('file_refs_filter')}
19 19 </div>
20 20
21 21 <div class="pull-right">
22 22
23 23 ## Download
24 24 % if c.lf_node:
25 25 <a class="btn btn-default" href="${h.route_path('repo_file_download',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path, _query=dict(lf=1))}">
26 26 ${_('Download largefile')}
27 27 </a>
28 28 % else:
29 29 <a class="btn btn-default" href="${h.route_path('repo_file_download',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path)}">
30 30 ${_('Download file')}
31 31 </a>
32 32 % endif
33 33
34 34 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
35 35 ## on branch head, can edit files
36 36 %if c.on_branch_head and c.branch_or_raw_id:
37 37 ## binary files are delete only
38 38 % if c.file.is_binary:
39 39 ${h.link_to(_('Edit'), '#Edit', class_="btn btn-default disabled tooltip", title=_('Editing binary files not allowed'))}
40 40 ${h.link_to(_('Delete'), h.route_path('repo_files_remove_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _query=query),class_="btn btn-danger")}
41 41 % else:
42 42 <a class="btn btn-default" href="${h.route_path('repo_files_edit_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _query=query)}">
43 43 ${_('Edit on branch: ')}<code>${c.branch_name}</code>
44 44 </a>
45 45
46 46 <a class="btn btn-danger" href="${h.route_path('repo_files_remove_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _query=query)}">
47 47 ${_('Delete')}
48 48 </a>
49 49 % endif
50 50 ## not on head, forbid all
51 51 % else:
52 52 ${h.link_to(_('Edit'), '#Edit', class_="btn btn-default disabled tooltip", title=_('Editing files allowed only when on branch head commit'))}
53 53 ${h.link_to(_('Delete'), '#Delete', class_="btn btn-default btn-danger disabled tooltip", title=_('Deleting files allowed only when on branch head commit'))}
54 54 % endif
55 55 %endif
56 56
57 57 </div>
58 58 </div>
59 59 <div id="file_history_container"></div>
60 60
61 61 </div>
62 62 </div>
63 63
64 64 <div class="codeblock">
65 65 <div class=" codeblock-header">
66 66 <div class="file-filename">
67 <i class="icon-file"></i> ${c.file}
67 <i class="icon-file"></i> ${c.file.name}
68 68 </div>
69 69
70 70 <div class="file-stats">
71 71
72 72 <div class="stats-info">
73 73 <span class="stats-first-item">
74 74 % if c.file_size_too_big:
75 75 0 ${(_('lines'))}
76 76 % else:
77 77 ${c.file.lines()[0]} ${_ungettext('line', 'lines', c.file.lines()[0])}
78 78 % endif
79 79 </span>
80 80
81 81 <span> | ${h.format_byte_size_binary(c.file.size)}</span>
82 82 % if c.lf_node:
83 83 <span title="${_('This file is a pointer to large binary file')}"> | ${_('LargeFile')} ${h.format_byte_size_binary(c.lf_node.size)} </span>
84 84 % endif
85 85 <span>
86 86 | ${c.file.mimetype}
87 87 </span>
88 88
89 89 % if not c.file_size_too_big:
90 90 <span> |
91 91 ${h.get_lexer_for_filenode(c.file).__class__.__name__}
92 92 </span>
93 93 % endif
94 94
95 95 </div>
96 96 </div>
97 97 </div>
98 98
99 99 <div class="path clear-fix">
100 100 <div class="pull-left">
101 101 ${h.files_breadcrumbs(c.repo_name, c.rhodecode_db_repo.repo_type, c.commit.raw_id, c.file.path, c.rhodecode_db_repo.landing_ref_name, request.GET.get('at'))}
102 102 </div>
103 103
104 104 <div class="pull-right stats">
105 105 <a id="file_history_overview" href="#loadHistory">
106 106 ${_('History')}
107 107 </a>
108 108 |
109 109 %if c.annotate:
110 110 ${h.link_to(_('Source'), h.route_path('repo_files', repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path))}
111 111 %else:
112 112 ${h.link_to(_('Annotation'), h.route_path('repo_files:annotated',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path))}
113 113 %endif
114 114 | ${h.link_to(_('Raw'), h.route_path('repo_file_raw',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path))}
115 115 % if not c.file.is_binary:
116 116 |<a href="#copySource" onclick="return false;" class="no-grey clipboard-action" data-clipboard-text="${c.file.content}">${_('Copy content')}</a>
117 117 |<a href="#copyPermaLink" onclick="return false;" class="no-grey clipboard-action" data-clipboard-text="${h.route_url('repo_files', repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path)}">${_('Copy permalink')}</a>
118 118 % endif
119 119
120 120 </div>
121 121 <div class="clear-fix"></div>
122 122 </div>
123 123
124 124 <div class="code-body clear-fix ">
125 125 %if c.file.is_binary:
126 126 <% rendered_binary = h.render_binary(c.repo_name, c.file)%>
127 127 % if rendered_binary:
128 128 <div class="text-center">
129 129 ${rendered_binary}
130 130 </div>
131 131 % else:
132 132 <div>
133 133 ${_('Binary file ({})').format(c.file.mimetype)}
134 134 </div>
135 135 % endif
136 136 %else:
137 137 % if c.file_size_too_big:
138 138 ${_('File size {} is bigger then allowed limit {}. ').format(h.format_byte_size_binary(c.file.size), h.format_byte_size_binary(c.visual.cut_off_limit_file))} ${h.link_to(_('Show as raw'),
139 139 h.route_path('repo_file_raw',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path))}
140 140 % else:
141 141 %if c.renderer and not c.annotate:
142 142 ## pick relative url based on renderer
143 143 <%
144 144 relative_urls = {
145 145 'raw': h.route_path('repo_file_raw',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path),
146 146 'standard': h.route_path('repo_files',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path),
147 147 }
148 148 %>
149 149 ${h.render(c.file.content, renderer=c.renderer, relative_urls=relative_urls)}
150 150 %else:
151 151 <table class="cb codehilite">
152 152 %if c.annotate:
153 153 <% color_hasher = h.color_hasher() %>
154 154 %for annotation, lines in c.annotated_lines:
155 155 ${sourceblock.render_annotation_lines(annotation, lines, color_hasher)}
156 156 %endfor
157 157 %else:
158 158 %for line_num, tokens in enumerate(c.lines, 1):
159 159 ${sourceblock.render_line(line_num, tokens)}
160 160 %endfor
161 161 %endif
162 162 </table>
163 163 %endif
164 164 % endif
165 165 %endif
166 166 </div>
167 167
168 168 </div>
169 169
170 170 <script type="text/javascript">
171 171 % if request.GET.get('mark'):
172 172
173 173 $(function(){
174 174 $(".codehilite").mark(
175 175 "${request.GET.get('mark')}",
176 176 {
177 177 "className": 'match',
178 178 "accuracy": "complementary",
179 179 "ignorePunctuation": ":._(){}[]!'+=".split(""),
180 180 "each": function(el) {
181 181 // and also highlight lines !
182 182 $($(el).closest('tr')).find('td.cb-lineno').addClass('cb-line-selected');
183 183 }
184 184 }
185 185 );
186 186
187 187 });
188 188 % endif
189 189 </script>
@@ -1,276 +1,282 b''
1 1 <%namespace name="base" file="/base/base.mako"/>
2 2
3 3 <%def name="refs_counters(branches, closed_branches, tags, bookmarks)">
4 4 <span class="summary-branchtag summary-tag">
5 5 <a href="${h.route_path('branches_home',repo_name=c.repo_name)}" class="childs">
6 6 <i class="icon-branch"></i>
7 7 % if len(branches) == 1:
8 8 <span>${len(branches)}</span> ${_('Branch')}
9 9 % else:
10 10 <span>${len(branches)}</span> ${_('Branches')}
11 11 % endif
12 12 </a>
13 13 </span>
14 14
15 15 %if closed_branches:
16 16 <span class="summary-branchtag summary-tag">
17 17 <a href="${h.route_path('branches_home',repo_name=c.repo_name)}" class="childs">
18 18 <i class="icon-branch"></i>
19 19 % if len(closed_branches) == 1:
20 20 <span>${len(closed_branches)}</span> ${_('Closed Branch')}
21 21 % else:
22 22 <span>${len(closed_branches)}</span> ${_('Closed Branches')}
23 23 % endif
24 24 </a>
25 25 </span>
26 26 %endif
27 27
28 28 <span class="summary-tagtag summary-tag">
29 29 <a href="${h.route_path('tags_home',repo_name=c.repo_name)}" class="childs">
30 30 <i class="icon-tag"></i>
31 31 % if len(tags) == 1:
32 32 <span>${len(tags)}</span> ${_('Tag')}
33 33 % else:
34 34 <span>${len(tags)}</span> ${_('Tags')}
35 35 % endif
36 36 </a>
37 37 </span>
38 38
39 39 %if bookmarks:
40 40 <span class="summary-booktag summary-tag">
41 41 <a href="${h.route_path('bookmarks_home',repo_name=c.repo_name)}" class="childs">
42 42 <i class="icon-bookmark"></i>
43 43 % if len(bookmarks) == 1:
44 44 <span>${len(bookmarks)}</span> ${_('Bookmark')}
45 45 % else:
46 46 <span>${len(bookmarks)}</span> ${_('Bookmarks')}
47 47 % endif
48 48 </a>
49 49 </span>
50 50 %endif
51 51 </%def>
52 52
53 <%def name="summary_detail(breadcrumbs_links, show_downloads=True)">
53 <%def name="summary_detail(breadcrumbs_links, show_downloads=True, simplified=False)">
54 54 <% summary = lambda n:{False:'summary-short'}.get(n) %>
55 55
56 56 <div id="summary-menu-stats" class="summary-detail">
57 57 <div class="fieldset">
58 58 <div class="left-content">
59 59 <div class="left-clone">
60 60 <select id="clone_option" name="clone_option">
61 61 <option value="http" selected="selected">HTTP</option>
62 62 <option value="http_id">HTTP UID</option>
63 63 % if c.ssh_enabled:
64 64 <option value="ssh">SSH</option>
65 65 % endif
66 66 </select>
67 67 </div>
68 68
69 69 <div class="right-clone">
70 70 <%
71 71 maybe_disabled = ''
72 72 if h.is_svn_without_proxy(c.rhodecode_db_repo):
73 73 maybe_disabled = 'disabled'
74 74 %>
75 75
76 76 <span id="clone_option_http">
77 77 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url}"/>
78 78 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url}" title="${_('Copy the clone url')}"></i>
79 79 </span>
80 80
81 81 <span style="display: none;" id="clone_option_http_id">
82 82 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url_id}"/>
83 83 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url_id}" title="${_('Copy the clone by id url')}"></i>
84 84 </span>
85 85
86 86 <span style="display: none;" id="clone_option_ssh">
87 87 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url_ssh}"/>
88 88 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url_ssh}" title="${_('Copy the clone by ssh url')}"></i>
89 89 </span>
90 90
91 91 % if maybe_disabled:
92 92 <p class="help-block">${_('SVN Protocol is disabled. To enable it, see the')} <a href="${h.route_url('enterprise_svn_setup')}" target="_blank">${_('documentation here')}</a>.</p>
93 93 % endif
94 94 </div>
95 95 </div>
96 96
97 97 <div class="right-content">
98 98 <div class="commit-info">
99 99 <div class="tags">
100 100 <% commit_rev = h.safe_int(c.rhodecode_db_repo.changeset_cache.get('revision'), 0) + 1 %>
101 101 % if c.rhodecode_repo:
102 102 ${refs_counters(
103 103 c.rhodecode_repo.branches,
104 104 c.rhodecode_repo.branches_closed,
105 105 c.rhodecode_repo.tags,
106 106 c.rhodecode_repo.bookmarks)}
107 107 % else:
108 108 ## missing requirements can make c.rhodecode_repo None
109 109 ${refs_counters([], [], [], [])}
110 110 % endif
111 111
112 112 ## commits
113 113 <span class="summary-tag">
114 114 % if commit_rev == -1:
115 115 <i class="icon-history"></i>
116 116 % if commit_rev == -1:
117 117 <span>0</span> ${_('Commit')}
118 118 % else:
119 119 <span>0</span> ${_('Commits')}
120 120 % endif
121 121 % else:
122 122 <a href="${h.route_path('repo_commits', repo_name=c.repo_name)}">
123 123 <i class="icon-history"></i>
124 124 % if commit_rev == 1:
125 125 <span>${commit_rev}</span> ${_('Commit')}
126 126 % else:
127 127 <span>${commit_rev}</span> ${_('Commits')}
128 128 % endif
129 129 </a>
130 130 % endif
131 131 </span>
132 132
133 133 ## forks
134 134 <span class="summary-tag">
135 135 <a title="${_('Number of Repository Forks')}" href="${h.route_path('repo_forks_show_all', repo_name=c.repo_name)}">
136 136 <i class="icon-code-fork"></i>
137 137 <span>${c.repository_forks}</span> ${_ungettext('Fork', 'Forks', c.repository_forks)}</a>
138 138 </span>
139 139 </div>
140 140 </div>
141 141 </div>
142 142 </div>
143
144 % if not simplified:
143 145 ## owner, description, downloads, statistics
144 146
145 147 ## Owner
146 148 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
147 149 <div class="left-label-summary">
148 150 <p>${_('Owner')}</p>
149 151 <div class="right-label-summary">
150 152 ${base.gravatar_with_user(c.rhodecode_db_repo.user.email, 16, tooltip=True)}
151 153 </div>
152 154
153 155 </div>
154 156 </div>
155 157
156 158 ## Description
157 159 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
158 160 <div class="left-label-summary">
159 161 <p>${_('Description')}</p>
160 162
161 163 <div class="right-label-summary input ${summary(c.show_stats)}">
162 164 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
163 165 ${dt.repo_desc(c.rhodecode_db_repo.description_safe, c.visual.stylify_metatags)}
164 166 </div>
165 167 </div>
166 168 </div>
167 169
168 170 ## Downloads
169 171 % if show_downloads:
170 172 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
171 173 <div class="left-label-summary">
172 174 <p>${_('Downloads')}</p>
173 175
174 176 <div class="right-label-summary input ${summary(c.show_stats)} downloads">
175 177 % if c.rhodecode_repo and len(c.rhodecode_repo.commit_ids) == 0:
176 178 <span class="disabled">
177 179 ${_('There are no downloads yet')}
178 180 </span>
179 181 % elif not c.enable_downloads:
180 182 <span class="disabled">
181 183 ${_('Downloads are disabled for this repository')}.
182 184 </span>
183 185 % if c.is_super_admin:
184 186 ${h.link_to(_('Enable downloads'),h.route_path('edit_repo',repo_name=c.repo_name, _anchor='repo_enable_downloads'))}
185 187 % endif
186 188 % else:
187 189 <div class="enabled pull-left" style="margin-right: 10px">
188 190
189 191 <div class="btn-group btn-group-actions">
190 192 <a class="archive_link btn btn-small" data-ext=".zip" href="${h.route_path('repo_archivefile',repo_name=c.rhodecode_db_repo.repo_name, fname=c.rhodecode_db_repo.landing_ref_name+'.zip', _query={'with_hash': '1'})}">
191 193 <i class="icon-download"></i>
192 194 ${c.rhodecode_db_repo.landing_ref_name}.zip
193 195 ## replaced by some JS on select
194 196 </a>
195 197
196 198 <a class="tooltip btn btn-primary btn-more-option" data-toggle="dropdown" aria-pressed="false" role="button" title="${_('more download options')}">
197 199 <i class="icon-down"></i>
198 200 </a>
199 201
200 202 <div class="btn-action-switcher-container left-align">
201 203 <ul class="btn-action-switcher" role="menu" style="min-width: 200px; width: max-content">
202 204 % for a_type, content_type, extension in h.ARCHIVE_SPECS:
203 205 % if extension not in ['.zip']:
204 206 <li>
205 207 <a class="archive_link" data-ext="${extension}" href="${h.route_path('repo_archivefile',repo_name=c.rhodecode_db_repo.repo_name, fname=c.rhodecode_db_repo.landing_ref_name+extension, _query={'with_hash': '1'})}">
206 208 <i class="icon-download"></i>
207 209 ${c.rhodecode_db_repo.landing_ref_name+extension}
208 210 </a>
209 211 </li>
210 212 % endif
211 213 % endfor
212 214 </ul>
213 215 </div>
214 216 </div>
215 217
216 218 </div>
217 219 ${h.hidden('download_options')}
218 220 % endif
219 221 </div>
220 222 </div>
221 223 </div>
222 224 % endif
223 225
224 226 ## Repo size
225 227 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
226 228 <div class="left-label-summary">
227 229 <p>${_('Repository size')}</p>
228 230
229 231 <div class="right-label-summary">
230 232 <div class="tags">
231 233 ## repo size
232 234 % if commit_rev == -1:
233 235 <span class="stats-bullet">0 B</span>
234 236 % else:
235 237 <span>
236 238 <a href="#showSize" onclick="calculateSize(); $(this).hide(); return false" id="show-repo-size">Show repository size</a>
237 239 </span>
238 240 <span class="stats-bullet" id="repo_size_container" style="display:none">
239 241 ${_('Calculating Repository Size...')}
240 242 </span>
241 243 % endif
242 244 </div>
243 245 </div>
244 246 </div>
245 247 </div>
246 248
247 249 ## Statistics
248 250 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
249 251 <div class="left-label-summary">
250 252 <p>${_('Code Statistics')}</p>
251 253
252 254 <div class="right-label-summary input ${summary(c.show_stats)} statistics">
253 255 % if c.show_stats:
254 256 <div id="lang_stats" class="enabled">
255 257 <a href="#showSize" onclick="calculateSize(); $('#show-repo-size').hide(); $(this).hide(); return false" id="show-repo-size">Show code statistics</a>
256 258 </div>
257 259 % else:
258 260 <span class="disabled">
259 261 ${_('Statistics are disabled for this repository')}.
260 262 </span>
261 263 % if c.is_super_admin:
262 264 ${h.link_to(_('Enable statistics'),h.route_path('edit_repo',repo_name=c.repo_name, _anchor='repo_enable_statistics'))}
263 265 % endif
264 266 % endif
265 267 </div>
266 268
267 269 </div>
268 270 </div>
269
271 % endif
270 272
271 273 </div><!--end summary-detail-->
272 274
275 % if simplified:
276 <div style="height: 25px"></div>
277 %else:
273 278 <div id="summary_details_expand" class="btn-collapse" data-toggle="summary-details">
274 279 ${_('Show More')}
275 280 </div>
281 % endif
276 282 </%def>
@@ -1,113 +1,118 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.mako"/>
3 <%namespace name="components" file="/summary/components.mako"/>
3 4
4 5 <%def name="title()">
5 ${_('%s Tags') % c.repo_name}
6 ${_('{} Tags').format(c.repo_name)}
6 7 %if c.rhodecode_name:
7 8 &middot; ${h.branding(c.rhodecode_name)}
8 9 %endif
9 10 </%def>
10 11
11 12 <%def name="breadcrumbs_links()"></%def>
12 13
13 14 <%def name="menu_bar_nav()">
14 15 ${self.menu_items(active='repositories')}
15 16 </%def>
16 17
17 18 <%def name="menu_bar_subnav()">
18 19 ${self.repo_menu(active='summary')}
19 20 </%def>
20 21
21 22 <%def name="main()">
23 <div id="repo-summary" class="summary">
24 ${components.summary_detail(breadcrumbs_links=self.breadcrumbs_links(), show_downloads=False, simplified=True)}
25 </div>
26
22 27 <div class="box">
23 28 <div class="title">
24 29
25 30 %if c.has_references:
26 31 <ul class="links">
27 32 <li>
28 33 <input type="submit" id="compare_action" class="btn" disabled="disabled" value="${_('Compare Selected Tags')}">
29 34 </li>
30 35 </ul>
31 36 %endif
32 37 %if c.has_references:
33 38 <div class="grid-quick-filter">
34 39 <ul class="grid-filter-box">
35 40 <li class="grid-filter-box-icon">
36 41 <i class="icon-search"></i>
37 42 </li>
38 43 <li class="grid-filter-box-input">
39 44 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
40 45 </li>
41 46 </ul>
42 47 </div>
43 48 <div id="obj_count">0</div>
44 49 %endif
45 50 </div>
46 51 <table id="obj_list_table" class="rctable table-bordered"></table>
47 52 </div>
48 53
49 54
50 55 <script type="text/javascript">
51 56 $(document).ready(function() {
52 57
53 58 var get_datatable_count = function(){
54 59 var api = $('#obj_list_table').dataTable().api();
55 60 var total = api.page.info().recordsDisplay
56 61 var _text = _ngettext('{0} tag', '{0} tags', total).format(total);
57 62
58 63 $('#obj_count').text(_text);
59 64 };
60 65
61 66 // object list
62 67 $('#obj_list_table').DataTable({
63 68 data: ${c.data|n},
64 69 dom: 'rtp',
65 70 pageLength: ${c.visual.dashboard_items},
66 71 order: [[ 0, "asc" ]],
67 72 columns: [
68 73 { data: {"_": "name",
69 74 "sort": "name_raw"}, title: "${_('Name')}", className: "td-tags" },
70 75 { data: {"_": "date",
71 76 "sort": "date_raw"}, title: "${_('Date')}", className: "td-time" },
72 77 { data: {"_": "author",
73 78 "sort": "author"}, title: "${_('Author')}", className: "td-user" },
74 79 { data: {"_": "commit",
75 80 "sort": "commit_raw",
76 81 "type": Number}, title: "${_('Commit')}", className: "td-hash" },
77 82 { data: {"_": "compare",
78 83 "sort": "compare"}, title: "${_('Compare')}", className: "td-compare" }
79 84 ],
80 85 language: {
81 86 paginate: DEFAULT_GRID_PAGINATION,
82 87 emptyTable: _gettext("No tags available yet.")
83 88 },
84 89 "initComplete": function(settings, json) {
85 90 get_datatable_count();
86 91 timeagoActivate();
87 92 tooltipActivate();
88 93 compare_radio_buttons("${c.repo_name}", 'tag');
89 94 }
90 95 });
91 96
92 97 // update when things change
93 98 $('#obj_list_table').on('draw.dt', function() {
94 99 get_datatable_count();
95 100 timeagoActivate();
96 101 tooltipActivate();
97 102 });
98 103
99 104 // filter, filter both grids
100 105 $('#q_filter').on('keyup', function() {
101 106 var obj_api = $('#obj_list_table').dataTable().api();
102 107 obj_api
103 108 .columns(0)
104 109 .search(this.value)
105 110 .draw();
106 111 });
107 112
108 113 // refilter table if page load via back button
109 114 $("#q_filter").trigger('keyup');
110 115 });
111 116
112 117 </script>
113 118 </%def>
General Comments 0
You need to be logged in to leave comments. Login now